// ============================================================
// store.jsx — global app state + helpers
// ============================================================
const StoreContext = React.createContext(null);

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

function StoreProvider({ children }) {
  const [theme, setTheme] = React.useState(() => localStorage.getItem('lgs.theme') || 'dark');
  const [user, setUser] = React.useState(() => {
    const saved = localStorage.getItem('lgs.user');
    if (saved) return JSON.parse(saved);
    return null;
  });

  // FASE 52 — acting tenant impersonation per SUPERADMIN.
  // Quando settato, il client invia X-Acting-Tenant-Id su ogni fetch (read).
  // Persistito in localStorage. Cleared on logout.
  const [actingTenantId, setActingTenantIdRaw] = React.useState(() => {
    return localStorage.getItem('lgs.actingTenantId') || null;
  });
  const setActingTenantId = React.useCallback((id) => {
    if (id) {
      localStorage.setItem('lgs.actingTenantId', id);
    } else {
      localStorage.removeItem('lgs.actingTenantId');
    }
    setActingTenantIdRaw(id);
  }, []);
  const [route, setRoute] = React.useState(() => localStorage.getItem('lgs.route') || 'dashboard');
  const [routeParam, setRouteParam] = React.useState(() => localStorage.getItem('lgs.routeParam') || null);
  const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false);
  const [copilotOpen, setCopilotOpen] = React.useState(false);
  const [cmdOpen, setCmdOpen] = React.useState(false);
  const [toasts, setToasts] = React.useState([]);
  const [aiSettings, setAiSettings] = React.useState(() => {
    const s = localStorage.getItem('lgs.ai');
    if (s) return JSON.parse(s);
    return { provider: 'claude', claudeKey: '', geminiKey: '', model: 'claude-sonnet-4-5', temperature: 0.3, mode: 'hybrid' };
  });

  // Extras: nuove entità create nella sessione (non persistenti a livello di seed)
  const [extras, setExtras] = React.useState(() => {
    const s = localStorage.getItem('lgs.extras');
    if (s) { try { return JSON.parse(s); } catch {} }
    return {
      vendors: [], projects: [], rda: [], oda: [], documents: [],
      categoriesExt: [],  // [CategoryDto] — categorie create in sessione (FASE 3a.1)
      capexClassesExt: [], // [CapexClassDto] — classi CAPEX create in sessione (FASE 3a.2)
      docTypesExt: [],     // [DocTypeDto] — tipi documento creati in sessione (FASE 3a.3)
      sitesExt: [],        // [SiteDto] — siti creati in sessione (FASE 3a.4)
      calendarsExt: [],    // [CalendarDto] — calendari creati in sessione (FASE 3a.5)
      legalEntitiesExt: [], // [LegalEntityDto] — legal entity create in sessione (FASE 3a.6)
      businessUnitsExt: [], // [BusinessUnitDto] — BU create in sessione (FASE 3a.6)
      rolesExt: [],         // [RoleDto] — ruoli creati in sessione (FASE 3a.7)
      delegationsExt: [],   // [DelegationDto] — deleghe create in sessione (FASE 3a.7)
      slasExt: [],          // [SlaDto] — SLA modificati in sessione (FASE 3a.15)
      escalationsExt: [],   // [EscDto] — escalation rules modificate in sessione (FASE 3a.15)
      notifRulesExt: [],    // [NotificationRuleDto] — regole notifiche modificate (FASE 3a.15)
      notifEventsExt: [],   // [NotificationEventDto] — eventi notifiche modificati (FASE 3a.15)
      templatesExt: [],     // [TemplateDto] — template modificati (FASE 3a.15)
      clausesExt: [],       // [ClauseDto] — clausole modificate (FASE 3a.15)
      workflowsExt: [],     // [WorkflowDto] — workflow modificati (FASE 3a.16)
      matrixExt: [],        // [MatrixDto] — regole matrix modificate (FASE 3a.16)
      communicationsExt: [], // [CommunicationDto] — comms create in sessione (MVP gap #1)
      milestones: {},     // { [projectId]: [{id,title,due,status}] }
      comms: {},          // { [projectId]: [{id,ts,from,subject,preview,kind}] }
      budgetRows: {},     // { [projectId]: [{v,a,p}] }
      projectDocs: {},    // { [projectId]: [{...doc}] }
    };
  });
  React.useEffect(() => { localStorage.setItem('lgs.extras', JSON.stringify(extras)); }, [extras]);

  const addVendor = React.useCallback((v) => {
    setExtras(e => ({ ...e, vendors: [{ ...v, id: v.id || ('v' + Date.now()) }, ...e.vendors] }));
  }, []);
  const addProject = React.useCallback((p) => {
    setExtras(e => ({ ...e, projects: [{ ...p, id: p.id || ('P-2026-' + (900 + e.projects.length)) }, ...e.projects] }));
  }, []);
  const addRda = React.useCallback((r) => {
    setExtras(e => ({ ...e, rda: [{ ...r, id: r.id || ('RDA-2026-' + (9000 + e.rda.length)) }, ...e.rda] }));
  }, []);
  /**
   * upsertRda: se esiste già un record con lo stesso id in `extras.rda`, lo sostituisce.
   * Altrimenti lo aggiunge in cima. Usato per propagare PATCH verso la UI (FASE 2b.5).
   * Nota: il record sostituisce eventuali versioni "vecchie" in `seed.RDA` perché la
   * dedup in RdA.jsx priorizza extras.
   */
  const upsertRda = React.useCallback((r) => {
    if (!r?.id) return;
    setExtras(e => {
      const idx = (e.rda || []).findIndex(x => x.id === r.id);
      if (idx >= 0) {
        const next = [...e.rda];
        next[idx] = r;
        return { ...e, rda: next };
      }
      return { ...e, rda: [r, ...e.rda] };
    });
  }, []);
  /**
   * upsertOda (FASE 10.5): inserisce/sostituisce un'OdA in `extras.oda` per id
   * (extras vince sul seed in Oda.jsx). Usato per propagare POST/PATCH + la
   * conversione "Genera OdA" verso la UI senza reload.
   */
  const upsertOda = React.useCallback((o) => {
    if (!o?.id) return;
    setExtras(e => {
      const existing = e.oda || [];
      const idx = existing.findIndex(x => x.id === o.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = o;
        return { ...e, oda: next };
      }
      return { ...e, oda: [o, ...existing] };
    });
  }, []);
  const addOda = upsertOda;
  const addDocument = React.useCallback((d) => {
    setExtras(e => ({ ...e, documents: [{ ...d, id: d.id || ('DOC-' + Date.now()) }, ...e.documents] }));
  }, []);
  const addMilestone = React.useCallback((projectId, m) => {
    setExtras(e => ({ ...e, milestones: { ...e.milestones, [projectId]: [...(e.milestones[projectId] || []), { ...m, id: m.id || ('ms' + Date.now()) }] } }));
  }, []);
  const addComm = React.useCallback((projectId, c) => {
    setExtras(e => ({ ...e, comms: { ...e.comms, [projectId]: [{ ...c, id: 'cm' + Date.now() }, ...(e.comms[projectId] || [])] } }));
  }, []);
  const addBudgetRow = React.useCallback((projectId, row) => {
    setExtras(e => ({ ...e, budgetRows: { ...e.budgetRows, [projectId]: [...(e.budgetRows[projectId] || []), row] } }));
  }, []);
  const addProjectDoc = React.useCallback((projectId, doc) => {
    setExtras(e => ({ ...e, projectDocs: { ...e.projectDocs, [projectId]: [{ ...doc, id: doc.id || ('PD-' + Date.now()) }, ...(e.projectDocs[projectId] || [])] } }));
  }, []);
  /**
   * addCategory: aggiunge una categoria appena creata via API in extras.
   * FASE 3a.1: la dropdown del NewProjectModal e la lista in CustCategories
   * fanno merge `[...extras.categoriesExt, ...seedCustom.CATEGORIES_EXT]` con
   * dedup per id, così la nuova categoria appare immediatamente senza reload.
   */
  const addCategory = React.useCallback((c) => {
    if (!c?.id) return;
    setExtras(e => {
      const existing = e.categoriesExt || [];
      const idx = existing.findIndex(x => x.id === c.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = c;
        return { ...e, categoriesExt: next };
      }
      return { ...e, categoriesExt: [c, ...existing] };
    });
  }, []);

  /**
   * addCapexClass: aggiunge una classe CAPEX appena creata via API in extras.
   * FASE 3a.2: la lista in CustCapexClasses fa merge
   * `[...extras.capexClassesExt, ...seedCustom.CAPEX_CLASSES]` con dedup per id.
   */
  const addCapexClass = React.useCallback((c) => {
    if (!c?.id) return;
    setExtras(e => {
      const existing = e.capexClassesExt || [];
      const idx = existing.findIndex(x => x.id === c.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = c;
        return { ...e, capexClassesExt: next };
      }
      return { ...e, capexClassesExt: [c, ...existing] };
    });
  }, []);

  /**
   * addDocType: aggiunge un tipo documento appena creato via API in extras.
   * FASE 3a.3: la lista in CustDocTypes fa merge
   * `[...extras.docTypesExt, ...seedCustom.DOC_TYPES]` con dedup per id.
   */
  const addDocType = React.useCallback((d) => {
    if (!d?.id) return;
    setExtras(e => {
      const existing = e.docTypesExt || [];
      const idx = existing.findIndex(x => x.id === d.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = d;
        return { ...e, docTypesExt: next };
      }
      return { ...e, docTypesExt: [d, ...existing] };
    });
  }, []);

  /**
   * addSite: aggiunge un sito appena creato via API in extras.
   * FASE 3a.4: la lista in CustSites fa merge
   * `[...extras.sitesExt, ...seedCustom.SITES_EXT]` con dedup per id.
   */
  const addSite = React.useCallback((s) => {
    if (!s?.id) return;
    setExtras(e => {
      const existing = e.sitesExt || [];
      const idx = existing.findIndex(x => x.id === s.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = s;
        return { ...e, sitesExt: next };
      }
      return { ...e, sitesExt: [s, ...existing] };
    });
  }, []);

  /**
   * Helper generico upsert in extras (FASE 3a.6+): da usare per tutte le entità
   * config che richiedono dedup extras+seed. Riduce la duplicazione delle
   * callback `addX(x)`.
   */
  const upsertInto = React.useCallback((bucket, item) => {
    if (!item?.id) return;
    setExtras(e => {
      const existing = e[bucket] || [];
      const idx = existing.findIndex(x => x.id === item.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = item;
        return { ...e, [bucket]: next };
      }
      return { ...e, [bucket]: [item, ...existing] };
    });
  }, []);

  const addLegalEntity = React.useCallback((le) => upsertInto('legalEntitiesExt', le), [upsertInto]);
  const addBusinessUnit = React.useCallback((bu) => upsertInto('businessUnitsExt', bu), [upsertInto]);
  const addRole = React.useCallback((r) => upsertInto('rolesExt', r), [upsertInto]);
  const addDelegation = React.useCallback((d) => upsertInto('delegationsExt', d), [upsertInto]);
  // FASE 3a.15 batch: upsert SLA policies post-PATCH.
  const addSla = React.useCallback((s) => upsertInto('slasExt', s), [upsertInto]);
  const addEscalation = React.useCallback((e) => upsertInto('escalationsExt', e), [upsertInto]);
  const addNotificationRule = React.useCallback((r) => upsertInto('notifRulesExt', r), [upsertInto]);
  const addNotificationEvent = React.useCallback((e) => upsertInto('notifEventsExt', e), [upsertInto]);
  const addTemplate = React.useCallback((t) => upsertInto('templatesExt', t), [upsertInto]);
  const addClause = React.useCallback((c) => upsertInto('clausesExt', c), [upsertInto]);
  const addWorkflow = React.useCallback((w) => upsertInto('workflowsExt', w), [upsertInto]);
  const addMatrix = React.useCallback((m) => upsertInto('matrixExt', m), [upsertInto]);
  const addCommunication = React.useCallback((c) => upsertInto('communicationsExt', c), [upsertInto]);

  /**
   * addCalendar: aggiunge un calendario appena creato via API in extras.
   * FASE 3a.5: la lista in CustCalendars fa merge
   * `[...extras.calendarsExt, ...seedCustom.CALENDARS]` con dedup per id.
   */
  const addCalendar = React.useCallback((c) => {
    if (!c?.id) return;
    setExtras(e => {
      const existing = e.calendarsExt || [];
      const idx = existing.findIndex(x => x.id === c.id);
      if (idx >= 0) {
        const next = [...existing];
        next[idx] = c;
        return { ...e, calendarsExt: next };
      }
      return { ...e, calendarsExt: [c, ...existing] };
    });
  }, []);

  React.useEffect(() => {
    document.body.classList.remove('theme-dark', 'theme-light');
    document.body.classList.add('theme-' + theme);
    localStorage.setItem('lgs.theme', theme);
  }, [theme]);

  React.useEffect(() => {
    if (user) localStorage.setItem('lgs.user', JSON.stringify(user));
    else localStorage.removeItem('lgs.user');
  }, [user]);

  React.useEffect(() => {
    localStorage.setItem('lgs.route', route);
    if (routeParam) localStorage.setItem('lgs.routeParam', routeParam);
    else localStorage.removeItem('lgs.routeParam');
  }, [route, routeParam]);

  React.useEffect(() => {
    localStorage.setItem('lgs.ai', JSON.stringify(aiSettings));
  }, [aiSettings]);

  const navigate = React.useCallback((r, p = null) => {
    setRoute(r);
    setRouteParam(p);
    document.querySelector('.main')?.scrollTo?.(0, 0);
  }, []);

  const pushToast = React.useCallback((t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts((prev) => [...prev, { id, ...t }]);
    setTimeout(() => setToasts((prev) => prev.filter((x) => x.id !== id)), 4200);
  }, []);
  // Espone pushToast globalmente così gli hook condivisi (es. useEditableEntity in
  // forms.jsx) possono mostrare un toast d'errore VISIBILE sui salvataggi, non solo
  // un banner dentro la modale che si rischia di non vedere.
  React.useEffect(() => { if (typeof window !== 'undefined') window.__pushToast = pushToast; }, [pushToast]);

  const value = React.useMemo(() => ({
    theme, setTheme,
    user, setUser,
    actingTenantId, setActingTenantId,
    route, routeParam, navigate,
    sidebarCollapsed, setSidebarCollapsed,
    copilotOpen, setCopilotOpen,
    cmdOpen, setCmdOpen,
    aiSettings, setAiSettings,
    toasts, pushToast,
    extras,
    addVendor, addProject, addRda, upsertRda, addOda, upsertOda, addDocument,
    addMilestone, addComm, addBudgetRow, addProjectDoc, addCategory, addCapexClass, addDocType, addSite, addCalendar, addLegalEntity, addBusinessUnit, addRole, addDelegation, addSla, addEscalation, addNotificationRule, addNotificationEvent, addTemplate, addClause, addWorkflow, addMatrix, addCommunication,
    seed: window.__SEED,
    seedCustom: window.__SEED_CUSTOMIZING,
  }), [theme, user, actingTenantId, setActingTenantId, route, routeParam, sidebarCollapsed, copilotOpen, cmdOpen, aiSettings, toasts, extras, navigate, pushToast, addVendor, addProject, addRda, upsertRda, addDocument, addMilestone, addComm, addBudgetRow, addProjectDoc, addCategory, addCapexClass, addDocType, addSite, addCalendar, addLegalEntity, addBusinessUnit, addRole, addDelegation, addSla, addEscalation, addNotificationRule, addNotificationEvent, addTemplate, addClause, addWorkflow, addMatrix, addCommunication]);

  return <StoreContext.Provider value={value}>{children}</StoreContext.Provider>;
}

// Helpers
function fmtEUR(n, compact = false) {
  if (n == null) return '—';
  if (compact) {
    if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(2).replace('.', ',') + ' M€';
    if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(0) + ' k€';
    return n + ' €';
  }
  return new Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR', maximumFractionDigits: 0 }).format(n);
}
function fmtPct(n, decimals = 0) {
  if (n == null) return '—';
  return (n > 0 ? '+' : '') + n.toFixed(decimals) + '%';
}
function fmtDate(s) {
  if (!s) return '—';
  const d = new Date(s);
  return d.toLocaleDateString('it-IT', { day: '2-digit', month: 'short', year: 'numeric' });
}
function daysBetween(a, b) {
  return Math.round((new Date(b) - new Date(a)) / (1000 * 60 * 60 * 24));
}
function healthLabel(h) {
  return { ok: 'In linea', warn: 'Attenzione', err: 'Critico' }[h] || h;
}
function phaseLabel(p) {
  return { requisito: 'Requisito', pianificazione: 'Pianificazione', offerta: 'Offerta', rda: 'RdA', esecuzione: 'Esecuzione', chiusura: 'Chiusura' }[p] || p;
}

Object.assign(window, {
  StoreContext, StoreProvider, useStore,
  fmtEUR, fmtPct, fmtDate, daysBetween, healthLabel, phaseLabel,
});
