// src/store.jsx — minimal global state + tiny hash router for the Piixi prototype.

const { useState, useEffect, useRef, useMemo, useCallback, useContext, createContext } = React;

const AxStoreCtx = createContext(null);

// ──────────────────────────────────────────────────────────────
// STATE SHAPE
// ──────────────────────────────────────────────────────────────
function makeInitialState() {
  return {
    user: {
      name: 'Miguel Ángel Rivera',
      email: 'miguel@madpanda.mx',
      orgName: 'Rivera Asesores',
    },
    // Auth gate for the prototype. Start on /inicio so the user lands on real product.
    signedIn: true,
    onboarded: true,
    // Live data (cloned from seeded AdmixData; mutations only here)
    policies:   AdmixData.POLICIES.slice(),
    pendientes: AdmixData.PENDIENTES.slice(),
    rulesets:   AdmixData.RULESETS.slice(),
    // Last bulk-import job — null at rest, holds progress/summary during/after run
    lastImport: null,
    importHistory: [
      {
        id: 'imp-001',
        folder: '~/Pólizas/2026/Mayo',
        startedAt: '2026-05-17T22:14:00',
        finishedAt: '2026-05-17T22:14:34',
        total: 47,
        matched: 34,
        pending: 11,
        unmatched: 2,
      },
    ],
    // UI notifications (toasts)
    toasts: [],
    // Per-screen UI state cache
    importDraft: null,
    // tweak state mirror (driven by useTweaks; here for cross-component reads)
    tweaks: null,
  };
}

function AxStoreProvider({ children }) {
  const [state, setState] = useState(makeInitialState);

  // ──────────────────────────────────────────────────────────────
  // ACTIONS
  // ──────────────────────────────────────────────────────────────
  const api = useMemo(() => {
    const update = (patch) => setState((s) => ({ ...s, ...(typeof patch === 'function' ? patch(s) : patch) }));

    const pushToast = (toast) => {
      const id = 't' + Date.now() + Math.random().toString(36).slice(2, 6);
      setState((s) => ({ ...s, toasts: [...s.toasts, { id, createdAt: Date.now(), ...toast }] }));
      if (toast.timeout !== 0) {
        setTimeout(() => {
          setState((s) => ({ ...s, toasts: s.toasts.filter((t) => t.id !== id) }));
        }, toast.timeout || 6500);
      }
      return id;
    };
    const dismissToast = (id) => setState((s) => ({ ...s, toasts: s.toasts.filter((t) => t.id !== id) }));

    const updatePendiente = (id, patch) => {
      setState((s) => ({
        ...s,
        pendientes: s.pendientes.map((p) => (p.id === id ? { ...p, ...patch } : p)),
      }));
    };

    const approvePendiente = (id, fields) => {
      setState((s) => {
        const pn = s.pendientes.find((p) => p.id === id);
        if (!pn) return s;
        // Promote to a real policy
        const newPolicy = {
          id: 'p' + String(s.policies.length + 1).padStart(4, '0'),
          polizaNumero: fields.polizaNumero || pn.polizaNumero || 'XX-000000',
          carrier: pn.carrier,
          ramo: pn.ramo,
          producto: pn.producto,
          contratante: pn.contratante,
          status: 'MANUALLY_APPROVED',
          inicioVigencia: fields.inicioVigencia || pn.inicioVigencia,
          finVigencia: fields.finVigencia || pn.finVigencia,
          primaTotal: fields.primaTotal != null ? Number(fields.primaTotal) : (pn.primaTotal || 0),
          moneda: pn.moneda || 'MXN',
          formaPago: pn.formaPago || 'Anual',
          createdAt: pn.importedAt,
          sourcePdf: pn.filename,
          asegurados: 1,
          provenance: 'fingerprint+udee',
        };
        return {
          ...s,
          policies: [newPolicy, ...s.policies],
          pendientes: s.pendientes.filter((p) => p.id !== id),
        };
      });
      pushToast({ kind: 'success', message: 'Póliza aprobada y guardada en tu libro.' });
    };

    const discardPendiente = (id) => {
      setState((s) => ({ ...s, pendientes: s.pendientes.filter((p) => p.id !== id) }));
      pushToast({ kind: 'info', message: 'Movido al bucket de no-pólizas. El PDF queda en el registro auditable.' });
    };

    const runImport = (folder, opts = {}) => {
      // Idempotency check: if same folder + same fileset as last completed import, do no-op replay
      const replay = state.importHistory.find((h) => h.folder === folder);
      if (replay && opts.replay) {
        const fakeJob = {
          id: 'imp-' + Date.now(),
          folder,
          total: replay.total,
          progress: replay.total,
          processing: null,
          startedAt: Date.now(),
          finishedAt: Date.now(),
          status: 'idempotent',
          summary: {
            matched: 0, pending: 0, unmatched: 0,
            alreadyImported: replay.total,
            previousDate: replay.startedAt,
          },
        };
        setState((s) => ({ ...s, lastImport: fakeJob }));
        pushToast({ kind: 'info', message: 'Estas 47 pólizas ya estaban en tu libro desde el 17 may.' });
        return fakeJob.id;
      }

      const files = AdmixData.IMPORT_FILES.slice();
      const job = {
        id: 'imp-' + Date.now(),
        folder,
        total: files.length,
        progress: 0,
        processing: null,
        files,
        startedAt: Date.now(),
        finishedAt: null,
        status: 'running',
      };
      setState((s) => ({ ...s, lastImport: job }));

      let idx = 0;
      const tick = () => {
        idx++;
        if (idx > files.length) {
          // Finalize
          const matched = files.filter((f) => f.outcome === 'matched').length;
          const pending = files.filter((f) => f.outcome === 'pending').length;
          const unmatched = files.filter((f) => f.outcome === 'unmatched').length;
          // Aggregate by carrier × ramo for matched
          const byCarrierRamo = {};
          files.filter((f) => f.outcome === 'matched').forEach((f) => {
            const k = (f.carrierId || '?') + ':' + (f.ramo || '?');
            byCarrierRamo[k] = (byCarrierRamo[k] || 0) + 1;
          });
          setState((s) => {
            const finished = {
              ...s.lastImport,
              progress: files.length,
              processing: null,
              finishedAt: Date.now(),
              status: 'done',
              summary: { matched, pending, unmatched, byCarrierRamo },
            };
            return {
              ...s,
              lastImport: finished,
              importHistory: [
                {
                  id: finished.id,
                  folder,
                  startedAt: new Date(finished.startedAt).toISOString(),
                  finishedAt: new Date(finished.finishedAt).toISOString(),
                  total: files.length,
                  matched, pending, unmatched,
                },
                ...s.importHistory,
              ],
            };
          });
          pushToast({
            kind: 'success',
            message: `Importación lista. ${matched} guardadas, ${pending} en pendientes.`,
          });
          return;
        }
        setState((s) => ({
          ...s,
          lastImport: s.lastImport && s.lastImport.id === job.id
            ? { ...s.lastImport, progress: idx, processing: files[idx - 1].name }
            : s.lastImport,
        }));
        setTimeout(tick, 90 + Math.random() * 130);
      };
      setTimeout(tick, 220);
      return job.id;
    };

    const cancelImport = () => {
      setState((s) => {
        if (!s.lastImport || s.lastImport.status !== 'running') return s;
        return { ...s, lastImport: { ...s.lastImport, status: 'cancelled', finishedAt: Date.now() } };
      });
    };

    const dismissImport = () => setState((s) => ({ ...s, lastImport: null }));

    const pullRulesets = () => {
      // Simulate finding 3 new rulesets
      const toInstall = state.rulesets.filter((r) => !r.installed).slice(0, 3);
      setState((s) => ({
        ...s,
        rulesets: s.rulesets.map((r) => toInstall.find((t) => t.id === r.id) ? { ...r, installed: true } : r),
      }));
      // Reclassify some pendientes
      setState((s) => {
        // Move ~5 pending with UNMATCHED_TAXONOMY out of pendientes — make them auto-approved policies
        const moved = s.pendientes.filter((p) => p.reason === 'UNMATCHED_TAXONOMY').slice(0, 5);
        if (moved.length === 0) return s;
        const newPolicies = moved.map((p, i) => ({
          id: 'p' + String(s.policies.length + 1 + i).padStart(4, '0'),
          polizaNumero: p.polizaNumero || (p.carrier ? AdmixData.carrierById(p.carrier).short : 'XX') + '-' + String(Math.floor(100000 + Math.random() * 899999)),
          carrier: p.carrier,
          ramo: p.ramo,
          producto: p.producto || 'Plan Esencial',
          contratante: p.contratante,
          status: 'AUTO_APPROVED',
          inicioVigencia: p.inicioVigencia || '2026-01-01',
          finVigencia: p.finVigencia || '2027-01-01',
          primaTotal: p.primaTotal || 12000,
          moneda: 'MXN',
          formaPago: p.formaPago || 'Anual',
          createdAt: p.importedAt,
          sourcePdf: p.filename,
          asegurados: 1,
          provenance: 'fingerprint+udee',
        }));
        return {
          ...s,
          policies: [...newPolicies, ...s.policies],
          pendientes: s.pendientes.filter((p) => !moved.includes(p)),
        };
      });
      return toInstall.length;
    };

    const setTweaks = (t) => setState((s) => ({ ...s, tweaks: t }));

    return {
      pushToast, dismissToast,
      updatePendiente, approvePendiente, discardPendiente,
      runImport, cancelImport, dismissImport,
      pullRulesets, setTweaks,
    };
  }, [state.importHistory]);

  return (
    <AxStoreCtx.Provider value={{ state, ...api }}>{children}</AxStoreCtx.Provider>
  );
}

function useAxStore() {
  const ctx = useContext(AxStoreCtx);
  if (!ctx) throw new Error('AxStoreProvider missing');
  return ctx;
}

// ──────────────────────────────────────────────────────────────
// HASH ROUTER
// ──────────────────────────────────────────────────────────────
function useHashRoute() {
  const [hash, setHash] = useState(() => window.location.hash || '#/inicio');
  useEffect(() => {
    const on = () => setHash(window.location.hash || '#/inicio');
    window.addEventListener('hashchange', on);
    return () => window.removeEventListener('hashchange', on);
  }, []);
  // Strip leading '#'
  const path = hash.replace(/^#/, '');
  const navigate = useCallback((to) => {
    if (!to) return;
    if (to.startsWith('#')) to = to.slice(1);
    if (!to.startsWith('/')) to = '/' + to;
    if (window.location.hash !== '#' + to) {
      window.location.hash = to;
    }
  }, []);

  // Match a route. Returns { params } or null.
  const match = useCallback((pattern) => {
    const pp = pattern.split('/').filter(Boolean);
    const aa = path.split('?')[0].split('/').filter(Boolean);
    if (pp.length !== aa.length) return null;
    const params = {};
    for (let i = 0; i < pp.length; i++) {
      if (pp[i].startsWith(':')) params[pp[i].slice(1)] = decodeURIComponent(aa[i]);
      else if (pp[i] !== aa[i]) return null;
    }
    return { params };
  }, [path]);

  const query = useMemo(() => {
    const q = (path.split('?')[1] || '');
    const out = {};
    q.split('&').filter(Boolean).forEach((kv) => {
      const [k, v] = kv.split('=');
      out[decodeURIComponent(k)] = v == null ? '' : decodeURIComponent(v);
    });
    return out;
  }, [path]);

  return { path: path.split('?')[0], hash, navigate, match, query };
}

Object.assign(window, { AxStoreProvider, useAxStore, useHashRoute });
