// State model — central app state backed by Supabase. Each user's data is isolated via RLS.

const StateContext = React.createContext(null);
const useAppState = () => React.useContext(StateContext);

// ── DB ↔ app mappers ──────────────────────────────────────────────────────────

const txToDb   = (t, uid) => ({ id: t.id, user_id: uid, type: t.type, amount: t.amount, category: t.category || null, source: t.source || null, date: t.date, note: t.note || null, member_id: t.memberId || null, recurring: t.recurring || null, day_of_month: t.dayOfMonth || null });
const dbToTx   = r => ({ id: r.id, type: r.type, amount: Number(r.amount), category: r.category, source: r.source, date: r.date, note: r.note, memberId: r.member_id, recurring: r.recurring, dayOfMonth: r.day_of_month });

const debtToDb = (d, uid) => ({ id: d.id, user_id: uid, name: d.name, balance: d.balance, original_balance: d.originalBalance, apr: d.apr, minimum: d.minimum, member_id: d.memberId || null });
const dbToDebt = r => ({ id: r.id, name: r.name, balance: Number(r.balance), originalBalance: Number(r.original_balance), apr: Number(r.apr), minimum: Number(r.minimum), memberId: r.member_id });

const goalToDb = (g, uid) => ({ id: g.id, user_id: uid, name: g.name, target: g.target, saved: g.saved, deadline: g.deadline || null, member_id: g.memberId || null });
const dbToGoal = r => ({ id: r.id, name: r.name, target: Number(r.target), saved: Number(r.saved), deadline: r.deadline, memberId: r.member_id });

const billToDb = (b, uid) => ({ id: b.id, user_id: uid, name: b.name, amount: b.amount, day_of_month: b.dayOfMonth, category: b.category || null, member_id: b.memberId || null });
const dbToBill = r => ({ id: r.id, name: r.name, amount: Number(r.amount), dayOfMonth: r.day_of_month, category: r.category, memberId: r.member_id });

const memberToDb = (m, uid) => ({ id: m.id, user_id: uid, name: m.name, color: m.color || null, initials: m.initials || null });
const dbToMember = r => ({ id: r.id, name: r.name, color: r.color, initials: r.initials });

// ── Default state ─────────────────────────────────────────────────────────────

const defaultMembers = (mode) => mode === 'household'
  ? [
      { id: 'm1', name: 'Thandi', color: AVATAR_COLORS[0], initials: 'T' },
      { id: 'm2', name: 'Sipho',  color: AVATAR_COLORS[2], initials: 'S' },
    ]
  : [{ id: 'm1', name: 'You', color: AVATAR_COLORS[0], initials: 'Y' }];

const initialState = (mode) => ({
  mode,
  members: defaultMembers(mode),
  transactions: [],
  debts: [],
  goals: [],
  bills: [],
});

// ── Provider ──────────────────────────────────────────────────────────────────

const StateProvider = ({ children, mode, setMode }) => {
  const { user } = useAuth();
  const [state, setState] = React.useState(() => initialState(mode));
  const [dataReady, setDataReady] = React.useState(false);
  const [household, setHousehold] = React.useState(null);
  const [reloadKey, setReloadKey] = React.useState(0);
  const triggerReload = React.useCallback(() => setReloadKey(k => k + 1), []);
  const db = supabaseClient;

  // Load all data — RLS handles filtering (own data + partner's if household active)
  React.useEffect(() => {
    if (!user) return;
    setDataReady(false);

    // Ensure profile has email stored (needed for partner display)
    db.from('profiles').upsert({ id: user.id, email: user.email }, { onConflict: 'id' });

    Promise.all([
      db.from('transactions').select('*').order('created_at', { ascending: false }),
      db.from('debts').select('*'),
      db.from('goals').select('*'),
      db.from('bills').select('*'),
      db.from('members').select('*').eq('user_id', user.id),
      db.from('profiles').select('mode').eq('id', user.id).maybeSingle(),
      db.from('households').select('*').or(`owner_id.eq.${user.id},partner_id.eq.${user.id}`).maybeSingle(),
    ]).then(async ([txR, debtR, goalR, billR, memberR, profileR, householdR]) => {
      const transactions = (txR.data || []).map(dbToTx);
      const debts        = (debtR.data || []).map(dbToDebt);
      const goals        = (goalR.data || []).map(dbToGoal);
      const bills        = (billR.data || []).map(dbToBill);
      const members      = memberR.data?.length ? memberR.data.map(dbToMember) : defaultMembers(mode);
      const savedMode    = profileR.data?.mode || mode;
      setState({ mode: savedMode, members, transactions, debts, goals, bills });
      if (savedMode !== mode) setMode(savedMode);

      // Load household + partner info
      if (householdR.data) {
        const h = householdR.data;
        const isOwner = h.owner_id === user.id;
        const partnerId = isOwner ? h.partner_id : h.owner_id;
        let partnerEmail = null;
        if (partnerId) {
          const { data: pProfile } = await db.from('profiles').select('email').eq('id', partnerId).maybeSingle();
          partnerEmail = pProfile?.email;
        }
        setHousehold({
          id: h.id, isOwner, status: h.status, inviteToken: h.invite_token,
          partner: partnerId ? { id: partnerId, email: partnerEmail } : null,
        });
      } else {
        setHousehold(null);
      }
    }).finally(() => setDataReady(true));
  }, [user?.id, reloadKey]);

  // Sync mode changes → profile table
  React.useEffect(() => {
    if (!user || !dataReady) return;
    db.from('profiles').upsert({ id: user.id, mode }, { onConflict: 'id' });
    setState(s => {
      if (s.mode === mode) return s;
      let members = s.members;
      if (mode === 'single' && s.members.length > 1) members = [s.members[0]];
      else if (mode === 'household' && s.members.length < 2) members = defaultMembers('household');
      return { ...s, mode, members };
    });
  }, [mode, dataReady]);

  const userId = user?.id;

  const actions = React.useMemo(() => ({

    addTransaction: t => {
      const tx = { id: uid(), ...t };
      setState(s => ({ ...s, transactions: [tx, ...s.transactions] }));
      db.from('transactions').insert(txToDb(tx, userId)).then(({ error }) => { if (error) console.error('tx insert', error); });
    },

    deleteTransaction: id => {
      setState(s => ({ ...s, transactions: s.transactions.filter(t => t.id !== id) }));
      db.from('transactions').delete().eq('id', id).eq('user_id', userId).then(({ error }) => { if (error) console.error('tx delete', error); });
    },

    addDebt: d => {
      const debt = { id: uid(), originalBalance: d.balance, ...d };
      setState(s => ({ ...s, debts: [...s.debts, debt] }));
      db.from('debts').insert(debtToDb(debt, userId)).then(({ error }) => { if (error) console.error('debt insert', error); });
    },

    updateDebt: (id, patch) => {
      setState(s => {
        const debts = s.debts.map(d => d.id === id ? { ...d, ...patch } : d);
        const updated = debts.find(d => d.id === id);
        if (updated) db.from('debts').upsert(debtToDb(updated, userId)).then(({ error }) => { if (error) console.error('debt upsert', error); });
        return { ...s, debts };
      });
    },

    deleteDebt: id => {
      setState(s => ({ ...s, debts: s.debts.filter(d => d.id !== id) }));
      db.from('debts').delete().eq('id', id).eq('user_id', userId).then(({ error }) => { if (error) console.error('debt delete', error); });
    },

    addGoal: g => {
      const goal = { id: uid(), saved: 0, ...g };
      setState(s => ({ ...s, goals: [...s.goals, goal] }));
      db.from('goals').insert(goalToDb(goal, userId)).then(({ error }) => { if (error) console.error('goal insert', error); });
    },

    updateGoal: (id, patch) => {
      setState(s => {
        const goals = s.goals.map(g => g.id === id ? { ...g, ...patch } : g);
        const updated = goals.find(g => g.id === id);
        if (updated) db.from('goals').upsert(goalToDb(updated, userId)).then(({ error }) => { if (error) console.error('goal upsert', error); });
        return { ...s, goals };
      });
    },

    deleteGoal: id => {
      setState(s => ({ ...s, goals: s.goals.filter(g => g.id !== id) }));
      db.from('goals').delete().eq('id', id).eq('user_id', userId).then(({ error }) => { if (error) console.error('goal delete', error); });
    },

    addBill: b => {
      const bill = { id: uid(), ...b };
      setState(s => ({ ...s, bills: [...s.bills, bill] }));
      db.from('bills').insert(billToDb(bill, userId)).then(({ error }) => { if (error) console.error('bill insert', error); });
    },

    deleteBill: id => {
      setState(s => ({ ...s, bills: s.bills.filter(b => b.id !== id) }));
      db.from('bills').delete().eq('id', id).eq('user_id', userId).then(({ error }) => { if (error) console.error('bill delete', error); });
    },

    addMember: m => {
      setState(s => {
        const member = { id: uid(), color: AVATAR_COLORS[s.members.length % AVATAR_COLORS.length], ...m };
        db.from('members').insert(memberToDb(member, userId)).then(({ error }) => { if (error) console.error('member insert', error); });
        return { ...s, members: [...s.members, member] };
      });
    },

    updateMember: (id, patch) => {
      setState(s => {
        const members = s.members.map(m => m.id === id ? { ...m, ...patch } : m);
        const updated = members.find(m => m.id === id);
        if (updated) db.from('members').upsert(memberToDb(updated, userId)).then(({ error }) => { if (error) console.error('member upsert', error); });
        return { ...s, members };
      });
    },

    deleteMember: id => {
      setState(s => ({ ...s, members: s.members.filter(m => m.id !== id) }));
      db.from('members').delete().eq('id', id).eq('user_id', userId).then(({ error }) => { if (error) console.error('member delete', error); });
    },

    loadDemo: () => {
      setState(s => {
        const demo = buildDemo(s.mode, s.members);
        Promise.all([
          db.from('transactions').upsert(demo.transactions.map(t => txToDb(t, userId))),
          db.from('debts').upsert(demo.debts.map(d => debtToDb(d, userId))),
          db.from('goals').upsert(demo.goals.map(g => goalToDb(g, userId))),
          db.from('bills').upsert(demo.bills.map(b => billToDb(b, userId))),
        ]).then(results => results.forEach(({ error }) => { if (error) console.error('demo upsert', error); }));
        return { ...s, ...demo };
      });
    },

    clearAll: () => {
      setState(s => ({ ...initialState(s.mode), members: s.members }));
      Promise.all([
        db.from('transactions').delete().eq('user_id', userId),
        db.from('debts').delete().eq('user_id', userId),
        db.from('goals').delete().eq('user_id', userId),
        db.from('bills').delete().eq('user_id', userId),
      ]).then(results => results.forEach(({ error }) => { if (error) console.error('clear', error); }));
    },

  }), [userId]);

  // ── Household actions ────────────────────────────────────────────────────────

  const householdActions = React.useMemo(() => ({

    createHousehold: async () => {
      const { data, error } = await db.from('households').insert({ owner_id: userId }).select().single();
      if (error) throw error;
      setHousehold({ id: data.id, isOwner: true, status: 'pending', inviteToken: data.invite_token, partner: null });
    },

    acceptHousehold: async (token) => {
      const { data: h, error: e1 } = await db.from('households').select('*').eq('invite_token', token).eq('status', 'pending').maybeSingle();
      if (e1 || !h) throw new Error('Invalid or expired invite link.');
      if (h.owner_id === userId) throw new Error("You can't accept your own invite.");
      const { error: e2 } = await db.from('households').update({ partner_id: userId, status: 'active' }).eq('id', h.id);
      if (e2) throw e2;
      triggerReload();
    },

    leaveHousehold: async () => {
      if (!household) return;
      await db.from('households').delete().eq('id', household.id);
      setHousehold(null);
      triggerReload();
    },

  }), [userId, household, triggerReload]);

  // Derived selectors (unchanged)
  const derived = React.useMemo(() => {
    const txs = state.transactions;
    const income  = txs.filter(t => t.type === 'income').reduce((a, t) => a + t.amount, 0);
    const expense = txs.filter(t => t.type === 'expense').reduce((a, t) => a + t.amount, 0);
    const net = income - expense;
    const byCategory = {};
    txs.filter(t => t.type === 'expense').forEach(t => { byCategory[t.category] = (byCategory[t.category] || 0) + t.amount; });
    const categoryBreakdown = Object.entries(byCategory)
      .map(([id, amount]) => ({ ...categoryById(id), amount, pct: expense ? amount / expense : 0 }))
      .sort((a, b) => b.amount - a.amount);
    const totalDebt         = state.debts.reduce((a, d) => a + d.balance, 0);
    const totalGoals        = state.goals.reduce((a, g) => a + g.saved, 0);
    const totalGoalsTarget  = state.goals.reduce((a, g) => a + g.target, 0);
    const months = [];
    const now = today();
    for (let i = 5; i >= 0; i--) {
      const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
      const m = { key: `${d.getFullYear()}-${d.getMonth()}`, label: monthNames[d.getMonth()], income: 0, expense: 0 };
      txs.forEach(t => {
        const td = new Date(t.date);
        if (td.getFullYear() === d.getFullYear() && td.getMonth() === d.getMonth()) {
          if (t.type === 'income') m.income += t.amount; else m.expense += t.amount;
        }
      });
      m.net = m.income - m.expense;
      months.push(m);
    }
    return { income, expense, net, categoryBreakdown, totalDebt, totalGoals, totalGoalsTarget, months };
  }, [state]);

  if (!dataReady) return (
    <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--surface)' }}>
      <div style={{ width: 32, height: 32, borderRadius: '50%', border: '3px solid var(--line)', borderTopColor: 'var(--ink)', animation: 'spin 0.7s linear infinite' }} />
    </div>
  );

  return (
    <StateContext.Provider value={{ state, ...actions, ...householdActions, household, derived, setMode }}>
      {children}
    </StateContext.Provider>
  );
};

// ── Demo data ─────────────────────────────────────────────────────────────────

const buildDemo = (mode, members) => {
  const m1 = members[0]?.id || 'm1';
  const m2 = members[1]?.id || m1;
  const now = today();
  const day = (n, monthsAgo = 0) => new Date(now.getFullYear(), now.getMonth() - monthsAgo, n).toISOString();
  const txs = [];
  for (let mo = 0; mo < 4; mo++) {
    txs.push({ id: uid(), type: 'income',  amount: 38500, source: 'salary',    category: null,            date: day(25, mo), note: 'Monthly salary',          memberId: m1, recurring: 'monthly', dayOfMonth: 25 });
    if (mode === 'household') txs.push({ id: uid(), type: 'income', amount: 32000, source: 'salary', category: null, date: day(25, mo), note: 'Monthly salary', memberId: m2, recurring: 'monthly', dayOfMonth: 25 });
    txs.push({ id: uid(), type: 'expense', amount: 12500, category: 'rent',          date: day(1,  mo), note: 'Bond / Rent',             memberId: m1, recurring: 'monthly', dayOfMonth: 1  });
    txs.push({ id: uid(), type: 'expense', amount: 2400,  category: 'utilities',     date: day(5,  mo), note: 'Eskom + water',            memberId: m1, recurring: 'monthly', dayOfMonth: 5  });
    txs.push({ id: uid(), type: 'expense', amount: 1100,  category: 'utilities',     date: day(7,  mo), note: 'Fibre + cell',             memberId: mode === 'household' ? m2 : m1, recurring: 'monthly', dayOfMonth: 7 });
    txs.push({ id: uid(), type: 'expense', amount: 4200 + (mo*120), category: 'groceries', date: day(8, mo), note: 'Checkers monthly',   memberId: m1 });
    txs.push({ id: uid(), type: 'expense', amount: 1850,  category: 'groceries',     date: day(20, mo), note: 'Woolies',                  memberId: mode === 'household' ? m2 : m1 });
    txs.push({ id: uid(), type: 'expense', amount: 2600,  category: 'transport',     date: day(10, mo), note: 'Petrol',                   memberId: m1 });
    txs.push({ id: uid(), type: 'expense', amount: 980,   category: 'subscriptions', date: day(2,  mo), note: 'DStv + Netflix + Spotify', memberId: m1, recurring: 'monthly', dayOfMonth: 2  });
    txs.push({ id: uid(), type: 'expense', amount: 1640,  category: 'eating-out',    date: day(15, mo), note: 'Restaurants & takeaways',  memberId: mode === 'household' ? m2 : m1 });
    txs.push({ id: uid(), type: 'expense', amount: 720,   category: 'entertainment', date: day(18, mo), note: 'Cinema, drinks',           memberId: m1 });
    if (mo === 0) {
      txs.push({ id: uid(), type: 'expense', amount: 1280, category: 'medical',  date: day(12, mo), note: 'Pharmacy + GP',        memberId: mode === 'household' ? m2 : m1 });
      txs.push({ id: uid(), type: 'income',  amount: 4500, source: 'freelance', category: null, date: day(22, mo), note: 'Side project invoice', memberId: m1 });
    }
  }
  return {
    transactions: txs,
    debts: [
      { id: uid(), name: 'Credit card — FNB',       balance: 24800,  originalBalance: 30000,  apr: 22.5, minimum: 800,  memberId: m1 },
      { id: uid(), name: 'Personal loan — Capitec', balance: 48200,  originalBalance: 60000,  apr: 18.0, minimum: 1900, memberId: m1 },
      { id: uid(), name: 'Vehicle finance — WesBank',balance: 165000, originalBalance: 220000, apr: 12.5, minimum: 4200, memberId: mode === 'household' ? m2 : m1 },
      { id: uid(), name: 'Store account — Edgars',  balance: 3400,   originalBalance: 5000,   apr: 24.0, minimum: 350,  memberId: mode === 'household' ? m2 : m1 },
    ],
    goals: [
      { id: uid(), name: 'Emergency fund',   target: 90000, saved: 32500, memberId: m1 },
      { id: uid(), name: 'December holiday', target: 25000, saved: 8200,  deadline: '2026-12-01', memberId: mode === 'household' ? m2 : m1 },
      { id: uid(), name: 'New laptop',       target: 28000, saved: 12000, memberId: m1 },
    ],
    bills: [
      { id: uid(), name: 'Bond / Rent',     amount: 12500, dayOfMonth: 1,  category: 'rent',          memberId: m1 },
      { id: uid(), name: 'Subscriptions',   amount: 980,   dayOfMonth: 2,  category: 'subscriptions', memberId: m1 },
      { id: uid(), name: 'Eskom + water',   amount: 2400,  dayOfMonth: 5,  category: 'utilities',     memberId: m1 },
      { id: uid(), name: 'Fibre + cell',    amount: 1100,  dayOfMonth: 7,  category: 'utilities',     memberId: mode === 'household' ? m2 : m1 },
      { id: uid(), name: 'Vehicle finance', amount: 4200,  dayOfMonth: 25, category: 'transport',     memberId: mode === 'household' ? m2 : m1 },
    ],
  };
};

Object.assign(window, { StateContext, useAppState, StateProvider, buildDemo });
