// ResDri driver — API-driven app shell.
//
// Boot sequence:
//   1) Read JWT from localStorage. If valid (/auth/me succeeds), enter app.
//   2) Else show LoginScreen → on success, enter app.
//   3) Once in app: fetch /drivers/me + /orders/active + /orders/pool;
//      connect WebSocket for live order.new / order.update.
//
// All state transitions hit real backend endpoints.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "dark": false,
  "palette": "teal",
  "font": "Cairo",
  "density": "comfortable",
  "alertPlacement": "right",
  "homeState": "online",
  "radiusKm": 0
}/*EDITMODE-END*/;
// radiusKm starts at 0 (falsy) so the server's defaultRadiusKm is used until
// the driver explicitly moves the slider — then their choice persists.

// Last-resort fallback config if /api/config can't be reached on boot.
// Everything here is overridden the moment the server responds.
const CONFIG_FALLBACK = {
  brandName: 'ResDri', pilotCity: 'أم الفحم', currency: '₪',
  defaultRadiusKm: 1.5, maxRadiusKm: 5, radiusMinKm: 0.5, radiusStepKm: 0.5,
  cityLat: 32.5167, cityLng: 35.1500,
  locationPushSeconds: 25, supportPhone: null, supportWhatsapp: null,
};

// Map backend status → prototype flow id
const STATUS_TO_FLOW = {
  PENDING: 'idle',
  ACCEPTED: 'toPickup',
  EN_ROUTE_PICKUP: 'toPickup',
  AT_PICKUP: 'atPickup',
  PICKED_UP: 'toDropoff',
  EN_ROUTE_DROPOFF: 'toDropoff',
  AT_DROPOFF: 'cash',
  DELIVERED: 'cash',
  COMPLETED: 'completed',
  CANCELLED: 'idle',
};

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // ── Auth + boot state
  const [authPhase, setAuthPhase] = React.useState('loading'); // 'loading' | 'login' | 'app'
  const [driver, setDriver] = React.useState(null);
  const [bootError, setBootError] = React.useState(null);

  // ── Server-driven config (radius bounds, city centre, currency, GPS cadence…)
  // Fetched once on boot. window.RESDRI_CONFIG is also set so non-React modules
  // (map.jsx projection) can read it. Falls back to CONFIG_FALLBACK until loaded.
  const [config, setConfig] = React.useState(window.RESDRI_CONFIG || CONFIG_FALLBACK);
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const c = await window.api.getConfig();
        if (cancelled || !c) return;
        const merged = { ...CONFIG_FALLBACK, ...c };
        window.RESDRI_CONFIG = merged;
        setConfig(merged);
      } catch (e) {
        console.warn('[config] using fallback —', e.message);
        window.RESDRI_CONFIG = CONFIG_FALLBACK;
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Fullscreen mode: small viewport OR launched as installed PWA.
  // In fullscreen we drop the iOS device frame and the dev chrome (Tweaks panel,
  // step label, WS badge) so the app looks like a real native app on the phone.
  const [fullScreen, setFullScreen] = React.useState(() => computeFullScreen());
  React.useEffect(() => {
    const recompute = () => setFullScreen(computeFullScreen());
    window.addEventListener('resize', recompute);
    const mql = window.matchMedia('(display-mode: standalone)');
    if (mql.addEventListener) mql.addEventListener('change', recompute);
    return () => {
      window.removeEventListener('resize', recompute);
      if (mql.removeEventListener) mql.removeEventListener('change', recompute);
    };
  }, []);

  // ── App state
  const [pool, setPool] = React.useState([]);
  const [activeOrder, setActiveOrder] = React.useState(null);
  const [orderId, setOrderId] = React.useState(null);
  const [flow, setFlow] = React.useState('idle');
  const [tab, setTab]   = React.useState('home');
  const [online, setOnline] = React.useState(false);
  const [wsStatus, setWsStatus] = React.useState('disconnected');
  // Driver's last known GPS position; null until geolocation grants or seeded.
  const [driverLocation, setDriverLocation] = React.useState(null);
  // Working radius — driver's saved choice wins; otherwise the server default.
  // Bounds (min/max/step) also come from config.
  const radiusKm = t.radiusKm || config.defaultRadiusKm || 1.5;
  const setRadiusKm = (v) => setTweak('radiusKm', v);

  // Theme application
  React.useEffect(() => {
    const root = document.documentElement;
    root.dataset.theme = t.dark ? 'dark' : 'light';
    root.dataset.palette = t.palette;
    root.dataset.density = t.density;
    root.style.setProperty('--font', `"${t.font}", "Cairo", "Tajawal", system-ui, sans-serif`);
  }, [t.dark, t.palette, t.font, t.density]);

  // ── Boot
  React.useEffect(() => {
    (async () => {
      if (!window.api.getToken()) {
        setAuthPhase('login'); return;
      }
      try {
        await window.api.me();
        await enterApp();
      } catch (e) {
        // Token invalid/expired
        window.api.setToken(null);
        setAuthPhase('login');
      }
    })();
  }, []);

  async function enterApp() {
    try {
      const [d, active, p] = await Promise.all([
        window.api.driverMe(),
        window.api.active().catch(() => null),
        window.api.pool(),
      ]);
      setDriver(d);
      setOnline(!!d.isOnline);
      setPool(p || []);
      if (active && active.id) {
        setOrderId(active.id);
        setActiveOrder(active);
        setFlow(STATUS_TO_FLOW[active.status] || 'idle');
      } else {
        setFlow('idle');
      }
      setAuthPhase('app');
      window.resdriWs.connect();
    } catch (e) {
      setBootError(e.message || 'تعذّر التحميل');
      setAuthPhase('login');
    }
  }

  // ── Geolocation: watch the device GPS, push to backend, mirror to state.
  // Falls back silently when the browser blocks it (e.g., HTTP origin → no
  // geolocation on iOS Safari). The pool then uses the seeded city centre.
  React.useEffect(() => {
    if (authPhase !== 'app') return;
    if (!navigator.geolocation) return;
    // Seed with driver's stored location from /drivers/me if available
    if (driver?.currentLat && driver?.currentLng) {
      setDriverLocation({ lat: driver.currentLat, lng: driver.currentLng });
    }
    let lastPush = 0;
    const pushIntervalMs = (config.locationPushSeconds || 25) * 1000;
    const watcher = navigator.geolocation.watchPosition(
      (p) => {
        const lat = p.coords.latitude, lng = p.coords.longitude;
        setDriverLocation({ lat, lng });
        const now = Date.now();
        if (now - lastPush > pushIntervalMs) {
          lastPush = now;
          window.api.updateLocation(lat, lng).catch(() => {});
        }
      },
      (err) => console.warn('[geo]', err.message),
      { enableHighAccuracy: true, maximumAge: 30000, timeout: 60000 }
    );
    return () => navigator.geolocation.clearWatch(watcher);
  }, [authPhase, driver?.id, config.locationPushSeconds]);

  // ── WebSocket listeners
  React.useEffect(() => {
    if (authPhase !== 'app') return;
    const onStatus = (e) => setWsStatus(e.detail);
    const onNew = (e) => {
      const o = e.detail;
      // Add to pool list
      setPool((prev) => {
        if (prev.find((x) => x.id === o.id)) return prev;
        return [o, ...prev];
      });
      // If we're online and idle on home → fire the incoming alert
      setFlow((curFlow) => {
        if (curFlow === 'idle' && tab === 'home' && online) {
          setOrderId(o.id);
          return 'incoming';
        }
        return curFlow;
      });
    };
    const onUpdate = (e) => {
      const o = e.detail;
      setPool((prev) => {
        // If it's still PENDING update in place; if status changed, remove.
        if (o.status === 'PENDING') {
          const i = prev.findIndex((x) => x.id === o.id);
          if (i >= 0) { const copy = [...prev]; copy[i] = o; return copy; }
          return [o, ...prev];
        }
        return prev.filter((x) => x.id !== o.id);
      });
      // If this is my active order, mirror its state
      setActiveOrder((cur) => (cur && cur.id === o.id ? o : cur));
    };
    window.addEventListener('resdri:ws-status', onStatus);
    window.addEventListener('resdri:order-new', onNew);
    window.addEventListener('resdri:order-update', onUpdate);
    return () => {
      window.removeEventListener('resdri:ws-status', onStatus);
      window.removeEventListener('resdri:order-new', onNew);
      window.removeEventListener('resdri:order-update', onUpdate);
    };
  }, [authPhase, tab, online]);

  // Expose for the verifier / dev tools
  React.useEffect(() => {
    window.__appSetFlow = setFlow;
    window.__appSetTab = setTab;
  }, []);

  // ── Handlers
  const refreshPool = async () => {
    try {
      const p = await window.api.pool();
      setPool(p || []);
    } catch (e) {
      console.warn('refreshPool', e);
    }
  };

  const openOrder = async (id) => {
    try {
      const o = await window.api.order(id);
      if (!o) return;
      setOrderId(id);
      // If this is the current driver's active order already, go to its flow.
      if (o.driverId === driver?.id && o.status !== 'PENDING' && o.status !== 'CANCELLED') {
        setActiveOrder(o);
        setFlow(STATUS_TO_FLOW[o.status] || 'idle');
      } else {
        setFlow('detail');
      }
    } catch (e) {
      alert(e.message);
    }
  };

  // accept(id?) — id can be passed explicitly (when accepting from the home/orders list)
  // or fall back to the currently selected orderId state (when accepting from detail).
  const accept = async (id) => {
    const targetId = id || orderId;
    if (!targetId) return;
    if (id) setOrderId(id);
    try {
      const o = await window.api.accept(targetId);
      setActiveOrder(o);
      try { await window.api.enRoutePickup(o.id); } catch {}
      setFlow('toPickup');
    } catch (e) {
      alert(e.message);
      setFlow('idle');
      await refreshPool();
    }
  };

  const skip = () => { setFlow('idle'); setOrderId(null); };

  const arrivedAtPickup = async () => {
    if (!activeOrder) return setFlow('atPickup');
    try {
      const o = await window.api.arrivedPickup(activeOrder.id);
      setActiveOrder(o);
      setFlow('atPickup');
    } catch (e) { alert(e.message); }
  };

  const confirmedPickup = async () => {
    if (!activeOrder) return setFlow('toDropoff');
    try {
      const o1 = await window.api.pickedUp(activeOrder.id);
      const o2 = await window.api.enRouteDropoff(activeOrder.id);
      setActiveOrder(o2 || o1);
      setFlow('toDropoff');
    } catch (e) { alert(e.message); }
  };

  const arrivedAtDropoff = async () => {
    if (!activeOrder) return setFlow('cash');
    try {
      const o = await window.api.arrivedDropoff(activeOrder.id);
      setActiveOrder(o);
      setFlow('cash');
    } catch (e) { alert(e.message); }
  };

  const collectedCash = async () => {
    if (!activeOrder) return setFlow('completed');
    try {
      // delivered → complete with cash amount from order
      await window.api.delivered(activeOrder.id);
      const completed = await window.api.complete(activeOrder.id, activeOrder.cash);
      setActiveOrder(completed);
      // refresh driver wallet
      try { const d = await window.api.driverMe(); setDriver(d); } catch {}
      setFlow('completed');
    } catch (e) {
      alert(e.message);
    }
  };

  const backToHome = async () => {
    setFlow('idle'); setTab('home'); setOrderId(null); setActiveOrder(null);
    await refreshPool();
  };

  // Called as setOnline(true|false) from the lever; mirrors backend state.
  const setOnlineApi = async (next) => {
    setOnline(next); // optimistic
    try { await window.api.setOnline(next); } catch (e) {
      setOnline(!next);
      alert(e.message);
    }
  };

  const logout = () => {
    window.api.setToken(null);
    window.resdriWs.disconnect();
    setAuthPhase('login');
    setDriver(null);
    setPool([]); setActiveOrder(null); setOrderId(null);
    setFlow('idle'); setTab('home');
  };

  // ── Rendering paths
  // 1) Loading — black device, dot spinner
  // 2) Login — phone+OTP
  // 3) App — the actual driver UI
  if (authPhase === 'loading') {
    return <DeviceShell t={t} fullScreen={fullScreen}><BootSpinner /></DeviceShell>;
  }
  if (authPhase === 'login') {
    return (
      <DeviceShell t={t} fullScreen={fullScreen}>
        <LoginScreen onAuthed={async () => { await enterApp(); }} />
        {bootError && <BootError text={bootError} />}
        <InstallHint />
      </DeviceShell>
    );
  }

  // ── App body
  const order = activeOrder || (orderId ? pool.find((o) => o.id === orderId) : null) || pool[0];
  const TAB_BAR_HEIGHT = 72;
  const isIdle = flow === 'idle';

  let tabBody = null;
  if (isIdle) {
    if (tab === 'home') {
      const driverLatLng = driverLocation || (driver?.currentLat && driver?.currentLng
                          ? { lat: driver.currentLat, lng: driver.currentLng } : null);
      tabBody = <HomeScreen
        online={online} setOnline={setOnlineApi}
        homeState={online ? (pool.length > 0 ? 'online' : 'empty') : 'offline'}
        openOrder={openOrder}
        acceptOrder={(id) => accept(id)}
        today={Number(driver?.cashHeld) || 0}
        tabBarHeight={TAB_BAR_HEIGHT}
        orders={pool}
        driverName={driver?.fullName || ''}
        driverLatLng={driverLatLng}
        radiusKm={radiusKm}
        setRadiusKm={setRadiusKm}
        config={config}
      />;
    } else if (tab === 'orders')   tabBody = <OrdersScreen
        online={online}
        today={Number(driver?.cashHeld) || 0}
        openOrder={openOrder}
        acceptOrder={(id) => accept(id)}
        radiusKm={radiusKm}
        orders={pool}
        driverName={driver?.fullName}
        config={config}
      />;
    else if (tab === 'earnings')   tabBody = <EarningsScreen online={online} driverName={driver?.fullName} />;
    else if (tab === 'wallet')     tabBody = <WalletScreen online={online} driver={driver} driverName={driver?.fullName} />;
    else if (tab === 'profile')    tabBody = <ProfileScreen online={online} driver={driver} driverName={driver?.fullName} onLogout={logout} />;
  }

  let flowBody = null;
  switch (flow) {
    case 'incoming':
      flowBody = order && <IncomingAlert order={order} primarySide={t.alertPlacement}
        onAccept={accept} onSkip={skip} />;
      break;
    case 'detail':
      flowBody = order && <OrderDetail order={order}
        onAccept={accept} onSkip={skip} onBack={backToHome} />;
      break;
    case 'toPickup':
      flowBody = order && <ToPickup order={order} onArrived={arrivedAtPickup} onBack={backToHome} />;
      break;
    case 'atPickup':
      flowBody = order && <AtPickup order={order} onPicked={confirmedPickup} />;
      break;
    case 'toDropoff':
      flowBody = order && <ToDropoff order={order} onArrived={arrivedAtDropoff} />;
      break;
    case 'cash':
      flowBody = order && <CashCollection order={order} onCollected={collectedCash} onBack={() => setFlow('toDropoff')} />;
      break;
    case 'completed':
      flowBody = order && <Completed order={order} totalToday={Number(driver?.cashHeld) || 0} onClose={backToHome} />;
      break;
  }

  // ── Inner content: tab body or flow body, plus the tab bar on idle screens.
  const inner = (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--bg)' }}>
      {isIdle ? tabBody : flowBody}
      {isIdle && <TabBar active={tab} onChange={setTab} />}
    </div>
  );

  if (fullScreen) {
    return (
      <div dir="rtl" lang="ar" style={{
        position: 'fixed', inset: 0, background: 'var(--bg)', overflow: 'hidden',
      }}>
        {inner}
        <InstallHint />
      </div>
    );
  }

  return (
    <div dir="rtl" lang="ar" style={{
      width: '100%', minHeight: '100vh',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: 24, gap: 18,
    }}>
      <div data-screen-label={screenLabel(flow, tab)} style={{ position: 'relative' }}>
        <IOSDevice width={402} height={874} dark={t.dark}>
          {inner}
        </IOSDevice>

        {window.RESDRI_DEV && (
          <div style={{
            position: 'absolute', top: '100%', left: 0, right: 0,
            marginTop: 14, textAlign: 'center',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          }}>
            <StepLabel flow={flow} tab={tab} />
            <WsBadge status={wsStatus} pool={pool.length} />
          </div>
        )}
      </div>

      {/* Tweaks — only in dev mode */}
      {window.RESDRI_DEV && <TweaksPanel title="Tweaks">
        <TweakSection label="المظهر · Theme" />
        <TweakToggle  label="Dark mode" value={t.dark} onChange={(v) => setTweak('dark', v)} />
        <TweakColor   label="Palette"
                      value={t.palette}
                      options={[
                        ['#0F766E', '#F59E0B', '#0F172A'],
                        ['#EA580C', '#0EA5E9', '#0F172A'],
                        ['#4338CA', '#F59E0B', '#0F172A'],
                        ['#18181B', '#F59E0B', '#475569'],
                      ]}
                      onChange={(v) => {
                        const lookup = { '#0F766E': 'teal', '#EA580C': 'orange', '#4338CA': 'indigo', '#18181B': 'mono' };
                        const first = Array.isArray(v) ? v[0] : v;
                        setTweak('palette', lookup[first] || 'teal');
                      }}
        />

        <TweakSection label="الخط · Typography" />
        <TweakRadio label="Font" value={t.font} options={['Cairo','Tajawal','IBM Plex Sans Arabic']}
                    onChange={(v) => setTweak('font', v)} />

        <TweakSection label="الكثافة · Density" />
        <TweakRadio label="Spacing" value={t.density} options={['comfortable', 'compact']}
                    onChange={(v) => setTweak('density', v)} />

        <TweakSection label="التنبيه · Incoming alert" />
        <TweakRadio label="Primary side (RTL)" value={t.alertPlacement} options={['right', 'left']}
                    onChange={(v) => setTweak('alertPlacement', v)} />

        <TweakSection label="التنقل · Jump" />
        <TweakSelect label="Tab" value={tab}
                     options={['home','orders','earnings','wallet','profile']}
                     onChange={(v) => { setTab(v); setFlow('idle'); }} />
        <TweakButton label="تسجيل الخروج" onClick={logout} />
      </TweaksPanel>}

      {/* In production we still need a logout path. Tiny floating ghost button
          in the bottom corner of the desktop wrapper (hidden in fullscreen/PWA mode
          where Profile tab → logout button is the path). */}
      {!window.RESDRI_DEV && !fullScreen && (
        <button onClick={logout} className="tap" style={{
          position: 'fixed', bottom: 12, insetInlineStart: 12, zIndex: 100,
          height: 32, padding: '0 12px', borderRadius: 999,
          background: 'rgba(0,0,0,0.4)', color: '#fff', border: 'none',
          fontFamily: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer',
          opacity: 0.5,
        }}>تسجيل الخروج</button>
      )}
    </div>
  );
}

// ── Visual atoms ──────────────────────────────────────────────────

function DeviceShell({ children, t, fullScreen }) {
  if (fullScreen) {
    return (
      <div dir="rtl" lang="ar" style={{
        position: 'fixed', inset: 0, background: 'var(--bg)', overflow: 'hidden',
      }}>
        {children}
      </div>
    );
  }
  return (
    <div dir="rtl" lang="ar" style={{
      width: '100%', minHeight: '100vh',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: 24,
    }}>
      <IOSDevice width={402} height={874} dark={t.dark}>
        <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--bg)' }}>
          {children}
        </div>
      </IOSDevice>
    </div>
  );
}

function computeFullScreen() {
  const standalone =
    (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) ||
    window.navigator.standalone === true;
  const smallViewport = window.innerWidth < 500;
  return standalone || smallViewport;
}

function BootSpinner() {
  return (
    <div style={{
      position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <div style={{ display: 'flex', gap: 6 }}>
        {[0, 0.15, 0.3].map((d) => (
          <span key={d} style={{
            width: 10, height: 10, borderRadius: 999, background: 'var(--primary)',
            animation: `radar-soft 1.4s ease-in-out ${d}s infinite`,
          }} />
        ))}
      </div>
    </div>
  );
}

function BootError({ text }) {
  return (
    <div style={{
      position: 'absolute', bottom: 80, left: 16, right: 16, zIndex: 100,
      background: 'color-mix(in oklch, var(--danger) 14%, transparent)',
      border: '1px solid color-mix(in oklch, var(--danger) 32%, transparent)',
      color: 'var(--danger)',
      borderRadius: 12, padding: 12,
      fontSize: 13, fontWeight: 700, textAlign: 'center',
    }}>{text}</div>
  );
}

function WsBadge({ status, pool }) {
  const c = status === 'connected' ? '#22C55E' : status === 'error' ? '#EF4444' : '#94A3B8';
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      background: 'rgba(255,255,255,0.06)', backdropFilter: 'blur(10px)',
      border: '1px solid rgba(255,255,255,0.10)', color: '#E2E8F0',
      padding: '6px 10px', borderRadius: 999,
      fontFamily: 'system-ui, sans-serif', fontSize: 12, fontWeight: 700,
    }}>
      <span style={{ width: 8, height: 8, borderRadius: 999, background: c }} />
      <span style={{ opacity: 0.7 }}>WS</span>
      <span style={{ opacity: 0.4 }}>·</span>
      <span>{pool} في المسبح</span>
    </div>
  );
}

function screenLabel(flow, tab) {
  const flowMap = {
    incoming: '02 Incoming order (hero)',
    detail: '03 Order detail',
    toPickup: '04 Heading to pickup',
    atPickup: '05 At pickup',
    toDropoff: '06 Heading to dropoff',
    cash: '07 Cash collection (hero)',
    completed: '08 Completed',
  };
  if (flow !== 'idle') return flowMap[flow] || flow;
  return ({ home: '01 Home', orders: '01b Orders', earnings: '01c Earnings', wallet: '01d Wallet', profile: '01e Profile' }[tab] || tab);
}

function StepLabel({ flow, tab }) {
  const flowMap = {
    idle: ({ home: 'Home', orders: 'Orders', earnings: 'Earnings', wallet: 'Wallet', profile: 'Profile' })[tab],
    incoming: 'Incoming alert',
    detail: 'Order detail',
    toPickup: 'Heading to pickup',
    atPickup: 'At pickup',
    toDropoff: 'Heading to dropoff',
    cash: 'Cash collection',
    completed: 'Completed',
  };
  const isIdle = flow === 'idle';
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      background: 'rgba(255,255,255,0.06)', backdropFilter: 'blur(10px)',
      border: '1px solid rgba(255,255,255,0.10)', color: '#E2E8F0',
      padding: '6px 10px', borderRadius: 999,
      fontFamily: 'system-ui, sans-serif', fontSize: 12, fontWeight: 700,
    }}>
      <span style={{ opacity: 0.6 }}>{isIdle ? 'IDLE · TAB' : 'ACTIVE · FLOW'}</span>
      <span style={{ opacity: 0.3 }}>·</span>
      <span>{flowMap[flow]}</span>
    </div>
  );
}

Object.assign(window, { App });

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
