﻿// BillyHub generated split file. Keep script order in Bills Manager.html.
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "showCents": true,
  "sampleData": false,
  "accent": "#b8e64b"
}/*EDITMODE-END*/;

const THEME_PALETTES = {
  default: {
    label: 'BillyHub',
    light: {
      '--bg': '#f2f2ef',
      '--bg-accent': '#e4ead8',
      '--bg-accent-2': '#d5dcc9',
      '--surface': 'rgba(255, 255, 255, 0.92)',
      '--surface-strong': '#ffffff',
      '--surface-soft': 'rgba(246, 247, 242, 0.84)',
      '--text': '#181a16',
      '--muted': '#64685f',
      '--line': 'rgba(24, 26, 22, 0.12)',
      '--navy': '#252923',
      '--teal': '#b8e64b',
      '--cyan': '#8e9587',
      '--lime': '#d7f487',
      '--peach': '#b7bcae',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 60px rgba(18, 20, 16, 0.14)',
    },
    dark: {
      '--bg': '#10110e',
      '--bg-accent': '#1a1d16',
      '--bg-accent-2': '#282c22',
      '--surface': 'rgba(18, 19, 16, 0.96)',
      '--surface-strong': '#151713',
      '--surface-soft': 'rgba(25, 27, 22, 0.92)',
      '--text': '#f4f5ef',
      '--muted': '#adb2a5',
      '--line': 'rgba(224, 231, 210, 0.13)',
      '--navy': '#dde6d2',
      '--teal': '#c2f25a',
      '--cyan': '#777d70',
      '--lime': '#d8ff75',
      '--peach': '#929889',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 70px rgba(0, 0, 0, 0.52)',
    },
  },
  woodland: {
    label: 'Woodland',
    light: {
      '--bg': '#f1f2ea',
      '--bg-accent': '#dde4d4',
      '--bg-accent-2': '#c8d2bf',
      '--surface': 'rgba(255, 255, 252, 0.92)',
      '--surface-strong': '#fcfcf8',
      '--surface-soft': 'rgba(242, 244, 237, 0.88)',
      '--text': '#181b16',
      '--muted': '#677060',
      '--line': 'rgba(24, 27, 22, 0.12)',
      '--navy': '#2f382a',
      '--teal': '#94c85d',
      '--cyan': '#88947f',
      '--lime': '#cfe98b',
      '--peach': '#b7c1ad',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 60px rgba(23, 28, 18, 0.14)',
    },
    dark: {
      '--bg': '#0f120d',
      '--bg-accent': '#171c15',
      '--bg-accent-2': '#232b1e',
      '--surface': 'rgba(18, 21, 16, 0.96)',
      '--surface-strong': '#141914',
      '--surface-soft': 'rgba(24, 29, 21, 0.92)',
      '--text': '#f2f4ed',
      '--muted': '#a7b19e',
      '--line': 'rgba(220, 229, 210, 0.13)',
      '--navy': '#dce8d3',
      '--teal': '#a8d86a',
      '--cyan': '#73806e',
      '--lime': '#d1f07f',
      '--peach': '#8b9583',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 70px rgba(0, 0, 0, 0.54)',
    },
  },
  jadePebble: {
    label: 'Jade Pebble',
    light: {
      '--bg': '#f2f4f1',
      '--bg-accent': '#dde5df',
      '--bg-accent-2': '#c8d3cc',
      '--surface': 'rgba(255, 255, 255, 0.93)',
      '--surface-strong': '#fbfcfb',
      '--surface-soft': 'rgba(242, 245, 243, 0.88)',
      '--text': '#19201d',
      '--muted': '#63706a',
      '--line': 'rgba(25, 32, 29, 0.12)',
      '--navy': '#2f3b35',
      '--teal': '#71c29f',
      '--cyan': '#8f9893',
      '--lime': '#bfe8c6',
      '--peach': '#bbc4bf',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 60px rgba(20, 26, 23, 0.14)',
    },
    dark: {
      '--bg': '#0f1211',
      '--bg-accent': '#161b19',
      '--bg-accent-2': '#232b28',
      '--surface': 'rgba(19, 22, 21, 0.96)',
      '--surface-strong': '#141918',
      '--surface-soft': 'rgba(24, 29, 27, 0.92)',
      '--text': '#f1f4f2',
      '--muted': '#a5b1ab',
      '--line': 'rgba(219, 228, 223, 0.13)',
      '--navy': '#d7e3dc',
      '--teal': '#79cba7',
      '--cyan': '#76817c',
      '--lime': '#b9e6be',
      '--peach': '#8e9894',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 70px rgba(0, 0, 0, 0.54)',
    },
  },
  tropicalJade: {
    label: 'Tropical Jade',
    light: {
      '--bg': '#eef6f0',
      '--bg-accent': '#d6e9db',
      '--bg-accent-2': '#bbd8c6',
      '--surface': 'rgba(255, 255, 255, 0.94)',
      '--surface-strong': '#fbfefc',
      '--surface-soft': 'rgba(241, 248, 243, 0.88)',
      '--text': '#13221d',
      '--muted': '#587168',
      '--line': 'rgba(19, 34, 29, 0.12)',
      '--navy': '#1f4033',
      '--teal': '#2cc38d',
      '--cyan': '#7f978f',
      '--lime': '#9ef0c0',
      '--peach': '#afd0bf',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 62px rgba(13, 32, 25, 0.15)',
    },
    dark: {
      '--bg': '#081310',
      '--bg-accent': '#0f1f1a',
      '--bg-accent-2': '#153129',
      '--surface': 'rgba(11, 22, 18, 0.96)',
      '--surface-strong': '#0d1915',
      '--surface-soft': 'rgba(16, 30, 25, 0.92)',
      '--text': '#edf7f2',
      '--muted': '#98b5aa',
      '--line': 'rgba(212, 235, 225, 0.13)',
      '--navy': '#d6f2e6',
      '--teal': '#39d899',
      '--cyan': '#69857c',
      '--lime': '#90f0b7',
      '--peach': '#7d9990',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 72px rgba(0, 0, 0, 0.56)',
    },
  },
  sapphire: {
    label: 'Sapphire',
    light: {
      '--bg': '#eef3f8',
      '--bg-accent': '#dae4ef',
      '--bg-accent-2': '#c4d2e1',
      '--surface': 'rgba(255, 255, 255, 0.94)',
      '--surface-strong': '#fbfcff',
      '--surface-soft': 'rgba(242, 246, 251, 0.88)',
      '--text': '#18212b',
      '--muted': '#667483',
      '--line': 'rgba(24, 33, 43, 0.12)',
      '--navy': '#203447',
      '--teal': '#7bcf9a',
      '--cyan': '#8797a6',
      '--lime': '#b7e9c7',
      '--peach': '#b2c0ce',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 60px rgba(18, 30, 44, 0.15)',
    },
    dark: {
      '--bg': '#0c1218',
      '--bg-accent': '#131d26',
      '--bg-accent-2': '#1c2a37',
      '--surface': 'rgba(15, 22, 30, 0.96)',
      '--surface-strong': '#111922',
      '--surface-soft': 'rgba(21, 31, 41, 0.92)',
      '--text': '#f1f5f9',
      '--muted': '#a7b4c1',
      '--line': 'rgba(219, 229, 239, 0.13)',
      '--navy': '#d8e4ef',
      '--teal': '#88dca8',
      '--cyan': '#718393',
      '--lime': '#b2e7c3',
      '--peach': '#8c9cae',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 70px rgba(0, 0, 0, 0.56)',
    },
  },
  amethystMint: {
    label: 'Amethyst Mint',
    light: {
      '--bg': '#f4f1f6',
      '--bg-accent': '#e4ddef',
      '--bg-accent-2': '#d4cae2',
      '--surface': 'rgba(255, 255, 255, 0.93)',
      '--surface-strong': '#fdfcff',
      '--surface-soft': 'rgba(246, 242, 249, 0.88)',
      '--text': '#201a25',
      '--muted': '#72687a',
      '--line': 'rgba(32, 26, 37, 0.12)',
      '--navy': '#392d45',
      '--teal': '#8ad7b1',
      '--cyan': '#9a90a2',
      '--lime': '#cbefd9',
      '--peach': '#c6bcd1',
      '--danger': '#c43b4b',
      '--danger-soft': 'rgba(196, 59, 75, 0.13)',
      '--shadow': '0 24px 60px rgba(34, 24, 42, 0.15)',
    },
    dark: {
      '--bg': '#120f15',
      '--bg-accent': '#1c1622',
      '--bg-accent-2': '#2a2232',
      '--surface': 'rgba(21, 17, 25, 0.96)',
      '--surface-strong': '#17131b',
      '--surface-soft': 'rgba(28, 22, 33, 0.92)',
      '--text': '#f5f1f8',
      '--muted': '#b5acbc',
      '--line': 'rgba(232, 224, 238, 0.13)',
      '--navy': '#e8dff0',
      '--teal': '#97e4bc',
      '--cyan': '#83788a',
      '--lime': '#c6efd5',
      '--peach': '#9a90a3',
      '--danger': '#ff6f7f',
      '--danger-soft': 'rgba(255, 111, 127, 0.16)',
      '--shadow': '0 24px 72px rgba(0, 0, 0, 0.58)',
    },
  },
};

const BILLYHUB_BACKUP_VERSION = 1;
const SUPABASE_URL = 'https://ihnckcyxpirumuyeceoj.supabase.co';
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlobmNrY3l4cGlydW11eWVjZW9qIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzcxNDQ5MzUsImV4cCI6MjA5MjcyMDkzNX0.TzWWpUcq7Hv764f90sdpHUQpngjHediXuVOrXI_6e_k';
const supabaseClient = window.supabase?.createClient?.(SUPABASE_URL, SUPABASE_ANON_KEY) || null;
const BILLYHUB_STORAGE_KEYS = [
  'bills-data', 'bills-debts', 'bills-payday-v3', 'bills-payperiod', 'bills-budget',
  'bills-income', 'bills-extra-payment', 'bills-payment-log', 'bills-funding-log', 'bills-expenses', 'bills-profile',
  'bills-tweaks', 'bills-theme', 'bills-theme-name', 'bills-tab', 'bills-imported-version',
  'bills-debts-imported-version',
];
const BILLYHUB_LOCAL_CLEAR_KEYS = [...BILLYHUB_STORAGE_KEYS, 'billyhub-last-saved', 'billyhub-last-backup'];
const BLANK_APP_STATE = {
  bills: [],
  debts: [],
  payday: { firstDate:'', frequency:'biweekly' },
  payPeriod: { date1:'', date2:'' },
  budget: { amount:'' },
  income: { mode:'hourly', amount:'' },
  extraPayment: { amount:'' },
  paymentLog: [],
  fundingLog: [],
  expenses: [],
  profile: { firstName: '' },
  tweaks: TWEAK_DEFAULTS,
  theme: 'dark',
  themeName: 'default',
  tab: 'dashboard',
};

function App() {
  const load = (key, fallback) => { try { return JSON.parse(localStorage.getItem(key) || 'null') || fallback; } catch { return fallback; } };

  const [theme,     setTheme]     = useState(() => localStorage.getItem('bills-theme') || 'dark');
  const [themeName, setThemeName] = useState(() => localStorage.getItem('bills-theme-name') || 'default');
  const [tab,       setTab]       = useState(() => localStorage.getItem('bills-tab') || 'dashboard');
  const [bills,     setBills]     = useState(() => load('bills-data', []));
  const [debts,     setDebts]     = useState(() => load('bills-debts', []));
  const [payday,    setPayday]    = useState(() => load('bills-payday-v3', { firstDate:'', frequency:'biweekly' }));
  const [payPeriod, setPayPeriod] = useState(() => load('bills-payperiod', { date1:'', date2:'' }));
  const [budget,    setBudget]    = useState(() => load('bills-budget', { amount:'' }));
  const [income,    setIncome]    = useState(() => load('bills-income', { mode:'hourly', amount:'' }));
  const [extraPayment, setExtraPayment] = useState(() => load('bills-extra-payment', { amount:'' }));
  const [paymentLog, setPaymentLog] = useState(() => load('bills-payment-log', []));
  const [fundingLog, setFundingLog] = useState(() => load('bills-funding-log', []));
  const [expenses, setExpenses] = useState(() => load('bills-expenses', []));
  const [profile, setProfile] = useState(() => load('bills-profile', { firstName: '' }));
  const [tweaks,    setTweaks]    = useState(() => {
    const saved = load('bills-tweaks', {});
    return { ...TWEAK_DEFAULTS, ...saved, accent: TWEAK_DEFAULTS.accent };
  });
  const [billModal, setBillModal] = useState(null);
  const [debtModal, setDebtModal] = useState(null);
  const [paymentModal, setPaymentModal] = useState(null);
  const [quickEntryModal, setQuickEntryModal] = useState(null);
  const [activityModal, setActivityModal] = useState(null);
  const [confirmModal, setConfirmModal] = useState(null);
  const [importPreview, setImportPreview] = useState(null);
  const [lastSavedAt, setLastSavedAt] = useState(() => localStorage.getItem('billyhub-last-saved') || '');
  const [lastBackupAt, setLastBackupAt] = useState(() => localStorage.getItem('billyhub-last-backup') || '');
  const [editMode,  setEditMode]  = useState(false);
  const importInputRef = useRef(null);
  const [authMode, setAuthMode] = useState('login');
  const [authLoading, setAuthLoading] = useState(true);
  const [authError, setAuthError] = useState('');
  const [authNotice, setAuthNotice] = useState('');
  const [session, setSession] = useState(null);
  const [cloudReady, setCloudReady] = useState(false);
  const [cloudStatus, setCloudStatus] = useState('Loading account...');
  const applyingCloudState = useRef(false);

  const resetAppState = (options = {}) => {
    const keepTheme = options.keepTheme;
    const next = {
      ...BLANK_APP_STATE,
      theme: keepTheme ? theme : BLANK_APP_STATE.theme,
      themeName: keepTheme ? themeName : BLANK_APP_STATE.themeName,
    };
    applyingCloudState.current = true;
    setBills(next.bills);
    setDebts(next.debts);
    setPayday(next.payday);
    setPayPeriod(next.payPeriod);
    setBudget(next.budget);
    setIncome(next.income);
    setExtraPayment(next.extraPayment);
    setPaymentLog(next.paymentLog);
    setFundingLog(next.fundingLog);
    setExpenses(next.expenses);
    setProfile(next.profile);
    setTweaks(next.tweaks);
    setTheme(next.theme);
    setThemeName(next.themeName);
    setTab(next.tab);
    window.setTimeout(() => { applyingCloudState.current = false; }, 0);
  };

  const appStateSnapshot = () => ({
    bills, debts, payday, payPeriod, budget, income, extraPayment,
    paymentLog, fundingLog, expenses, profile, tweaks, theme, themeName, tab,
  });

  const applyAppState = data => {
    const next = data && typeof data === 'object' ? data : BLANK_APP_STATE;
    applyingCloudState.current = true;
    setBills(Array.isArray(next.bills) ? next.bills : []);
    setDebts(Array.isArray(next.debts) ? next.debts : []);
    setPayday(next.payday && typeof next.payday === 'object' ? next.payday : BLANK_APP_STATE.payday);
    setPayPeriod(next.payPeriod && typeof next.payPeriod === 'object' ? next.payPeriod : BLANK_APP_STATE.payPeriod);
    setBudget(next.budget && typeof next.budget === 'object' ? next.budget : BLANK_APP_STATE.budget);
    setIncome(next.income && typeof next.income === 'object' ? next.income : BLANK_APP_STATE.income);
    setExtraPayment(next.extraPayment && typeof next.extraPayment === 'object' ? next.extraPayment : BLANK_APP_STATE.extraPayment);
    setPaymentLog(Array.isArray(next.paymentLog) ? next.paymentLog : []);
    setFundingLog(Array.isArray(next.fundingLog) ? next.fundingLog : []);
    setExpenses(Array.isArray(next.expenses) ? next.expenses : []);
    setProfile(next.profile && typeof next.profile === 'object' ? next.profile : { firstName: session?.user?.user_metadata?.first_name || '' });
    setTweaks({ ...TWEAK_DEFAULTS, ...(next.tweaks || {}), accent: TWEAK_DEFAULTS.accent });
    if (['dark', 'light'].includes(next.theme)) setTheme(next.theme);
    if (THEME_PALETTES[next.themeName]) setThemeName(next.themeName);
    setTab(['dashboard', 'calendar', 'bills', 'debt', 'banker', 'analytics', 'settings'].includes(next.tab) ? next.tab : 'dashboard');
    window.setTimeout(() => { applyingCloudState.current = false; }, 0);
  };

  useEffect(() => {
    let mounted = true;
    if (!supabaseClient) {
      setAuthLoading(false);
      setAuthError('Login service did not load. Refresh the page and try again.');
      return undefined;
    }
    supabaseClient.auth.getSession().then(({ data, error }) => {
      if (!mounted) return;
      if (error) setAuthError(error.message);
      setSession(data?.session || null);
      setAuthLoading(false);
    });
    const { data: listener } = supabaseClient.auth.onAuthStateChange((event, nextSession) => {
      if (!mounted) return;
      setSession(nextSession);
      setAuthLoading(false);
      if (event === 'SIGNED_OUT') {
        BILLYHUB_LOCAL_CLEAR_KEYS.forEach(key => localStorage.removeItem(key));
        setCloudReady(false);
        setCloudStatus('');
        resetAppState();
      }
    });
    return () => {
      mounted = false;
      listener?.subscription?.unsubscribe?.();
    };
  }, []);

  useEffect(() => {
    let cancelled = false;
    const loadCloudState = async () => {
      if (!session?.user || !supabaseClient) return;
      setCloudReady(false);
      setCloudStatus('Loading account...');
      const { data, error } = await supabaseClient
        .from('app_states')
        .select('data')
        .eq('user_id', session.user.id)
        .maybeSingle();
      if (cancelled) return;
      if (error) {
        setCloudStatus('Cloud load failed');
        setAuthError(error.message);
        return;
      }
      if (data?.data) {
        applyAppState(data.data);
      } else {
        resetAppState({ keepTheme: true });
        setProfile({ firstName: session.user.user_metadata?.first_name || '' });
      }
      setCloudReady(true);
      setCloudStatus('');
    };
    loadCloudState();
    return () => { cancelled = true; };
  }, [session?.user?.id]);

  useEffect(() => {
    if (!session?.user || !cloudReady || !supabaseClient || applyingCloudState.current) return undefined;
    setCloudStatus('Saving to account...');
    const handle = window.setTimeout(async () => {
      const { error } = await supabaseClient
        .from('app_states')
        .upsert({
          user_id: session.user.id,
          data: appStateSnapshot(),
          updated_at: new Date().toISOString(),
        }, { onConflict: 'user_id' });
      setCloudStatus(error ? 'Cloud save failed' : 'Saved to account');
      if (error) console.error('BillyHub cloud save failed', error);
    }, 900);
    return () => window.clearTimeout(handle);
  }, [bills, debts, payday, payPeriod, budget, income, extraPayment, paymentLog, fundingLog, expenses, profile, tweaks, theme, themeName, tab, session?.user?.id, cloudReady]);

  useEffect(() => { if (tweaks.sampleData && bills.length === 0) setBills(SAMPLE_BILLS); }, [tweaks.sampleData]);
  useEffect(() => { if (tweaks.sampleData && debts.length === 0) setDebts(SAMPLE_DEBTS); }, [tweaks.sampleData]);
  useEffect(() => {
    const mode = theme === 'light' ? 'light' : 'dark';
    const paletteKey = THEME_PALETTES[themeName] ? themeName : 'default';
    const palette = THEME_PALETTES[paletteKey][mode];
    document.body.setAttribute('data-theme', mode);
    document.body.setAttribute('data-theme-name', paletteKey);
    Object.entries(palette).forEach(([key, value]) => {
      document.documentElement.style.setProperty(key, value);
    });
    localStorage.setItem('bills-theme', mode);
    localStorage.setItem('bills-theme-name', paletteKey);
  }, [theme, themeName]);
  useEffect(() => { localStorage.setItem('bills-tab', tab); }, [tab]);
  useEffect(() => { localStorage.setItem('bills-data', JSON.stringify(bills)); }, [bills]);
  useEffect(() => { localStorage.setItem('bills-debts', JSON.stringify(debts)); }, [debts]);
  useEffect(() => { localStorage.setItem('bills-payday-v3', JSON.stringify(payday)); }, [payday]);
  useEffect(() => { localStorage.setItem('bills-payperiod', JSON.stringify(payPeriod)); }, [payPeriod]);
  useEffect(() => { localStorage.setItem('bills-budget', JSON.stringify(budget)); }, [budget]);
  useEffect(() => { localStorage.setItem('bills-income', JSON.stringify(income)); }, [income]);
  useEffect(() => { localStorage.setItem('bills-extra-payment', JSON.stringify(extraPayment)); }, [extraPayment]);
  useEffect(() => { localStorage.setItem('bills-payment-log', JSON.stringify(paymentLog)); }, [paymentLog]);
  useEffect(() => { localStorage.setItem('bills-funding-log', JSON.stringify(fundingLog)); }, [fundingLog]);
  useEffect(() => { localStorage.setItem('bills-expenses', JSON.stringify(expenses)); }, [expenses]);
  useEffect(() => { localStorage.setItem('bills-profile', JSON.stringify(profile)); }, [profile]);
  useEffect(() => { localStorage.setItem('bills-tweaks', JSON.stringify(tweaks)); }, [tweaks]);
  useEffect(() => {
    const stamp = new Date().toISOString();
    localStorage.setItem('billyhub-last-saved', stamp);
    setLastSavedAt(stamp);
  }, [bills, debts, payday, payPeriod, budget, income, extraPayment, paymentLog, fundingLog, expenses, profile, tweaks, theme, themeName, tab]);

  // Edit mode â€” Tweaks panel only shown when active (Brandon)
  useEffect(() => {
    const handler = e => {
      if (e.data?.type === '__activate_edit_mode')   setEditMode(true);
      if (e.data?.type === '__deactivate_edit_mode') setEditMode(false);
    };
    window.addEventListener('message', handler);
    window.parent.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', handler);
  }, []);

  const setTweak = (k, v) => setTweaks(t => {
    const next = { ...t, [k]: v };
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [k]: v } }, '*');
    return next;
  });

  const exportData = () => {
    const data = {
      'bills-data': bills,
      'bills-debts': debts,
      'bills-payday-v3': payday,
      'bills-payperiod': payPeriod,
      'bills-budget': budget,
      'bills-income': income,
      'bills-extra-payment': extraPayment,
      'bills-payment-log': paymentLog,
      'bills-funding-log': fundingLog,
      'bills-expenses': expenses,
      'bills-profile': profile,
      'bills-tweaks': tweaks,
      'bills-theme': theme,
      'bills-theme-name': themeName,
      'bills-tab': tab,
      'bills-imported-version': localStorage.getItem('bills-imported-version') || '',
      'bills-debts-imported-version': localStorage.getItem('bills-debts-imported-version') || '',
    };
    const payload = {
      app: 'BillyHub',
      version: BILLYHUB_BACKUP_VERSION,
      exportedAt: new Date().toISOString(),
      data,
    };
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = `billyhub-backup-${todayISO()}.json`;
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
    const stamp = new Date().toISOString();
    localStorage.setItem('billyhub-last-backup', stamp);
    setLastBackupAt(stamp);
  };

  const backupSummary = data => ({
    bills: Array.isArray(data?.['bills-data']) ? data['bills-data'].length : 0,
    debts: Array.isArray(data?.['bills-debts']) ? data['bills-debts'].length : 0,
    payments: Array.isArray(data?.['bills-payment-log']) ? data['bills-payment-log'].length : 0,
    expenses: Array.isArray(data?.['bills-expenses']) ? data['bills-expenses'].length : 0,
    funding: Array.isArray(data?.['bills-funding-log']) ? data['bills-funding-log'].length : 0,
  });

  const applyImportedData = data => {
    const has = key => Object.prototype.hasOwnProperty.call(data, key);
    const arr = key => Array.isArray(data[key]) ? data[key] : null;
    const obj = key => data[key] && typeof data[key] === 'object' && !Array.isArray(data[key]) ? data[key] : null;

    if (has('bills-data') && arr('bills-data')) setBills(data['bills-data']);
    if (has('bills-debts') && arr('bills-debts')) setDebts(data['bills-debts']);
    if (has('bills-payday-v3') && obj('bills-payday-v3')) setPayday(data['bills-payday-v3']);
    if (has('bills-payperiod') && obj('bills-payperiod')) setPayPeriod(data['bills-payperiod']);
    if (has('bills-budget') && obj('bills-budget')) setBudget(data['bills-budget']);
    if (has('bills-income') && obj('bills-income')) setIncome(data['bills-income']);
    if (has('bills-extra-payment') && obj('bills-extra-payment')) setExtraPayment(data['bills-extra-payment']);
    if (has('bills-payment-log') && arr('bills-payment-log')) setPaymentLog(data['bills-payment-log']);
    if (has('bills-funding-log') && arr('bills-funding-log')) setFundingLog(data['bills-funding-log']);
    if (has('bills-expenses') && arr('bills-expenses')) setExpenses(data['bills-expenses']);
    if (has('bills-profile') && obj('bills-profile')) setProfile(data['bills-profile']);
    if (has('bills-tweaks') && obj('bills-tweaks')) setTweaks(t => ({ ...t, ...data['bills-tweaks'], accent: TWEAK_DEFAULTS.accent }));
    if (has('bills-theme') && ['dark', 'light'].includes(data['bills-theme'])) setTheme(data['bills-theme']);
    if (has('bills-theme-name') && THEME_PALETTES[data['bills-theme-name']]) setThemeName(data['bills-theme-name']);
    if (has('bills-tab') && ['dashboard', 'calendar', 'bills', 'debt', 'banker', 'analytics', 'settings'].includes(data['bills-tab'])) setTab(data['bills-tab']);

    BILLYHUB_STORAGE_KEYS
      .filter(key => key.endsWith('imported-version') && has(key))
      .forEach(key => localStorage.setItem(key, data[key] || ''));
  };

  const importData = event => {
    const file = event.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const payload = JSON.parse(reader.result);
        const data = payload?.data || payload;
        if (!data || typeof data !== 'object') throw new Error('That file does not look like a BillyHub backup.');
        setImportPreview({
          fileName: file.name,
          exportedAt: payload?.exportedAt || '',
          data,
          current: {
            bills: bills.length,
            debts: debts.length,
            payments: paymentLog.length,
            expenses: expenses.length,
            funding: fundingLog.length,
          },
          incoming: backupSummary(data),
        });
      } catch (error) {
        alert(error.message || 'Could not import that backup file.');
      } finally {
        event.target.value = '';
      }
    };
    reader.readAsText(file);
  };

  const updatePayday    = (k, v) => setPayday(p    => ({ ...p, [k]: v }));
  const updatePayPeriod = (k, v) => setPayPeriod(p => ({ ...p, [k]: v }));
  const updateBudget    = (k, v) => setBudget(b    => ({ ...b, [k]: v }));
  const updateEffectiveBudget = (k, v) => updateBudget(k, v);
  const updateIncome    = (k, v) => setIncome(i    => ({ ...i, [k]: v }));
  const updateExtraPayment = (id, v) => setExtraPayment(e => ({ ...e, [id]: v }));
  const applyDebtPayment = (bill, amount) => {
    const debt = findDebtForBill(bill, debts);
    if (!debt || !Number.isFinite(amount) || amount <= 0) return null;
    const appliedAmount = Math.min(parseFloat(debt.balance) || 0, amount);
    setDebts(list => list.map(item => item.id === debt.id
      ? { ...item, balance: Math.max(0, (parseFloat(item.balance) || 0) - amount) }
      : item
    ));
    return { debtId: debt.id, debtName: debt.name, debtAppliedAmount: appliedAmount };
  };
  const restoreDebtPayment = item => {
    if (!item || !item.debtId) return;
    const amount = Number.isFinite(parseFloat(item.debtAppliedAmount))
      ? parseFloat(item.debtAppliedAmount)
      : parseFloat(item.amount);
    if (!Number.isFinite(amount) || amount <= 0) return;
    setDebts(list => list.map(debt => debt.id === item.debtId
      ? { ...debt, balance: (parseFloat(debt.balance) || 0) + amount }
      : debt
    ));
  };
  const adjustDebtPayment = (item, nextAmount) => {
    if (!item || !item.debtId) return item;
    const previousApplied = Number.isFinite(parseFloat(item.debtAppliedAmount))
      ? parseFloat(item.debtAppliedAmount)
      : parseFloat(item.amount) || 0;
    const debt = debts.find(entry => entry.id === item.debtId);
    const currentBalance = debt ? parseFloat(debt.balance) || 0 : 0;
    const originalBalance = currentBalance + previousApplied;
    const nextApplied = Math.min(originalBalance, Math.max(0, parseFloat(nextAmount) || 0));
    const delta = nextApplied - previousApplied;
    if (delta !== 0) {
      setDebts(list => list.map(debt => debt.id === item.debtId
        ? { ...debt, balance: Math.max(0, (parseFloat(debt.balance) || 0) - delta) }
        : debt
      ));
    }
    return { ...item, debtAppliedAmount: nextApplied };
  };
  const removePaidDate = (billId, key) => setBills(bs => bs.map(b => {
    if (b.id !== billId) return b;
    const nextPaidDates = (Array.isArray(b.paidDates) ? b.paidDates : []).filter(d => d !== key);
    return { ...b, paidDates: nextPaidDates };
  }));
  const addPaidDate = (billId, key) => setBills(bs => bs.map(b => {
    if (b.id !== billId) return b;
    const paidDates = Array.isArray(b.paidDates) ? b.paidDates : [];
    return paidDates.includes(key) ? b : { ...b, paidDates: [...paidDates, key] };
  }));
  const runActivityDelete = item => {
    if (item.activityType === 'expense') {
      setExpenses(list => list.filter(expense => expense.id !== item.id));
      return;
    }
    if (item.activityType === 'funding') {
      setFundingLog(list => list.filter(entry => entry.id !== item.id));
      return;
    }
    const key = item.dueDate;
    const billId = item.billId;
    restoreDebtPayment(item);
    setPaymentLog(log => {
      const nextLog = log.filter(entry => entry.id !== item.id);
      const stillLogged = nextLog.some(entry => entry.billId === billId && entry.dueDate === key);
      if (!stillLogged) removePaidDate(billId, key);
      return nextLog;
    });
  };
  const undoBillPayment = (bill, dueDate) => {
    const key = occurrenceKey(dueDate);
    setConfirmModal({
      title: 'Remove payment log?',
      body: `This will remove the saved payment for ${bill.name} on ${shortDate(dueDate)}.`,
      confirmLabel: 'Remove Log',
      onConfirm: () => {
        paymentLog
          .filter(item => item.billId === bill.id && item.dueDate === key)
          .forEach(restoreDebtPayment);
        setPaymentLog(log => log.filter(item => !(item.billId === bill.id && item.dueDate === key)));
        removePaidDate(bill.id, key);
      },
    });
  };
  const markBillPaid = (bill, dueDate) => {
    const key = occurrenceKey(dueDate);
    const paidDates = Array.isArray(bill.paidDates) ? bill.paidDates : [];

    if (paidDates.includes(key)) {
      undoBillPayment(bill, dueDate);
      return;
    }

    const defaultAmount = Number.isFinite(parseFloat(bill.amount)) ? parseFloat(bill.amount) : 0;
    setPaymentModal({ bill, dueDate, amount: defaultAmount.toFixed(2) });
  };
  const saveDashboardPayment = amount => {
    if (!paymentModal) return;
    const { bill, dueDate } = paymentModal;
    const key = occurrenceKey(dueDate);
    if (!Number.isFinite(amount) || amount < 0) {
      window.alert('Enter a valid payment amount.');
      return;
    }

    const debtPayment = applyDebtPayment(bill, amount);
    setPaymentLog(log => [{
      id: uid(),
      billId: bill.id,
      billName: bill.name,
      dueDate: key,
      date: todayISO(),
      createdAt: new Date().toISOString(),
      amount,
      source: 'dashboard',
      ...(debtPayment || {}),
    }, ...log]);

    addPaidDate(bill.id, key);
    setPaymentModal(null);
  };
  const openQuickExpenseModal = () => setQuickEntryModal({ mode:'expense', category:'Gas', amount:'', note:'' });
  const openFundingModal = () => setQuickEntryModal({ mode:'funding', amount:'', note:'' });
  const saveQuickEntry = entry => {
    const amount = parseFloat(entry.amount);
    if (!Number.isFinite(amount) || amount < 0) {
      window.alert('Enter a valid amount.');
      return;
    }
    if (entry.mode === 'funding') {
      setFundingLog(items => [{
        id: uid(),
        date: todayISO(),
        createdAt: new Date().toISOString(),
        activityType: 'funding',
        title: 'Money added',
        note: entry.note || '',
        amount,
      }, ...items]);
      setQuickEntryModal(null);
      return;
    }
    addExpense({
      id: uid(),
      date: todayISO(),
      createdAt: new Date().toISOString(),
      category: entry.category || 'Other',
      note: entry.note || '',
      amount,
    });
    setQuickEntryModal(null);
  };
  const toggleBillPrepaid = (bill, dueDate) => setBills(bs => bs.map(b => {
    if (b.id !== bill.id) return b;
    const key = occurrenceKey(dueDate);
    const prepaidDates = Array.isArray(b.prepaidDates) ? b.prepaidDates : [];
    const nextPrepaidDates = prepaidDates.includes(key)
      ? prepaidDates.filter(d => d !== key)
      : [...prepaidDates, key];
    return { ...b, prepaidDates: nextPrepaidDates };
  }));
  const logBillPayment = (bill, dueDate, amount) => {
    if (!Number.isFinite(amount)) return;
    const key = occurrenceKey(dueDate);
    const debtPayment = applyDebtPayment(bill, amount);
    setPaymentLog(log => [{
      id: uid(),
      billId: bill.id,
      billName: bill.name,
      dueDate: key,
      date: todayISO(),
      createdAt: new Date().toISOString(),
      amount,
      ...(debtPayment || {}),
    }, ...log]);
    addPaidDate(bill.id, key);
  };
  const addExpense = expense => setExpenses(items => [expense, ...items]);
  const editActivityItem = item => setActivityModal(item);
  const saveActivityItem = item => {
    const amount = parseFloat(item.amount);
    if (!Number.isFinite(amount) || amount < 0) {
      window.alert('Enter a valid amount.');
      return;
    }
    if (item.activityType === 'expense') {
      setExpenses(list => list.map(expense => expense.id === item.id
        ? { ...expense, amount, category: item.category || 'Other', note: item.note || '' }
        : expense
      ));
      setActivityModal(null);
      return;
    }
    if (item.activityType === 'funding') {
      setFundingLog(list => list.map(entry => entry.id === item.id
        ? { ...entry, amount, note: item.note || '' }
        : entry
      ));
      setActivityModal(null);
      return;
    }
    const originalPayment = paymentLog.find(entry => entry.id === item.id) || item;
    const adjustedPayment = { ...adjustDebtPayment(originalPayment, amount), amount };
    setPaymentLog(log => log.map(entry => entry.id === item.id ? adjustedPayment : entry));
    setActivityModal(null);
  };
  const deleteActivityItem = item => {
    const label = item.activityType === 'expense'
      ? 'expense'
      : item.activityType === 'funding'
        ? 'money entry'
        : 'payment log';
    setConfirmModal({
      title: 'Delete entry?',
      body: `This will permanently remove the ${label} from BillyHub.`,
      confirmLabel: 'Delete Entry',
      onConfirm: () => {
        runActivityDelete(item);
        setActivityModal(null);
      },
    });
  };

  const saveBill  = bill => { setBills(bs => { const i=bs.findIndex(b=>b.id===bill.id); if(i>=0){const n=[...bs];n[i]=bill;return n;} return [...bs,bill]; }); setBillModal(null); };
  const deleteBill = id => setBills(bs => bs.filter(b => b.id !== id));
  const saveDebt  = debt => { setDebts(ds => { const i=ds.findIndex(d=>d.id===debt.id); if(i>=0){const n=[...ds];n[i]=debt;return n;} return [...ds,debt]; }); setDebtModal(null); };
  const deleteDebt = id => setDebts(ds => ds.filter(d => d.id !== id));
  const requestBillDelete = bill => setConfirmModal({
    title: 'Delete bill?',
    body: `This will remove ${bill.name} and its saved details from BillyHub.`,
    confirmLabel: 'Delete Bill',
    onConfirm: () => {
      deleteBill(bill.id);
      setBillModal(null);
    },
  });
  const requestDebtDelete = debt => setConfirmModal({
    title: 'Delete debt?',
    body: `This will remove ${debt.name} and its payoff data from BillyHub.`,
    confirmLabel: 'Delete Debt',
    onConfirm: () => {
      deleteDebt(debt.id);
      setDebtModal(null);
    },
  });

  // Edit handlers â€” passing {} triggers "new" mode in modals
  const openBillModal = (bill) => setBillModal({ bill: bill || {} });
  const openDebtModal = (debt) => setDebtModal({ debt: debt || {} });

  const handleAuthSubmit = async ({ firstName = '', email, password }) => {
    if (!supabaseClient) return;
    setAuthLoading(true);
    setAuthError('');
    setAuthNotice('');
    const trimmedFirstName = firstName.trim();
    const credentials = authMode === 'signup'
      ? { email: email.trim(), password, options: { data: { first_name: trimmedFirstName } } }
      : { email: email.trim(), password };
    const result = authMode === 'signup'
      ? await supabaseClient.auth.signUp(credentials)
      : await supabaseClient.auth.signInWithPassword(credentials);
    setAuthLoading(false);
    if (result.error) {
      setAuthError(result.error.message);
      return;
    }
    if (authMode === 'signup' && !result.data?.session) {
      setAuthNotice('Check your email to confirm your account, then log in.');
      setAuthMode('login');
    }
  };

  const handleLogout = async () => {
    if (!supabaseClient) return;
    await supabaseClient.auth.signOut();
  };

  const TABS = [
    { id:'dashboard', label:'Dashboard' }, { id:'calendar', label:'Calendar' },
    { id:'bills', label:'Bills' }, { id:'debt', label:'Debt' }, { id:'banker', label:'Budget' },
  ];
  const automaticPayPeriod = getAutomaticPayPeriod(payday);
  const effectivePayPeriod = automaticPayPeriod
    ? { date1: toLocalISO(automaticPayPeriod.start), date2: toLocalISO(automaticPayPeriod.end), source: 'auto' }
    : payPeriod;
  const effectiveBudget = budget;

  if (authLoading && !session) {
    return (
      <div className="auth-shell">
        <div className="auth-card auth-loading-card">
          <div className="auth-brand">
            <img src="Billy Hub Logo.png" alt="BillyHub logo" className="auth-logo" />
            <div>
              <span>BillyHub</span>
              <h1>Loading</h1>
            </div>
          </div>
        </div>
      </div>
    );
  }

  if (!session) {
    return (
      <AuthScreen
        mode={authMode}
        setMode={setAuthMode}
        loading={authLoading}
        error={authError}
        notice={authNotice}
        onSubmit={handleAuthSubmit}
      />
    );
  }

  return (
    <>
      <div className="page-shell">
        <TopBar
          isSettingsActive={tab === 'settings'}
          onOpenSettings={() => setTab('settings')}
          userEmail={session.user?.email}
          onLogout={handleLogout}
        />
        <input
          ref={importInputRef}
          className="visually-hidden-file"
          type="file"
          accept="application/json,.json"
          onChange={importData}
        />
        <Hero
          bills={bills}
          debts={debts}
          payPeriod={effectivePayPeriod}
          payday={payday}
          budget={effectiveBudget}
          paymentLog={paymentLog}
          fundingLog={fundingLog}
          expenses={expenses}
          userFirstName={profile.firstName}
          onQuickAddExpense={openQuickExpenseModal}
        />
        <div className="tab-bar">
          {TABS.map(t => <button key={t.id} className={`tab-button${tab===t.id?' is-active':''}`} onClick={() => setTab(t.id)}>{t.label}</button>)}
        </div>

        <div className={`tab-panel${tab==='dashboard'?' is-active':''}`}>
          <DashboardTab bills={bills} showCents={tweaks.showCents}
            payPeriod={effectivePayPeriod} onPayPeriodChange={updatePayPeriod}
            budget={effectiveBudget} onBudgetChange={updateEffectiveBudget}
            payday={payday}
            paymentLog={paymentLog}
            onMarkBillPaid={markBillPaid}
            onToggleBillPrepaid={toggleBillPrepaid} />
        </div>
        <div className={`tab-panel${tab==='bills'?' is-active':''}`}>
          <BillsTab bills={bills} onEdit={openBillModal} onDelete={deleteBill} showCents={tweaks.showCents} />
        </div>
        <div className={`tab-panel${tab==='debt'?' is-active':''}`}>
          <DebtTab debts={debts} onEdit={openDebtModal} onDelete={deleteDebt}
            income={income} onIncomeChange={updateIncome}
            extraPayment={extraPayment} onExtraPaymentChange={updateExtraPayment} />
        </div>
        <div className={`tab-panel${tab==='calendar'?' is-active':''}`}>
          <CalendarTab bills={bills} payday={payday} onPaydayChange={updatePayday} />
        </div>
        <div className={`tab-panel${tab==='banker'?' is-active':''}`}>
          <BudgetTab bills={bills} showCents={tweaks.showCents}
            payPeriod={effectivePayPeriod} budget={effectiveBudget} onBudgetChange={updateEffectiveBudget}
            paymentLog={paymentLog} fundingLog={fundingLog} expenses={expenses}
            onLogBillPayment={logBillPayment} onUndoBillPayment={undoBillPayment} onAddExpense={addExpense}
            onAddMoney={openFundingModal}
            onEditActivity={editActivityItem} onDeleteActivity={deleteActivityItem}
            onOpenAnalytics={() => setTab('analytics')} />
        </div>
        <div className={`tab-panel${tab==='analytics'?' is-active':''}`}>
          <BudgetAnalyticsTab
            bills={bills}
            showCents={tweaks.showCents}
            payPeriod={effectivePayPeriod}
            budget={effectiveBudget}
            paymentLog={paymentLog}
            fundingLog={fundingLog}
            expenses={expenses}
            onBack={() => setTab('banker')}
          />
        </div>
        <div className={`tab-panel${tab==='settings'?' is-active':''}`}>
          <SettingsTab
            theme={theme}
            themeName={themeName}
            themes={Object.entries(THEME_PALETTES).map(([id, meta]) => ({ id, label: id === 'default' ? 'Default' : meta.label }))}
            onToggle={() => setTheme(t => t==='dark'?'light':'dark')}
            onThemeSelect={setThemeName}
            onExportData={exportData}
            onImportClick={() => importInputRef.current?.click()}
            tweaks={tweaks}
            onTweakChange={setTweak}
            payday={payday}
            onPaydayChange={updatePayday}
          />
        </div>
        <footer className="app-footer">created by Mr. Lewis 2026</footer>
      </div>

      {billModal && (
        <BillModal bill={billModal.bill} onSave={saveBill} onDeleteRequest={requestBillDelete} onClose={() => setBillModal(null)} />
      )}
      {debtModal && (
        <DebtModal debt={debtModal.debt} onSave={saveDebt} onDeleteRequest={requestDebtDelete} onClose={() => setDebtModal(null)} />
      )}
      {paymentModal && (
        <PaymentModal payment={paymentModal} showCents={tweaks.showCents} onSave={saveDashboardPayment} onClose={() => setPaymentModal(null)} />
      )}
      {quickEntryModal && (
        <QuickEntryModal entry={quickEntryModal} onSave={saveQuickEntry} onClose={() => setQuickEntryModal(null)} />
      )}
      {activityModal && (
        <ActivityModal item={activityModal} onSave={saveActivityItem} onDelete={deleteActivityItem} onClose={() => setActivityModal(null)} />
      )}
      {confirmModal && (
        <ConfirmModal {...confirmModal} onClose={() => setConfirmModal(null)} />
      )}
      {importPreview && (
        <ImportPreviewModal
          preview={importPreview}
          onClose={() => setImportPreview(null)}
          onConfirm={() => {
            applyImportedData(importPreview.data);
            setImportPreview(null);
            alert('BillyHub backup imported.');
          }}
        />
      )}
      {editMode && <TweaksPanel tweaks={tweaks} onChange={setTweak} />}
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);

