/* GarageWiz — App root: routing, state, context */
(function () {
  const e = React.createElement;
  const { useState, useEffect, useCallback } = React;
  const store = window.gwStore;

  const CONFIG_VERSION = 2; // bump to invalidate stale saved builds
  const DEFAULT_CONFIG = {
    _v: CONFIG_VERSION,
    width: null, heightId: null, panel: 'short', modelCode: '14',
    windowsOn: false, glassId: 'plain', insertsOn: false,
    openerMode: 'none', openerTier: 'Better', openerId: null,
    addons: {}, qty: 1, discount: 0, couponCode: null, quoteDate: null, signature: null, customer: { name: '', phone: '', email: '' },
  };

  function loadConfig() {
    const saved = store.get('config', null);
    if (!saved || saved._v !== CONFIG_VERSION) return Object.assign({}, DEFAULT_CONFIG);
    return Object.assign({}, DEFAULT_CONFIG, saved);
  }

  function parseHash() {
    let h = (location.hash || '').replace(/^#\/?/, '');
    const parts = h.split('/').filter(Boolean);
    return { section: parts[0] || 'dashboard', sub: parts[1] || null, raw: h };
  }

  function App() {
    const [route, setRoute] = useState(parseHash());
    // with a real backend, the Supabase session is the source of truth (not localStorage)
    const [user, setUser] = useState(() => (window.GW.db && window.GW.db.configured) ? null : store.get('user', null));
    const [settings, setSettingsState] = useState(() => Object.assign({}, GW.defaultSettings, store.get('settings', {})));
    const [config, setConfigState] = useState(loadConfig);
    const [disabledMfrs, setDisabledMfrsState] = useState(() => store.get('disabledMfrs', {}));
    const [estimate, setEstimateState] = useState(() => store.get('estimate', null));
    const [dealer, setDealer] = useState(null);
    const [dealerUnlocked, setDealerUnlocked] = useState(false);
    const [version, setVersion] = useState(0);
    const [toastMsg, setToastMsg] = useState(null);

    useEffect(() => {
      const on = () => setRoute(parseHash());
      window.addEventListener('hashchange', on);
      return () => window.removeEventListener('hashchange', on);
    }, []);

    // persist
    useEffect(() => {
      store.set('settings', settings);
      if (window.GW.db && GW.db.configured && user && user.role === 'admin') {
        clearTimeout(window.__gwSettingsT);
        window.__gwSettingsT = setTimeout(() => { try { GW.db.saveSettings(settings); } catch (e) {} }, 700);
      }
    }, [settings]);
    useEffect(() => { store.set('config', config); }, [config]);
    useEffect(() => { store.set('disabledMfrs', disabledMfrs); }, [disabledMfrs]);
    useEffect(() => { store.set('estimate', estimate); }, [estimate]);

    // ---- real auth: restore Supabase session, react to sign in/out, and sync
    //      the shared catalog + settings from the backend ----
    useEffect(() => {
      if (!(window.GW.db && GW.db.configured)) return;
      let live = true;
      const apply = async () => {
        const prof = await GW.db.profile();
        if (!live) return;
        if (!prof) { setUser(null); return; }
        setUser({ id: prof.id, name: prof.name, email: prof.email, role: prof.role, active: prof.active });
        try {
          const cat = await GW.db.loadCatalog();
          if (cat && Object.keys(cat).length) GW.applyCatalog(cat);
          else if (prof.role === 'admin') await GW.db.publishCatalog(GW.collectCatalog(), settings); // first admin seeds it
          const bs = await GW.db.loadSettings();
          if (live && bs && Object.keys(bs).length) setSettingsState(prev => Object.assign({}, prev, bs));
          if (prof.role === 'admin') { try { const ps = await GW.db.listProfiles(); if (ps && ps.length) GW.users = ps; } catch (e) {} }
          if (live) setVersion(v => v + 1);
        } catch (e) { /* fall back to the local cache */ }
      };
      apply();
      GW.db.onAuth(() => apply());
      return () => { live = false; };
    }, []);

    const go = useCallback((hash) => { if (location.hash === hash) setRoute(parseHash()); else location.hash = hash; }, []);
    const resetConfig = useCallback(() => { setConfigState(Object.assign({}, DEFAULT_CONFIG)); }, []);
    const login = useCallback((u) => { setUser(u); store.set('user', u); go('#/dashboard'); }, [go]);
    const logout = useCallback(() => {
      if (window.GW.db && GW.db.configured) { try { GW.db.signOut(); } catch (e) {} }
      setUser(null); store.set('user', null); setDealerUnlocked(false); go('#/login');
    }, [go]);
    const toast = useCallback((m) => { setToastMsg(m); setTimeout(() => setToastMsg(t => t === m ? null : t), 2600); }, []);

    // ---- estimate (cart) actions — quick pricing is unaffected; an estimate
    // only exists once a rep explicitly adds something to it ----
    const addToEstimate = useCallback((item) => {
      setEstimateState((est) => {
        const base = est || GW.newEstimate();
        const next = Object.assign({}, base, { items: base.items.concat([item]) });
        if (!next.quoteDate) next.quoteDate = new Date().toISOString();
        return next;
      });
    }, []);
    const updateEstimate = useCallback((patch) => { setEstimateState((est) => Object.assign({}, est || GW.newEstimate(), patch)); }, []);
    const removeEstimateItem = useCallback((idx) => { setEstimateState((est) => est ? Object.assign({}, est, { items: est.items.filter((_, i) => i !== idx) }) : est); }, []);
    const clearEstimate = useCallback(() => setEstimateState(null), []);

    const ctx = {
      route, user, settings, config, disabledMfrs, dealer, dealerUnlocked, version,
      estimate,
      go, login, logout, resetConfig,
      setConfig: setConfigState,
      setSettings: setSettingsState,
      setDisabledMfrs: setDisabledMfrsState,
      setEstimate: setEstimateState,
      addToEstimate, updateEstimate, removeEstimateItem, clearEstimate,
      // Dealer View is admin-only, and re-locks every time it closes so it
      // always asks for the PIN again (no accidental opens in front of a customer)
      openDealer: () => { if (user && user.role === 'admin') setDealer(true); },
      closeDealer: () => { setDealer(false); setDealerUnlocked(false); },
      setDealerUnlocked,
      bump: () => setVersion(v => v + 1),
      toast,
    };

    // ---- routing guard ----
    const needsAuth = route.section !== 'login';
    if (!user && needsAuth) {
      return e(window.AppCtx.Provider, { value: ctx }, e(window.LoginScreen));
    }
    if (user && route.section === 'login') { go('#/dashboard'); }

    let screen;
    switch (route.section) {
      case 'login': screen = e(window.LoginScreen); break;
      case 'dashboard': screen = e(window.Hub); break;          // the tools hub
      case 'tools': {
        const toolId = route.sub;
        if (toolId === 'pricing' && GW.canOpenTool(user, 'pricing')) { screen = e(window.Dashboard); break; }
        // not permitted, coming-soon, or unknown tool → back to the hub
        go('#/dashboard'); screen = e(window.Hub); break;
      }
      case 'doors': screen = e(window.Configurator); break;
      case 'openers': screen = e(window.Openers); break;
      case 'parts': screen = e(window.Parts); break;
      case 'quote': screen = e(window.QuoteSummary); break;
      case 'estimate': screen = e(window.EstimateScreen); break;
      case 'estimates': screen = e(window.MyEstimates); break;
      case 'review': screen = e(window.ReviewScreen); break;
      case 'admin': {  // SYSTEM admin — whole suite
        if (!user || user.role !== 'admin') { go('#/dashboard'); screen = e(window.Hub); break; }
        const map = {
          users: window.AdminUsers, permissions: window.AdminPermissions,
          tools: window.AdminTools, backend: window.AdminBackend, activity: window.AdminActivity,
        };
        const Comp = map[route.sub] || window.AdminUsers;
        screen = e(window.AdminLayout, null, e(Comp));
        break;
      }
      case 'pricingadmin': {  // PRICING TOOL admin — lives inside the pricing tool
        if (!user || user.role !== 'admin') { go('#/dashboard'); screen = e(window.Hub); break; }
        if (!GW.canOpenTool(user, 'pricing')) { go('#/dashboard'); screen = e(window.Hub); break; }
        const map = {
          settings: window.AdminSettings, doors: window.AdminDoors, openers: window.AdminOpeners,
          manufacturers: window.AdminManufacturers, suppliers: window.AdminSuppliers,
          parts: window.AdminParts, upgrades: window.AdminUpgrades, bundles: window.AdminBundles, coupons: window.AdminCoupons, reviews: window.AdminReviews,
        };
        const Comp = map[route.sub] || window.AdminSettings;
        screen = e(window.AdminLayout, null, e(Comp));
        break;
      }
      default: screen = e(window.Hub);
    }

    const isConfigurator = route.section === 'doors';

    return e(window.AppCtx.Provider, { value: ctx },
      e('div', { style: { minHeight: '100vh', display: 'flex', flexDirection: 'column' } },
        e(window.TopBar),
        e('div', { className: isConfigurator ? 'has-configurator' : '', style: { flex: 1, display: 'flex', flexDirection: 'column' } }, screen),
      ),
      e(window.DealerPanel),
      // toast
      e('div', {
        style: {
          position: 'fixed', bottom: toastMsg ? 26 : -80, left: '50%', transform: 'translateX(-50%)',
          background: 'var(--ink)', color: '#fff', padding: '13px 22px', borderRadius: 999, zIndex: 300,
          boxShadow: 'var(--sh-pop)', fontWeight: 600, fontSize: 14.5, transition: 'bottom .3s cubic-bezier(.2,.8,.2,1)',
          display: 'flex', alignItems: 'center', gap: 10, maxWidth: '90vw',
        },
      }, toastMsg ? e(window.Icon, { name: 'check', size: 18, strokeWidth: 3, style: { color: 'var(--yellow)' } }) : null, toastMsg || ''),
    );
  }

  ReactDOM.createRoot(document.getElementById('root')).render(e(App));
})();
