// phone-app.jsx — bezel-free, Supabase-wired. Fills the viewport.

const TWEAK_DEFAULTS_PHONE = { theme: 'midnight', screen: 'today' };

function PhoneApp() {
  const theme = THEMES[TWEAK_DEFAULTS_PHONE.theme] || THEMES.midnight;
  const isDark = ['midnight','dark','oled','tactical','stack'].includes(theme.name);

  const [session, setSession] = React.useState(undefined); // undefined = checking
  const [tab, setTab] = React.useState(TWEAK_DEFAULTS_PHONE.screen);
  const [activeCustomerId, setActiveCustomerId] = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [showDeliveries, setShowDeliveries] = React.useState(false);
  const [showChat, setShowChat] = React.useState(false);
  const [customers, setCustomers] = React.useState([]);
  const [todos, setTodos] = React.useState([]);
  const [loaded, setLoaded] = React.useState(false);

  // ── auth: check session + subscribe ──
  React.useEffect(() => {
    SBAuth.getSession().then(setSession);
    const { data: sub } = SBAuth.onChange(setSession);
    return () => sub?.subscription?.unsubscribe?.();
  }, []);

  // ── load customers + todos once signed in ──
  React.useEffect(() => {
    if (!session) { setCustomers([]); setTodos([]); setLoaded(false); return; }
    let alive = true;
    SBData.load().then(rows => { if (alive) { setCustomers(rows); setLoaded(true); } });
    SBTodos.load().then(rows => { if (alive) setTodos(rows); });
    return () => { alive = false; };
  }, [session && session.user && session.user.id]);

  // always-fresh mirror of customers for patch-merging edits
  const customersRef = React.useRef([]);
  React.useEffect(() => { customersRef.current = customers; }, [customers]);

  // ── theme background ──
  React.useEffect(() => {
    document.body.style.background = theme.bg;
    document.documentElement.style.background = theme.bg;
    const m = document.querySelector('meta[name="theme-color"]');
    if (m) m.setAttribute('content', theme.bg);
  }, [theme.bg]);

  const userId = session && session.user && session.user.id;

  const openCustomer = (id) => setActiveCustomerId(id);
  const closeCustomer = () => setActiveCustomerId(null);

  // patch-based update for ANY customer by id: merges changed fields over the LATEST
  // customer (not a stale render-time copy), so two quick edits can't clobber each
  // other. Accepts an object patch or a (current)=>patch function.
  const patchCustomer = (id, patchOrFn) => {
    const cur = customersRef.current.find(c => c.id === id);
    if (!cur) return;
    const patch = typeof patchOrFn === 'function' ? patchOrFn(cur) : patchOrFn;
    const merged = { ...cur, ...patch };
    customersRef.current = customersRef.current.map(c => c.id === id ? merged : c);
    setCustomers(cs => cs.map(c => c.id === id ? merged : c));
    SBData.update(merged).then(saved => {
      if (saved) {
        customersRef.current = customersRef.current.map(c => c.id === saved.id ? saved : c);
        setCustomers(cs => cs.map(c => c.id === saved.id ? saved : c));
      }
    });
  };

  // the open customer card edits the active customer
  const updateCustomer = (patchOrFn) => patchCustomer(activeCustomerId, patchOrFn);

  // mark a hot lead "done for today" — hidden from Today until tomorrow (stamp = local day)
  const markHotDone = (customerId) => patchCustomer(customerId, { hotDoneOn: dayISO(0) });

  // mark a Today callback/appointment done: clear the date (drops it off the timeline)
  // and log it to history.
  const completeScheduleItem = (customerId, kind) => {
    const dLabel = new Date().toLocaleDateString('en-AU', { weekday: 'short', day: 'numeric' });
    patchCustomer(customerId, (cur) => {
      const text = kind === 'appt' ? 'Appointment done' : 'Called back';
      const history = [{ d: dLabel, text: '✓ ' + text, status: 'neutral' }, ...(cur.history || [])];
      return kind === 'appt'
        ? { appointmentAt: null, history }
        : { callbackAt: null, history };
    });
  };

  const addCustomer = (draft) => {
    const tempId = 'tmp_' + Date.now();
    const c = {
      ...draft, id: tempId,
      history: [{ d: new Date().toLocaleDateString('en-AU', { weekday: 'short', day: 'numeric' }), text: 'Added · ' + (draft.source || 'manual'), status: draft.status || 'neutral' }],
    };
    setCustomers(cs => [c, ...cs]);          // optimistic
    setActiveCustomerId(tempId);
    SBData.create(c, userId).then(saved => {
      if (!saved) return;
      setCustomers(cs => cs.map(x => x.id === tempId ? saved : x));
      setActiveCustomerId(prev => prev === tempId ? saved.id : prev);
    });
  };

  const deleteCustomer = (id) => {
    setCustomers(cs => cs.filter(c => c.id !== id)); // optimistic remove
    setActiveCustomerId(null);
    SBData.remove(id);
  };

  // ── personal to-do items (Today screen) ──
  const addTodo = (text) => {
    const t = (text || '').trim(); if (!t) return;
    const tempId = 'tmp_' + Date.now();
    setTodos(ts => [...ts, { id: tempId, text: t, done: false, created_at: new Date().toISOString() }]);
    SBTodos.add(t, userId).then(saved => { if (saved) setTodos(ts => ts.map(x => x.id === tempId ? saved : x)); });
  };
  const toggleTodo = (id) => setTodos(ts => {
    const item = ts.find(x => x.id === id); if (!item) return ts;
    const done = !item.done; SBTodos.setDone(id, done);
    return ts.map(x => x.id === id ? { ...x, done } : x);
  });
  const deleteTodo = (id) => { setTodos(ts => ts.filter(x => x.id !== id)); SBTodos.remove(id); };

  // executes a confirmed chat tool-call against the same data layer the UI uses.
  // returns { ok, message } which is sent back to the model as the tool result.
  const chatApply = async (name, input) => {
    try {
      if (name === 'create_customer') {
        const dLabel = new Date().toLocaleDateString('en-AU', { weekday: 'short', day: 'numeric' });
        const draft = {
          name: input.name || 'Unnamed', car: input.car || '', location: input.location || '',
          source: input.source || 'Manual', status: input.status || 'neutral', stage: input.stage || 'New lead',
          finance: input.finance ? [input.finance] : ['Unknown'], journey: input.journey || 'Just looking',
          notes: input.notes || '', nurturing: !!input.nurturing, carsWanted: !!input.carsWanted,
          readyDate: input.readyDate || null, id: 'tmp_' + Date.now(),
          history: [{ d: dLabel, text: 'Added · assistant', status: input.status || 'neutral' }],
        };
        if (draft.status === 'blue') draft.nurturing = true;
        customersRef.current = [draft, ...customersRef.current];
        setCustomers(cs => [draft, ...cs]);
        const saved = await SBData.create(draft, userId);
        if (saved) {
          customersRef.current = customersRef.current.map(x => x.id === draft.id ? saved : x);
          setCustomers(cs => cs.map(x => x.id === draft.id ? saved : x));
        }
        return { ok: true, message: 'Created ' + draft.name };
      }
      if (name === 'update_customer') {
        const cur = customersRef.current.find(c => c.id === input.id);
        if (!cur) return { ok: false, message: 'No customer with that id' };
        const patch = {};
        ['car', 'location', 'phone', 'status', 'stage', 'journey', 'readyDate',
         'callbackAt', 'callbackLabel', 'appointmentAt', 'appointmentLabel']
          .forEach(k => { if (input[k] !== undefined) patch[k] = input[k] === '' ? null : input[k]; });
        // a note from Alejandro is appended to the history, not overwritten
        if (input.notes !== undefined && input.notes !== '') {
          const existing = (Array.isArray(cur.noteLog) && cur.noteLog.length)
            ? cur.noteLog : (cur.notes ? [{ t: null, text: cur.notes }] : []);
          patch.noteLog = [{ t: new Date().toISOString(), text: input.notes }, ...existing];
          patch.notes = input.notes;
        }
        if (input.finance !== undefined) patch.finance = input.finance ? [input.finance] : ['Unknown'];
        if (input.nurturing !== undefined) patch.nurturing = !!input.nurturing;
        if (input.carsWanted !== undefined) patch.carsWanted = !!input.carsWanted;
        if (patch.status === 'blue') patch.nurturing = true;
        else if (patch.status === 'red' || patch.status === 'neutral') patch.nurturing = false;
        patchCustomer(input.id, patch);
        return { ok: true, message: 'Updated ' + (cur.name || 'customer') };
      }
      if (name === 'complete_schedule') {
        const cur = customersRef.current.find(c => c.id === input.id);
        if (!cur) return { ok: false, message: 'No customer with that id' };
        completeScheduleItem(input.id, input.kind === 'appointment' ? 'appt' : 'callback');
        return { ok: true, message: 'Marked ' + input.kind + ' done for ' + (cur.name || 'customer') };
      }
      if (name === 'remove_customer') {
        const cur = customersRef.current.find(c => c.id === input.id);
        if (!cur) return { ok: false, message: 'No customer with that id' };
        deleteCustomer(input.id);
        return { ok: true, message: 'Removed ' + (cur.name || 'customer') };
      }
      return { ok: false, message: 'Unknown action' };
    } catch (e) {
      return { ok: false, message: String(e && e.message || e) };
    }
  };

  const activeCustomer = customers.find(c => c.id === activeCustomerId);

  // swipe in from the left edge → go back (iOS-style)
  const touchRef = React.useRef(null);
  const goBack = () => {
    if (activeCustomer) closeCustomer();
    else if (showAdd) setShowAdd(false);
    else if (showDeliveries) setShowDeliveries(false);
    else if (showChat) setShowChat(false);
  };
  const onTouchStart = (e) => {
    const t = e.touches[0];
    touchRef.current = { x: t.clientX, y: t.clientY, time: Date.now(), edge: t.clientX < 40 };
  };
  const onTouchEnd = (e) => {
    const s = touchRef.current; touchRef.current = null;
    if (!s || !s.edge) return;
    const t = e.changedTouches[0];
    const dx = t.clientX - s.x, dy = t.clientY - s.y, dt = Date.now() - s.time;
    if (dx > 60 && Math.abs(dy) < 60 && dt < 700) goBack();
  };

  // schedule derives from each customer's callback/appointment date-times.
  // Uses LOCAL day (dayISO), not UTC, so AEST-morning appointments match "today".
  const isoDay = (off) => dayISO(off);
  const deriveSchedule = (day) => {
    const items = [];
    for (const c of customers) {
      if (c.appointmentAt && c.appointmentAt.slice(0, 10) === day)
        items.push({ time: c.appointmentAt.slice(11, 16), kind: 'appt', customerId: c.id, label: c.car || 'Appointment', tag: c.appointmentLabel || '' });
      if (c.callbackAt && c.callbackAt.slice(0, 10) === day)
        items.push({ time: c.callbackAt.slice(11, 16), kind: 'callback', customerId: c.id, label: c.car || 'Callback', tag: c.callbackLabel || '' });
    }
    return items;
  };
  const schedule = deriveSchedule(isoDay(0));
  const tomorrowSchedule = deriveSchedule(isoDay(1));

  const Beam = () => theme.beam ? (
    <React.Fragment>
      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 380, zIndex: 1, pointerEvents: 'none',
        background: 'radial-gradient(ellipse 65% 70% at 50% 2%, rgba(120,108,255,0.55) 0%, rgba(80,70,220,0.18) 38%, transparent 72%)' }}/>
      <div style={{ position: 'absolute', top: -20, left: '20%', width: 280, height: 460, zIndex: 1, pointerEvents: 'none',
        background: 'linear-gradient(165deg, rgba(150,140,255,0.22), transparent 60%)', transform: 'rotate(15deg)', filter: 'blur(40px)' }}/>
    </React.Fragment>
  ) : null;

  const shellStyle = {
    // Fill #root, whose height is the JS-measured window.innerHeight (--app-h). This is
    // the reliable way to fill an iOS standalone PWA top-to-bottom (100dvh/100vh and
    // position:fixed both mis-size there, leaving a black gap at the bottom).
    width: '100%', height: '100%',
    background: theme.bg, color: theme.text, position: 'relative', overflow: 'hidden',
    fontFamily: '-apple-system, "SF Pro Text", system-ui, sans-serif',
    WebkitFontSmoothing: 'antialiased', display: 'flex', flexDirection: 'column',
  };
  const topPad = (
    <style>{`#phone-app [data-topbar]{padding-top:calc(max(env(safe-area-inset-top,0px),24px) + 2px)!important;}`}</style>
  );

  // ── auth gate ──
  if (session === undefined) {
    return <div id="phone-app" style={shellStyle}>{topPad}<Beam/></div>; // brief blank while checking
  }
  if (!session) {
    return (
      <div id="phone-app" style={shellStyle}>{topPad}<Beam/>
        <LoginScreen theme={theme} />
      </div>
    );
  }

  const renderScreen = () => {
    if (showAdd) return <AddCaptureScreen theme={theme} dark={isDark} onClose={() => setShowAdd(false)} onSave={addCustomer} />;
    if (activeCustomer) return <CustomerScreen theme={theme} dark={isDark} customer={activeCustomer} onBack={closeCustomer} onUpdate={updateCustomer} onDelete={deleteCustomer} />;
    if (showChat) return <ChatScreen theme={theme} customers={customers} onBack={() => setShowChat(false)} onApply={chatApply} openCustomer={openCustomer} />;
    if (showDeliveries) return <DeliveriesScreen theme={theme} customers={customers} onBack={() => setShowDeliveries(false)} openCustomer={openCustomer} onPatch={patchCustomer} />;
    if (tab === 'today') return <TodayScreen theme={theme} dark={isDark} customers={customers} schedule={schedule} openCustomer={openCustomer} setTab={setTab} onComplete={completeScheduleItem} onHotDone={markHotDone} onOpenDeliveries={() => setShowDeliveries(true)} onOpenChat={() => setShowChat(true)} todos={todos} onAddTodo={addTodo} onToggleTodo={toggleTodo} onDeleteTodo={deleteTodo} />;
    if (tab === 'pipeline') return <PipelineScreen theme={theme} customers={customers} schedule={schedule} tomorrowSchedule={tomorrowSchedule} openCustomer={openCustomer} onOpenDeliveries={() => setShowDeliveries(true)} />;
    if (tab === 'nurturing') return <NurturingScreen theme={theme} customers={customers} openCustomer={openCustomer} setTab={setTab} onAdd={() => setShowAdd(true)} />;
    if (tab === 'reminders') return <RemindersScreen theme={theme} customers={customers} schedule={schedule} openCustomer={openCustomer} />;
    return null;
  };

  const showTabs = !activeCustomer && !showAdd && !showDeliveries && !showChat;

  return (
    <div id="phone-app" style={shellStyle} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
      {topPad}
      <Beam/>
      <div style={{ flex: 1, minHeight: 0, position: 'relative', zIndex: 5, display: 'flex', flexDirection: 'column' }}>
        {renderScreen()}
      </div>
      {showTabs && (
        <div style={{ position: 'relative', zIndex: 5 }}>
          <BottomTabBar theme={theme} dark={isDark} tab={tab} setTab={setTab} onAdd={() => setShowAdd(true)} />
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<PhoneApp/>);
