/* ===== store.jsx — global state ===== */
(function () {
  const StoreCtx = React.createContext(null);

  function useStore() {
    return React.useContext(StoreCtx);
  }

  // Detect actual viewport on first render
  function detectDevice() {
    if (typeof window === 'undefined') return 'desktop';
    return window.innerWidth < 768 ? 'mobile' : 'desktop';
  }

  // ============================================================
  // Field adapters: API camelCase ↔ frontend snake_case
  // Hoisted so loadAll() and other helpers can use them
  // ============================================================
  const TO_API = {
    suppliers: (r) => ({
      name: r.name, short: r.short, country: r.country, city: r.city, address: r.address,
      contactName: r.contact, contactEmail: r.email, contactPhone: r.phone,
      vat: r.vat, paymentTerms: r.payment_terms, tags: r.tags || [], notes: r.notes,
    }),
    customers: (r) => ({
      name: r.name, short: r.short, type: r.type,
      country: r.country, city: r.city, address: r.address,
      contactName: r.contact, contactEmail: r.email, contactPhone: r.phone,
      vat: r.vat, tags: r.tags || [], notes: r.notes,
    }),
    reagent_products: (r) => ({
      supplierId: r.supplier_id, brand: r.brand, name: r.name,
      code: r.code, catNo: r.cat_no,
      testsPerBox: Number(r.tests_per_box) || 1, unit: r.unit || 'kit',
      storageTemp: r.storage,
      defaultLowThresholdBoxes: Number(r.low_threshold) || null,
      tags: r.tags || [], notes: r.notes,
    }),
    reagent_batches: (r) => ({
      productId: r.product_id, supplierId: r.supplier_id, lot: r.lot,
      prodDate: r.prod_date, expiry: r.expiry,
      receivedDate: r.received,
      receivedBoxes: Number(r.received_boxes) || 0,
      location: r.location, info: r.info, notes: r.notes,
    }),
    device_products: (r) => ({
      supplierId: r.supplier_id, brand: r.brand,
      name: r.name, model: r.model, category: r.category,
    }),
    device_units: (r) => ({
      productId: r.product_id, serialNumber: r.sn || null,
      lot: r.lot, prodDate: r.prod_date,
      purchasedDate: r.purchased, warrantyUntil: r.warranty_until,
    }),
  };

  const FROM_API = {
    suppliers: (r) => ({
      id: r.id, name: r.name, short: r.short || r.name,
      country: r.country, city: r.city,
      contact: r.contactName, email: r.contactEmail, phone: r.contactPhone,
      vat: r.vat, payment_terms: r.paymentTerms,
      tags: r.tags || [], notes: r.notes,
      archived: r.status === 'archived',
      created: (r.createdAt || '').slice(0, 10),
    }),
    customers: (r) => ({
      id: r.id, name: r.name, short: r.short || r.name, type: r.type,
      country: r.country, city: r.city,
      contact: r.contactName, email: r.contactEmail, phone: r.contactPhone,
      vat: r.vat, tags: r.tags || [], notes: r.notes,
      archived: r.status === 'archived',
      created: (r.createdAt || '').slice(0, 10),
    }),
    reagent_products: (r) => ({
      id: r.id, supplier_id: r.supplierId, brand: r.brand,
      name: r.name, code: r.code, cat_no: r.catNo,
      tests_per_box: r.testsPerBox, unit: r.unit,
      storage: r.storageTemp,
      low_threshold: r.defaultLowThresholdBoxes || 0,
      tags: r.tags || [],
    }),
    reagent_batches: (r) => ({
      id: r.id, product_id: r.productId, supplier_id: r.supplierId,
      lot: r.lot, prod_date: r.prodDate, expiry: r.expiry,
      received: r.receivedDate,
      received_boxes: r.receivedBoxes,
      on_hand_boxes: r.onHandBoxes,
      on_hand_atoms: r.onHandAtoms,
      location: r.location, info: r.info,
      last_movement: r.lastMovement,
    }),
    device_products: (r) => ({
      id: r.id, supplier_id: r.supplierId, brand: r.brand,
      name: r.name, model: r.model, category: r.category,
    }),
    device_units: (r) => ({
      id: r.id, product_id: r.productId,
      sn: r.serialNumber || null,
      has_sn: !!r.serialNumber,
      lot: r.lot, prod_date: r.prodDate,
      purchased: r.purchasedDate, warranty_until: r.warrantyUntil,
      status: r.unitStatus || 'IN_STOCK',
      cur_holder:
        r.curHolderType === 'customer' ? { kind: 'customer', id: r.curHolderCustomerId } :
        r.curHolderType === 'supplier' ? { kind: 'supplier', id: r.curHolderSupplierId } :
        { kind: 'fb', id: null },
      fault_note: r.faultNote, sent_to_repair_at: r.sentToRepairAt,
    }),
  };

  const API_NAMES = {
    suppliers: 'suppliers',
    customers: 'customers',
    reagent_products: 'reagentProducts',
    reagent_batches: 'reagentBatches',
    device_products: 'deviceProducts',
    device_units: 'deviceUnits',
  };

  function StoreProvider({ children }) {
    const [state, setState] = React.useState(() => ({
      ...window.SEED,
      dismissedAlarms: new Set(),
      // UI
      device: detectDevice(),
      autoDevice: true,
      sidebarCollapsed: false,
      cmdkOpen: false,
      toasts: [],
      // ---- Auth + loading ----
      currentUser: null,            // populated after /auth/me succeeds
      currentUserId: null,
      authChecked: false,           // becomes true after first /auth/me call
      dataLoaded: false,            // becomes true after initial bulk load
      loadError: null,
    }));

    const set = (patch) => setState((s) => ({ ...s, ...(typeof patch === 'function' ? patch(s) : patch) }));

    // Track real viewport size, but only auto-switch if user hasn't pinned device manually
    React.useEffect(() => {
      const onResize = () => {
        setState((s) => {
          if (!s.autoDevice) return s;
          const want = window.innerWidth < 768 ? 'mobile' : 'desktop';
          return s.device === want ? s : { ...s, device: want };
        });
      };
      window.addEventListener('resize', onResize);
      window.addEventListener('orientationchange', onResize);
      return () => {
        window.removeEventListener('resize', onResize);
        window.removeEventListener('orientationchange', onResize);
      };
    }, []);

    // ============================================================
    // Auth bootstrap + data load
    // ============================================================
    const loadAll = React.useCallback(async () => {
      try {
        const api = window.api;
        const [sups, custs, rp, rb, dp, du, act] = await Promise.all([
          api.suppliers.list().catch(() => ({ rows: [] })),
          api.customers.list().catch(() => ({ rows: [] })),
          api.reagentProducts.list().catch(() => ({ rows: [] })),
          api.reagentBatches.list().catch(() => ({ rows: [] })),
          api.deviceProducts.list().catch(() => ({ rows: [] })),
          api.deviceUnits.list().catch(() => ({ rows: [] })),
          api.activity(30).catch(() => ({ rows: [] })),
        ]);
        set({
          suppliers:        (sups.rows  || []).map(FROM_API.suppliers),
          customers:        (custs.rows || []).map(FROM_API.customers),
          reagent_products: (rp.rows    || []).map(FROM_API.reagent_products),
          reagent_batches:  (rb.rows    || []).map(FROM_API.reagent_batches),
          device_products:  (dp.rows    || []).map(FROM_API.device_products),
          device_units:     (du.rows    || []).map(FROM_API.device_units),
          activity:         (act.rows   || []).map((a) => ({ ...a, actor: a.actorId, text: a.text })),
          dataLoaded: true,
        });
      } catch (e) {
        console.error('loadAll failed', e);
        set({ loadError: String(e), dataLoaded: true });
      }
    }, []);

    // On mount: check session and load data if logged in
    React.useEffect(() => {
      let cancelled = false;
      (async () => {
        try {
          const r = await window.api.auth.me();
          if (cancelled) return;
          if (r && r.user) {
            set({
              currentUser: r.user,
              currentUserId: r.user.id,
              authChecked: true,
            });
            loadAll();
          } else {
            set({ authChecked: true });
          }
        } catch (e) {
          if (!cancelled) set({ authChecked: true });
        }
      })();
      return () => { cancelled = true; };
    }, [loadAll]);

    // Login / Logout helpers (called by LoginPage)
    const login = async (email, password) => {
      const r = await window.api.auth.login(email, password);
      if (r && r.user) {
        set({
          currentUser: r.user,
          currentUserId: r.user.id,
          authChecked: true,
        });
        loadAll();
      }
      return r;
    };

    const logout = async () => {
      try { await window.api.auth.logout(); } catch {}
      set({
        currentUser: null,
        currentUserId: null,
        authChecked: true,
        dataLoaded: false,
        suppliers: [], customers: [],
        reagent_products: [], reagent_batches: [], reagent_outflows: [],
        device_products: [], device_units: [], device_transfers: [],
        activity: [],
      });
      if (window.location.pathname !== '/login') navigate('/login');
    };

    // toast helper
    const toast = (text, kind = 'success') => {
      const id = 't-' + Math.random().toString(36).slice(2, 7);
      set((s) => ({ toasts: [...s.toasts, { id, text, kind }] }));
      setTimeout(() => set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) })), 3200);
    };

    // CRUD wired to API
    const add = async (key, row) => {
      const apiName = API_NAMES[key];
      if (!apiName || !window.api?.[apiName]) {
        set((s) => ({ [key]: [row, ...s[key]] }));
        return row;
      }
      const body = (TO_API[key] || ((r) => r))(row);
      try {
        const r = await window.api[apiName].create(body);
        const newRow = { ...row, id: r.id || row.id };
        set((s) => ({ [key]: [newRow, ...s[key]] }));
        return newRow;
      } catch (e) {
        toast('保存失败：' + (e.message || e), 'error');
        throw e;
      }
    };

    const update = async (key, id, patch) => {
      const apiName = API_NAMES[key];
      if (apiName && window.api?.[apiName]) {
        try {
          if (patch.archived === true && window.api[apiName].archive) {
            await window.api[apiName].archive(id);
          } else if (patch.archived === false && window.api[apiName].restore) {
            await window.api[apiName].restore(id);
          } else if (window.api[apiName].update) {
            const body = (TO_API[key] || ((r) => r))(patch);
            await window.api[apiName].update(id, body);
          }
        } catch (e) {
          toast('更新失败：' + (e.message || e), 'error');
          throw e;
        }
      }
      set((s) => ({ [key]: s[key].map((r) => (r.id === id ? { ...r, ...patch } : r)) }));
    };

    const remove = async (key, id) => {
      // Soft delete = archive
      await update(key, id, { archived: true });
    };

    // ============================================================
    // Complex flows that touch multiple tables on the server
    // ============================================================
    const submitOutflow = async ({ batchId, customerId, takenAt, outUnit, outQty, dueAt, note }) => {
      try {
        const r = await window.api.reagentOutflows.create({
          batchId, customerId, takenAt, outUnit, outQty, dueAt, note,
        });
        // Reload batches (stock changed) + activity
        const [bl, act] = await Promise.all([
          window.api.reagentBatches.list().catch(() => ({ rows: [] })),
          window.api.activity(30).catch(() => ({ rows: [] })),
        ]);
        set({
          reagent_batches: (bl.rows || []).map(FROM_API.reagent_batches),
          activity: (act.rows || []).map((a) => ({ ...a, actor: a.actorId })),
        });
        return r;
      } catch (e) {
        toast('出库失败：' + (e.message || e), 'error');
        throw e;
      }
    };

    const submitReturn = async ({ batchId, returnDate, returnUnit, returnQty, reason, note }) => {
      try {
        const r = await window.api.reagentReturns.create({
          batchId, returnDate, returnUnit, returnQty, reason, note,
        });
        const bl = await window.api.reagentBatches.list().catch(() => ({ rows: [] }));
        set({ reagent_batches: (bl.rows || []).map(FROM_API.reagent_batches) });
        return r;
      } catch (e) {
        toast('退还失败：' + (e.message || e), 'error');
        throw e;
      }
    };

    const submitTransfer = async ({
      unitId, transferDate,
      fromType, fromCustomerId, fromSupplierId,
      toType, toCustomerId, toSupplierId,
      reason, deviceStatusAfter,
      shippingDoc, deliveryDoc, customsClearance, carrier, commenti, note,
    }) => {
      try {
        const r = await window.api.deviceTransfers.create({
          unitId, transferDate,
          fromType, fromCustomerId, fromSupplierId,
          toType, toCustomerId, toSupplierId,
          reason, deviceStatusAfter,
          shippingDoc, deliveryDoc, customsClearance, carrier, commenti, note,
        });
        const ul = await window.api.deviceUnits.list().catch(() => ({ rows: [] }));
        set({ device_units: (ul.rows || []).map(FROM_API.device_units) });
        return r;
      } catch (e) {
        toast('转移失败：' + (e.message || e), 'error');
        throw e;
      }
    };

    // Lookups
    const by = (key, id) => state[key]?.find((r) => r.id === id) || null;

    const dismissAlarm = (key) =>
      set((s) => ({ dismissedAlarms: new Set([...s.dismissedAlarms, key]) }));

    // Activity logging
    const logActivity = (entry) =>
      set((s) => ({
        activity: [{ id: 'a-' + Math.random().toString(36).slice(2, 7), ts: new Date().toISOString().slice(0, 19), actor: s.currentUserId, ...entry }, ...s.activity],
      }));

    // Global search
    const search = (q) => {
      if (!q) return [];
      const ql = q.toLowerCase();
      const hit = (s) => s && s.toLowerCase().includes(ql);
      const results = [];
      for (const p of state.reagent_products) {
        if (hit(p.name) || hit(p.code) || hit(p.cat_no)) {
          results.push({ kind: 'reagent_product', id: p.id, title: p.name, sub: `${p.code} · ${state.suppliers.find(x=>x.id===p.supplier_id)?.short}`, route: `/reagents/products` });
        }
      }
      for (const b of state.reagent_batches) {
        const p = state.reagent_products.find((x) => x.id === b.product_id);
        if (hit(b.lot) || hit(p?.name)) {
          results.push({ kind: 'reagent_batch', id: b.id, title: `${p?.name} · ${b.lot}`, sub: `剩 ${b.on_hand_boxes} 盒 · ${b.location}`, route: `/reagents/batches/${b.id}` });
        }
      }
      for (const dp of state.device_products) {
        if (hit(dp.name) || hit(dp.model) || hit(dp.brand)) {
          results.push({ kind: 'device_product', id: dp.id, title: dp.name, sub: `${dp.brand} · ${dp.model}`, route: `/devices/products` });
        }
      }
      for (const u of state.device_units) {
        if (hit(u.sn) || hit(state.device_products.find(x=>x.id===u.product_id)?.name)) {
          const dp = state.device_products.find((x) => x.id === u.product_id);
          results.push({ kind: 'device_unit', id: u.id, title: u.sn || `(SN 缺失) ${dp?.name}`, sub: `${dp?.name} · ${u.status}`, route: `/devices/units/${u.id}` });
        }
      }
      for (const s of state.suppliers) {
        if (hit(s.name) || hit(s.short) || hit(s.contact)) {
          results.push({ kind: 'supplier', id: s.id, title: s.name, sub: `供货商 · ${s.city} · ${s.contact}`, route: `/suppliers/${s.id}` });
        }
      }
      for (const c of state.customers) {
        if (hit(c.name) || hit(c.short) || hit(c.contact)) {
          results.push({ kind: 'customer', id: c.id, title: c.name, sub: `客户 · ${c.city} · ${c.contact}`, route: `/customers/${c.id}` });
        }
      }
      return results.slice(0, 24);
    };

    // Aggregate alarms across the system
    const allAlarms = React.useMemo(() => {
      const out = [];
      for (const b of state.reagent_batches) {
        const p = state.reagent_products.find((x) => x.id === b.product_id);
        for (const a of window.reagentAlarms(b, p)) {
          out.push({ ...a, scope: 'reagent', entity: b, entityKind: 'batch', product: p, key: `${a.type}:${b.id}` });
        }
      }
      for (const o of state.reagent_outflows) {
        for (const a of window.outflowAlarms(o)) {
          const b = state.reagent_batches.find((x) => x.id === o.batch_id);
          const p = state.reagent_products.find((x) => x.id === b?.product_id);
          out.push({ ...a, scope: 'reagent', entity: o, entityKind: 'outflow', batch: b, product: p, key: `${a.type}:${o.id}` });
        }
      }
      for (const u of state.device_units) {
        for (const a of window.deviceAlarms(u)) {
          const dp = state.device_products.find((x) => x.id === u.product_id);
          out.push({ ...a, scope: 'device', entity: u, entityKind: 'unit', product: dp, key: `${a.type}:${u.id}` });
        }
      }
      return out.sort((x, y) => window.severity(y.type) - window.severity(x.type));
    }, [state.reagent_batches, state.reagent_outflows, state.device_units]);

    const value = {
      ...state, set, add, update, remove, by, search, toast, dismissAlarm, logActivity, allAlarms,
      login, logout, loadAll,
      submitOutflow, submitReturn, submitTransfer,
    };
    return <StoreCtx.Provider value={value}>{children}</StoreCtx.Provider>;
  }

  // ===== HTML5 history router (clean URLs) =====
  // One-time migration: legacy "#/foo" → "/foo"
  (function migrateHash() {
    if (typeof window === 'undefined') return;
    const h = window.location.hash;
    if (h && h.indexOf('#/') === 0) {
      const p = h.slice(1); // strip leading "#"
      window.history.replaceState(null, '', p + window.location.search);
    }
  })();

  function currentPath() {
    return window.location.pathname || '/';
  }

  function useRoute() {
    const [path, setPath] = React.useState(currentPath());
    React.useEffect(() => {
      const on = () => setPath(currentPath());
      window.addEventListener('popstate', on);
      window.addEventListener('erp:navigate', on);
      // Legacy hashchange → migrate then route
      const onHash = () => {
        const h = window.location.hash;
        if (h && h.indexOf('#/') === 0) {
          const p = h.slice(1);
          window.history.replaceState(null, '', p + window.location.search);
          on();
        }
      };
      window.addEventListener('hashchange', onHash);
      return () => {
        window.removeEventListener('popstate', on);
        window.removeEventListener('erp:navigate', on);
        window.removeEventListener('hashchange', onHash);
      };
    }, []);
    const parts = path.split('/').filter(Boolean);
    // hash field returned for backward-compat (just the path)
    return { path, parts, hash: path };
  }

  function navigate(to) {
    if (!to) return;
    const p = String(to).replace(/^#/, '');
    const full = p + (p.includes('?') ? '' : '');
    if (full === window.location.pathname + window.location.search) return;
    window.history.pushState(null, '', full);
    window.dispatchEvent(new Event('erp:navigate'));
  }

  function matchRoute(path, pattern) {
    const pp = path.split('/').filter(Boolean);
    const tp = pattern.split('/').filter(Boolean);
    if (pp.length !== tp.length) return null;
    const params = {};
    for (let i = 0; i < tp.length; i++) {
      if (tp[i].startsWith(':')) params[tp[i].slice(1)] = decodeURIComponent(pp[i]);
      else if (tp[i] !== pp[i]) return null;
    }
    return params;
  }

  Object.assign(window, { StoreProvider, useStore, useRoute, navigate, matchRoute });
})();
