// Brook Consultancy — multi-record state store (v2)
//
// v2 changes from v1:
//   - Multiple consultations live concurrently under `records[recordId]`
//   - One `activeRecordId` points at the currently-edited record
//   - `fileIdByRecordId` tracks which Drive file backs each record
//   - `deviceId` generated once and persisted; used for `claimedBy`
//   - Each record carries `status` ('in_progress' | 'ready_for_analysis' | 'analysing' | 'complete' | 'archived'),
//     `claimedBy` (deviceId or null), and `updatedAt` (ISO)
//   - Functional setState everywhere — never spread captured state
//   - Auto-migration from v1 single-record blob

const STORE_KEY        = 'brook_store_v2';
const STORAGE_KEY_V1   = 'brook_consultation_v1';
const SESSION_TIMER_KEY = 'brook_session_v1';
const QUICK_CAPTURE_KEY = 'brook_quickcapture_v1';
const LOG_KEY           = 'brook_consultation_log_v1';
const PIN_KEY           = 'brook_pin_v1';
const DEVICE_ID_KEY     = 'brook_device_id_v1';
const DEFAULT_PIN       = '4274'; // BROK on a phone keypad

function nowIso() { return new Date().toISOString(); }
function uid()    { return Math.random().toString(36).slice(2, 10); }
function uuid() {
  // RFC4122-ish v4 without crypto dependency
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function getOrCreateDeviceId() {
  try {
    let id = localStorage.getItem(DEVICE_ID_KEY);
    if (!id) {
      id = 'dev-' + uuid();
      localStorage.setItem(DEVICE_ID_KEY, id);
    }
    return id;
  } catch { return 'dev-' + uuid(); }
}

// Map a booking's business_type pick to the closest consultation org_type
// option from the schema. Used when an operator converts a booking.
function mapBusinessTypeToOrgType(bt) {
  switch ((bt || '').toLowerCase()) {
    case 'care':       return 'Care Home';
    case 'social care': return 'Care Home';
    case 'small business': return 'Business or Enterprise';
    case 'nonprofit':  return 'Nonprofit';
    case 'faith':      return 'Faith-based Organisation';
    case 'community':  return 'Community Organisation';
    default:           return '';
  }
}

// ---------------------------------------------------------------
// Build initial empty consultation record
// ---------------------------------------------------------------
function buildInitialConsultation() {
  const data = {
    _id: uuid(),
    _started_at: nowIso(),
    _autofill_used: false,
    _autofill_meta: {},
    _section_progress: {},

    // v2 sync metadata
    kind: 'consultation',     // 'consultation' | 'booking'
    status: 'in_progress',
    claimedBy: null,
    updatedAt: nowIso(),

    fields: {},
    repeaters: { quotes: [], evidence: [], workstreams: [] },
    areas: {
      leadership: { evidence: [] },
      operations: { evidence: [] },
      financial: { evidence: [] },
      people:     { evidence: [] },
      risk:       { evidence: [] },
      customer:   { evidence: [] },
      technology: { evidence: [] }
    },
    findings: { strengths: [], afd: [], actions: [] },
    aiSummary: '',
    aiTranscript: '',
    aiResults: {
      executive_summary: { overall_assessment: '', key_risks: [], immediate_priorities: [] },
      statutory: { findings: [] },
      findings:  { strengths: [], afd: [], actions: [] }
    }
  };
  data.fields.consultation_date = new Date().toISOString().slice(0, 10);
  return data;
}

// ---------------------------------------------------------------
// LocalStorage IO
// ---------------------------------------------------------------
function safeRead(key, fallback) {
  try {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : fallback;
  } catch { return fallback; }
}
function safeWrite(key, val) {
  try { localStorage.setItem(key, JSON.stringify(val)); return true; }
  catch (e) { console.warn('LS write failed', e); return false; }
}

// ---------------------------------------------------------------
// Single-record migration (handles pre-rename "dashboard" -> "aiResults")
// ---------------------------------------------------------------
function migrateRecord(stored) {
  if (!stored) return stored;
  const fresh = buildInitialConsultation();
  const merged = { ...fresh, ...stored };
  if (stored.dashboard && !stored.aiResults) {
    const d = stored.dashboard;
    merged.aiResults = {
      executive_summary: d.executive_summary || fresh.aiResults.executive_summary,
      statutory: { findings: d.compliance || [] },
      findings: {
        strengths: d.strengths || [],
        afd: d.areas_for_development || d.afd || [],
        actions: d.actions || []
      }
    };
    Object.entries(d.areas || {}).forEach(([k, v]) => {
      merged.aiResults[k] = { ...v, applied: true };
    });
    delete merged.dashboard;
  }
  if (!merged.areas)    merged.areas = fresh.areas;
  if (!merged.findings) merged.findings = fresh.findings;
  if (typeof merged.aiSummary    !== 'string') merged.aiSummary = '';
  if (typeof merged.aiTranscript !== 'string') merged.aiTranscript = '';

  // v2 fields
  if (!merged.status)         merged.status = 'in_progress';
  if (merged.claimedBy === undefined) merged.claimedBy = null;
  if (!merged.updatedAt)      merged.updatedAt = merged._started_at || nowIso();
  if (!merged.kind)           merged.kind = 'consultation';
  return merged;
}

// ---------------------------------------------------------------
// Build initial booking record (Pass 3)
// Bookings live in the same records map as consultations, distinguished by
// kind: 'booking'. Operators convert them to consultations on arrival.
// ---------------------------------------------------------------
function buildInitialBooking() {
  return {
    _id: uuid(),
    _started_at: nowIso(),
    kind: 'booking',
    status: 'booked',       // booked | arrived | converted | no_show | cancelled
    claimedBy: null,
    updatedAt: nowIso(),

    // Booking-specific fields. Stored under `booking` so we never collide with
    // consultation `fields`.
    booking: {
      // Contact
      contact_name: '',
      business_name: '',
      business_type: '',        // Care / Small business / Nonprofit / Other
      region: '',
      email: '',
      phone: '',

      // Stand routing
      preferred_zone: '',       // Social Care / Small Business / Business Transformation
      slot_iso: '',             // ISO datetime of the booked 30-min slot
      slot_label: '',           // Human label, e.g. "Today 14:30 – 15:00"

      // Discovery (1-line + short paragraph)
      headline_challenge: '',
      hopes_from_review: '',

      // Audit
      submitted_via: 'qr-code',
      submitted_at: nowIso(),
      converted_record_id: null
    }
  };
}

// ---------------------------------------------------------------
// Build initial multi-record store. Migrates from v1 if present.
// ---------------------------------------------------------------
function buildInitialStore() {
  const deviceId = getOrCreateDeviceId();
  const base = {
    records: {},
    activeRecordId: null,
    fileIdByRecordId: {},
    deviceId,
    _lastSyncedAt: null,
    _syncErrors: []
  };

  // v1 migration: if old single-record blob exists, ingest it as the first record.
  const v1 = safeRead(STORAGE_KEY_V1, null);
  if (v1) {
    const rec = migrateRecord(v1);
    base.records[rec._id] = rec;
    base.activeRecordId = rec._id;
    try { localStorage.removeItem(STORAGE_KEY_V1); } catch {}
  }

  // Always ensure there's an active record so the UI never lands on null.
  if (!base.activeRecordId) {
    const rec = buildInitialConsultation();
    base.records[rec._id] = rec;
    base.activeRecordId = rec._id;
  }
  return base;
}

// ---------------------------------------------------------------
// Load store with v2 migration / hydration
// ---------------------------------------------------------------
function loadStore() {
  const stored = safeRead(STORE_KEY, null);
  if (!stored || !stored.records) {
    const fresh = buildInitialStore();
    safeWrite(STORE_KEY, fresh);
    return fresh;
  }
  // Ensure every record passes through migrateRecord (handles old shapes)
  const records = {};
  Object.entries(stored.records).forEach(([id, rec]) => {
    records[id] = migrateRecord(rec);
  });
  const out = {
    records,
    activeRecordId: stored.activeRecordId && records[stored.activeRecordId]
      ? stored.activeRecordId
      : (Object.keys(records)[0] || null),
    fileIdByRecordId: stored.fileIdByRecordId || {},
    deviceId: stored.deviceId || getOrCreateDeviceId(),
    _lastSyncedAt: stored._lastSyncedAt || null,
    _syncErrors: Array.isArray(stored._syncErrors) ? stored._syncErrors : []
  };

  // Guarantee an active record exists
  if (!out.activeRecordId) {
    const rec = buildInitialConsultation();
    out.records[rec._id] = rec;
    out.activeRecordId = rec._id;
  }
  return out;
}

// ---------------------------------------------------------------
// useConsultation — multi-record hook
//
// Returns:
//   store              — full store object (records, activeRecordId, ...)
//   data               — convenience: store.records[store.activeRecordId]
//   setStore(updater)  — functional setState on the entire store
//   updateActive(updater)   — functional setState on the active record only
//   newRecord(), switchTo(id), removeRecord(id)
//   plus all the v1 helpers (setField, setRepeater, ...) targeting the active record
//   replaceRecord(rec)      — replace a record by id (used by sync to merge remote)
//
// CRITICAL: every setState here uses the functional form. Never read state in a
// closure and spread it — that's how stale-state clobbers happen during sync.
// ---------------------------------------------------------------
function useConsultation() {
  const [store, _setStore] = React.useState(loadStore);
  const [saveStatus, setSaveStatus] = React.useState('saved');
  const saveTimer = React.useRef(null);

  // Persist on every change
  React.useEffect(() => {
    setSaveStatus('saving');
    if (saveTimer.current) clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => {
      const ok = safeWrite(STORE_KEY, store);
      setSaveStatus(ok ? 'saved' : 'error');
    }, 400);
    return () => { if (saveTimer.current) clearTimeout(saveTimer.current); };
  }, [store]);

  // Core setters — ALL functional
  const setStore = React.useCallback((updater) => {
    _setStore(prev => typeof updater === 'function' ? updater(prev) : updater);
  }, []);

  const updateActive = React.useCallback((recUpdater) => {
    _setStore(prev => {
      const id = prev.activeRecordId;
      if (!id) return prev;
      const cur = prev.records[id];
      if (!cur) return prev;
      const next = typeof recUpdater === 'function' ? recUpdater(cur) : recUpdater;
      // Stamp updatedAt automatically on any change that isn't already explicit
      const withTs = next === cur ? cur : { ...next, updatedAt: next.updatedAt && next.updatedAt !== cur.updatedAt ? next.updatedAt : nowIso() };
      return { ...prev, records: { ...prev.records, [id]: withTs } };
    });
  }, []);

  // Convenience helpers — every one wraps updateActive (which is functional)
  const setField = (id, value) => updateActive(d => ({
    ...d,
    fields: { ...d.fields, [id]: value }
  }));

  const setMeta = (id, meta) => updateActive(d => ({
    ...d,
    _autofill_meta: { ...d._autofill_meta, [id]: meta }
  }));

  const clearAutofillForField = (id) => updateActive(d => {
    const m = { ...d._autofill_meta };
    delete m[id];
    return { ...d, _autofill_meta: m, fields: { ...d.fields, [id]: '' } };
  });

  const acceptAutofillForField = (id) => updateActive(d => {
    const cur = d._autofill_meta[id];
    if (!cur) return d;
    return { ...d, _autofill_meta: { ...d._autofill_meta, [id]: { ...cur, accepted: true } } };
  });

  const setRepeater = (key, value) => updateActive(d => ({
    ...d, repeaters: { ...d.repeaters, [key]: value }
  }));

  // Back-compat: dashboard updates write to aiResults sub-tree
  const setDashboard = (next) => updateActive(d => {
    const cur = {
      areas: Object.fromEntries(Object.entries(d.aiResults || {}).filter(([k]) => !['statutory','findings','executive_summary'].includes(k))),
      compliance: d.aiResults?.statutory?.findings || [],
      strengths: d.aiResults?.findings?.strengths || [],
      areas_for_development: d.aiResults?.findings?.afd || [],
      afd: d.aiResults?.findings?.afd || [],
      actions: d.aiResults?.findings?.actions || [],
      executive_summary: d.aiResults?.executive_summary || { overall_assessment: '', key_risks: [], immediate_priorities: [] }
    };
    const updated = typeof next === 'function' ? next(cur) : next;
    const newAi = { ...d.aiResults };
    Object.entries(updated.areas || {}).forEach(([k, v]) => { newAi[k] = v; });
    newAi.statutory = { findings: updated.compliance || [] };
    newAi.findings = {
      strengths: updated.strengths || [],
      afd: updated.afd || updated.areas_for_development || [],
      actions: updated.actions || []
    };
    newAi.executive_summary = updated.executive_summary || newAi.executive_summary;
    return { ...d, aiResults: newAi };
  });

  const setAiSource = (summary, transcript) => updateActive(d => ({
    ...d,
    aiSummary: summary !== undefined ? summary : d.aiSummary,
    aiTranscript: transcript !== undefined ? transcript : d.aiTranscript
  }));

  const setAreaEvidence = (areaId, evidenceArr) => updateActive(d => ({
    ...d,
    areas: { ...d.areas, [areaId]: { ...(d.areas[areaId] || {}), evidence: evidenceArr } }
  }));

  const setManualFindings = (next) => updateActive(d => ({
    ...d,
    findings: typeof next === 'function' ? next(d.findings) : next
  }));

  // Replace the active record entirely (e.g. after import)
  const replaceAll = (next) => updateActive(() => migrateRecord(next));

  // ---- Multi-record management ----
  const newRecord = React.useCallback(() => {
    let newId = null;
    _setStore(prev => {
      const rec = buildInitialConsultation();
      newId = rec._id;
      return {
        ...prev,
        records: { ...prev.records, [rec._id]: rec },
        activeRecordId: rec._id
      };
    });
    return newId;
  }, []);

  // Add an externally-built record (e.g. a booking record loaded from the
  // Apps Script endpoint or imported file). Does not switch to it.
  const addRecord = React.useCallback((rec) => {
    if (!rec || !rec._id) return null;
    _setStore(prev => ({
      ...prev,
      records: { ...prev.records, [rec._id]: rec }
    }));
    return rec._id;
  }, []);

  // Convert a booking record into a fresh consultation record, copying basic
  // details across. The booking remains in the store with status='converted'
  // and a back-pointer to the new consultation.
  const convertBookingToConsultation = React.useCallback((bookingId) => {
    let newId = null;
    _setStore(prev => {
      const b = prev.records[bookingId];
      if (!b || b.kind !== 'booking') return prev;
      const cons = buildInitialConsultation();
      // Pre-fill consultation fields from the booking
      cons.fields = {
        ...cons.fields,
        org_name: b.booking?.business_name || '',
        org_type: mapBusinessTypeToOrgType(b.booking?.business_type),
        la_region: b.booking?.region || '',
        client_contact_name: b.booking?.contact_name || '',
        client_contact_email: b.booking?.email || '',
        client_contact_phone: b.booking?.phone || '',
        consultation_method: 'In Person',
        consultation_date: new Date().toISOString().slice(0, 10),
        client_request: b.booking?.hopes_from_review || '',
        core_challenges: b.booking?.headline_challenge || ''
      };
      cons._origin_booking_id = bookingId;
      newId = cons._id;
      return {
        ...prev,
        records: {
          ...prev.records,
          [cons._id]: cons,
          [bookingId]: {
            ...b,
            status: 'converted',
            updatedAt: nowIso(),
            booking: { ...b.booking, converted_record_id: cons._id }
          }
        },
        activeRecordId: cons._id
      };
    });
    return newId;
  }, []);

  const switchTo = React.useCallback((id) => {
    _setStore(prev => prev.records[id] ? { ...prev, activeRecordId: id } : prev);
  }, []);

  const removeRecord = React.useCallback((id) => {
    _setStore(prev => {
      if (!prev.records[id]) return prev;
      const records = { ...prev.records };
      delete records[id];
      const fileIds = { ...prev.fileIdByRecordId };
      delete fileIds[id];
      let activeRecordId = prev.activeRecordId;
      if (activeRecordId === id) {
        const remaining = Object.keys(records);
        activeRecordId = remaining[0] || null;
        if (!activeRecordId) {
          const fresh = buildInitialConsultation();
          records[fresh._id] = fresh;
          activeRecordId = fresh._id;
        }
      }
      return { ...prev, records, fileIdByRecordId: fileIds, activeRecordId };
    });
  }, []);

  // Replace a record by id (used by sync to merge a remote download)
  const replaceRecord = React.useCallback((id, rec) => {
    _setStore(prev => ({
      ...prev,
      records: { ...prev.records, [id]: migrateRecord(rec) }
    }));
  }, []);

  // Reset = start a fresh record alongside any existing ones
  const reset = newRecord;

  // applyAutofill rebuilt for the multi-record world
  const applyAutofill = (autofill, opts = {}) => {
    updateActive(d => {
      const ranAt = nowIso();
      const newFields = { ...d.fields };
      const newMeta = { ...d._autofill_meta };
      Object.entries(autofill.fields || {}).forEach(([k, v]) => {
        if (v.value !== undefined && v.value !== null && v.value !== '') {
          newFields[k] = v.value;
          newMeta[k] = { confidence: v.confidence || 'med', accepted: false, ranAt };
        }
      });
      const incomingAi = autofill.aiResults || {};
      const newAi = { ...d.aiResults };
      Object.entries(incomingAi).forEach(([k, v]) => {
        if (k === 'statutory' || k === 'findings' || k === 'executive_summary') {
          newAi[k] = v;
        } else {
          newAi[k] = { ...v, ranAt, applied: true };
        }
      });
      return {
        ...d,
        fields: newFields,
        _autofill_meta: newMeta,
        _autofill_used: true,
        repeaters: { ...d.repeaters, ...(autofill.repeaters || {}) },
        aiResults: newAi,
        aiSummary: opts.summary !== undefined ? opts.summary : d.aiSummary,
        aiTranscript: opts.transcript !== undefined ? opts.transcript : d.aiTranscript
      };
    });
  };

  const data = store.records[store.activeRecordId] || null;

  // ---- Back-compat alias so existing components keep working ----
  // Old code called store.setData(updater) where store was the hook return.
  // Map it to updateActive for v2.
  const setData = updateActive;

  return {
    // Multi-record API
    store, setStore,
    data, setData,
    updateActive,
    newRecord, switchTo, removeRecord, replaceRecord,
    addRecord, convertBookingToConsultation,
    activeRecordId: store.activeRecordId,
    deviceId: store.deviceId,

    // Convenience helpers (v1 API preserved)
    setField, setMeta,
    clearAutofillForField, acceptAutofillForField,
    setRepeater, setDashboard,
    setAiSource, setAreaEvidence, setManualFindings,
    reset, replaceAll,
    applyAutofill,
    saveStatus
  };
}

// ---------------------------------------------------------------
// Section progress (unchanged — operates on a record)
// ---------------------------------------------------------------
function computeSectionStatus(section, data) {
  if (!data) return 'not-started';
  if (section.isDashboard) {
    const ai = data.aiResults || {};
    const areaIds = Object.keys(ai).filter(k => !['statutory','findings','executive_summary'].includes(k));
    const hasAreas = areaIds.length > 0;
    const f = ai.findings || {};
    const hasFindings = (f.strengths?.length || f.afd?.length || f.actions?.length);
    if (hasAreas && hasFindings) return 'complete';
    if (hasAreas || hasFindings) return 'in-progress';
    return 'not-started';
  }
  const totalFields = [];
  const filledFields = [];
  section.subs.forEach(sub => {
    (sub.fields || []).forEach(f => {
      if (f.type === 'repeater_quotes' || f.type === 'repeater_evidence' || f.type === 'repeater_workstreams') {
        const key = f.type.replace('repeater_', '');
        totalFields.push(f.id);
        if ((data.repeaters[key] || []).length) filledFields.push(f.id);
      } else {
        totalFields.push(f.id);
        const v = data.fields[f.id];
        if (Array.isArray(v) ? v.length : v) filledFields.push(f.id);
      }
    });
  });
  if (filledFields.length === 0) return 'not-started';
  if (filledFields.length === totalFields.length) return 'complete';
  return 'in-progress';
}

function computeOverallProgress(data) {
  if (!data) return 0;
  const sections = window.BROOK_SCHEMA.sections;
  let total = 0, done = 0;
  sections.forEach(s => {
    if (s.isDashboard) return;
    s.subs.forEach(sub => {
      (sub.fields || []).forEach(f => {
        total++;
        if (f.type.startsWith('repeater_')) {
          const key = f.type.replace('repeater_', '');
          if ((data.repeaters[key] || []).length) done++;
        } else {
          const v = data.fields[f.id];
          if (Array.isArray(v) ? v.length : (v !== '' && v !== undefined && v !== null)) done++;
        }
      });
    });
  });
  return total ? Math.round((done / total) * 100) : 0;
}

// ---------------------------------------------------------------
// Session timer — keyed off the active record so multiple sessions
// each show their own elapsed time. Click the timer to pause/resume.
// Pause state is persisted per-record in localStorage so the pause
// survives navigation, sync, and refresh.
// ---------------------------------------------------------------
const TIMER_PAUSE_KEY = 'brook_timer_pause_v1';

function _readTimerPauses() {
  try { return JSON.parse(localStorage.getItem(TIMER_PAUSE_KEY) || '{}') || {}; }
  catch { return {}; }
}
function _writeTimerPauses(p) {
  try { localStorage.setItem(TIMER_PAUSE_KEY, JSON.stringify(p)); } catch {}
}

function useSessionTimer(recordStartIso, recordId) {
  // Pause state shape per-record: { pausedAt: ms|null, accumulatedPausedMs: number }
  const [pauseState, setPauseState] = React.useState(() => {
    if (!recordId) return { pausedAt: null, accumulatedPausedMs: 0 };
    const all = _readTimerPauses();
    return all[recordId] || { pausedAt: null, accumulatedPausedMs: 0 };
  });
  const [elapsed, setElapsed] = React.useState(0);

  // Reload pause state when the active record changes
  React.useEffect(() => {
    if (!recordId) { setPauseState({ pausedAt: null, accumulatedPausedMs: 0 }); return; }
    const all = _readTimerPauses();
    setPauseState(all[recordId] || { pausedAt: null, accumulatedPausedMs: 0 });
  }, [recordId]);

  // Persist pause state whenever it changes
  React.useEffect(() => {
    if (!recordId) return;
    const all = _readTimerPauses();
    if (!pauseState.pausedAt && !pauseState.accumulatedPausedMs) {
      delete all[recordId];
    } else {
      all[recordId] = pauseState;
    }
    _writeTimerPauses(all);
  }, [recordId, pauseState]);

  React.useEffect(() => {
    if (!recordStartIso) { setElapsed(0); return; }
    const start = Date.parse(recordStartIso) || Date.now();
    const tick = () => {
      const now = Date.now();
      // Subtract historic pauses + the current (ongoing) pause if paused.
      let pausedMs = pauseState.accumulatedPausedMs || 0;
      if (pauseState.pausedAt) pausedMs += (now - pauseState.pausedAt);
      setElapsed(Math.max(0, Math.floor((now - start - pausedMs) / 1000)));
    };
    tick();
    // While paused, the value doesn't change — slower tick saves cycles.
    const id = setInterval(tick, pauseState.pausedAt ? 5000 : 1000);
    return () => clearInterval(id);
  }, [recordStartIso, pauseState]);

  const toggle = React.useCallback(() => {
    setPauseState(prev => {
      const now = Date.now();
      if (prev.pausedAt) {
        // Resume: roll the in-progress pause into accumulated, clear pausedAt.
        return {
          pausedAt: null,
          accumulatedPausedMs: (prev.accumulatedPausedMs || 0) + (now - prev.pausedAt)
        };
      }
      // Pause: stamp pausedAt.
      return { pausedAt: now, accumulatedPausedMs: prev.accumulatedPausedMs || 0 };
    });
  }, []);

  const reset = React.useCallback(() => {
    setPauseState({ pausedAt: null, accumulatedPausedMs: 0 });
  }, []);

  const fmt = (s) => {
    const h = Math.floor(s / 3600);
    const m = Math.floor((s % 3600) / 60);
    const sec = s % 60;
    if (h) return `${h}:${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`;
    return `${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`;
  };

  return {
    elapsed,
    formatted: fmt(elapsed),
    paused: !!pauseState.pausedAt,
    toggle,
    reset
  };
}

// ---------------------------------------------------------------
// Quick capture
// ---------------------------------------------------------------
function useQuickCapture() {
  const [items, setItems] = React.useState(() => safeRead(QUICK_CAPTURE_KEY, []));
  React.useEffect(() => { safeWrite(QUICK_CAPTURE_KEY, items); }, [items]);

  const add = (text) => {
    if (!text.trim()) return;
    setItems(prev => [{ id: uid(), text: text.trim(), created: Date.now() }, ...prev]);
  };
  const remove = (id) => setItems(prev => prev.filter(x => x.id !== id));
  const clear = () => setItems([]);
  return { items, add, remove, clear };
}

// ---------------------------------------------------------------
// Consultation log — v2 derives from the store's records map directly
// so every record is visible in the Log without a separate upsert step.
// Kept as a hook for compatibility; status comes from record.status.
// ---------------------------------------------------------------
function useConsultationLog(store) {
  // Derive log entries directly from the records in the store. Each entry
  // mirrors the v1 shape plus the v2 status, claimedBy, and updatedAt.
  // Bookings (kind='booking') and consultations (kind='consultation') both surface here.
  const log = React.useMemo(() => {
    if (!store || !store.records) return [];
    return Object.values(store.records)
      .map(r => {
        if (r.kind === 'booking') {
          const b = r.booking || {};
          return {
            id: r._id,
            recordId: r._id,
            kind: 'booking',
            org_name: b.business_name || '(unnamed booking)',
            client_name: b.contact_name || '',
            consultant_name: '',
            org_type: b.business_type || '',
            consultation_date: (b.slot_iso || '').slice(0, 10),
            slot_iso: b.slot_iso || '',
            slot_label: b.slot_label || '',
            preferred_zone: b.preferred_zone || '',
            lead_priority: '',
            services: [],
            next_steps_due: '',
            autofill_used: false,
            status: r.status || 'booked',
            claimedBy: r.claimedBy || null,
            updatedAt: r.updatedAt || r._started_at,
            fileId: store.fileIdByRecordId?.[r._id] || null
          };
        }
        const f = r.fields || {};
        return {
          id: r._id,
          recordId: r._id,
          kind: 'consultation',
          org_name: f.org_name || '(unnamed organisation)',
          client_name: f.client_contact_name || '',
          consultant_name: f.consultant_name || '',
          org_type: f.org_type || '',
          consultation_date: f.consultation_date || '',
          lead_priority: f.lead_priority || '',
          services: f.recommended_services || [],
          next_steps_due: f.proposal_deadline || '',
          autofill_used: !!r._autofill_used,
          status: r.status || 'in_progress',
          claimedBy: r.claimedBy || null,
          updatedAt: r.updatedAt || r._started_at,
          fileId: store.fileIdByRecordId?.[r._id] || null,
          audio: r.audio || null
        };
      })
      .sort((a, b) => (b.updatedAt || '').localeCompare(a.updatedAt || ''));
  }, [store]);

  return { log };
}

// ---------------------------------------------------------------
// PIN
// ---------------------------------------------------------------
function usePinGate() {
  const [unlocked, setUnlocked] = React.useState(() => sessionStorage.getItem('brook_unlocked') === '1');
  const tryUnlock = (pin) => {
    const stored = safeRead(PIN_KEY, DEFAULT_PIN);
    if (pin === stored) {
      sessionStorage.setItem('brook_unlocked', '1');
      setUnlocked(true);
      return true;
    }
    return false;
  };
  const lock = () => { sessionStorage.removeItem('brook_unlocked'); setUnlocked(false); };
  return { unlocked, tryUnlock, lock };
}

// ---------------------------------------------------------------
// Export / Import — single-record envelope (unchanged from v1)
// ---------------------------------------------------------------
const BROOK_APP_KEY = 'brook-consultancy';
const BROOK_EXPORT_VERSION = 1;

function buildExportEnvelope(data, branding = {}) {
  return {
    kind: 'consultation',
    app: BROOK_APP_KEY,
    version: BROOK_EXPORT_VERSION,
    exportedAt: nowIso(),
    branding: {
      consultancyName: branding.consultancyName || 'Brook Consultancy Partners Ltd',
      consultantName: branding.consultantName || data.fields?.consultant_name || ''
    },
    consultation: {
      id: data._id,
      createdAt: data._started_at,
      updatedAt: data.updatedAt || nowIso(),
      status: data.status || 'in_progress',
      claimedBy: data.claimedBy || null,
      autofillUsed: !!data._autofill_used,
      setting: {
        organisationName: data.fields?.org_name || '',
        organisationType: data.fields?.org_type || '',
        region: data.fields?.la_region || '',
        clientContactName: data.fields?.client_contact_name || '',
        clientContactEmail: data.fields?.client_contact_email || '',
        clientContactPhone: data.fields?.client_contact_phone || '',
        consultationDate: data.fields?.consultation_date || '',
        consultationMethod: data.fields?.consultation_method || '',
        consultantName: data.fields?.consultant_name || ''
      },
      fields: data.fields || {},
      autofillMeta: data._autofill_meta || {},
      repeaters: data.repeaters || {},
      areas: data.areas || {},
      findings: data.findings || { strengths: [], afd: [], actions: [] },
      aiSummary: data.aiSummary || '',
      aiTranscript: data.aiTranscript || '',
      aiResults: data.aiResults || {}
    }
  };
}

function parseImportEnvelope(envelope) {
  if (!envelope || envelope.kind !== 'consultation' || envelope.app !== BROOK_APP_KEY) {
    throw new Error('This file is not a Brook Consultancy export (kind/app mismatch).');
  }
  const c = envelope.consultation || {};
  const data = {
    _id: c.id || uuid(),
    _started_at: c.createdAt || nowIso(),
    _autofill_used: !!c.autofillUsed,
    _autofill_meta: c.autofillMeta || {},
    status: c.status || 'in_progress',
    claimedBy: c.claimedBy || null,
    updatedAt: c.updatedAt || nowIso(),
    fields: c.fields || {},
    repeaters: c.repeaters || { quotes: [], evidence: [], workstreams: [] },
    areas: c.areas || {},
    findings: c.findings || { strengths: [], afd: [], actions: [] },
    aiSummary: c.aiSummary || '',
    aiTranscript: c.aiTranscript || '',
    aiResults: c.aiResults || {
      executive_summary: { overall_assessment: '', key_risks: [], immediate_priorities: [] },
      statutory: { findings: [] },
      findings: { strengths: [], afd: [], actions: [] }
    }
  };
  return migrateRecord(data);
}

function downloadExport(data, branding) {
  const env = buildExportEnvelope(data, branding);
  const orgSlug = (data.fields?.org_name || 'consultation')
    .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'consultation';
  const dateSlug = (data.fields?.consultation_date || new Date().toISOString().slice(0,10));
  const filename = `${orgSlug}-${dateSlug}.consultation.json`;
  const blob = new Blob([JSON.stringify(env, null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename;
  document.body.appendChild(a); a.click();
  setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 0);
  return filename;
}

// Status pill helpers used by the Consultation Log + Operator Queue
const STATUS_META = {
  // Consultation statuses
  in_progress:         { label: 'In progress',         color: '#1e6091', bg: '#e2eef7' },
  ready_for_analysis:  { label: 'Ready for analysis',  color: '#854d0e', bg: '#fef3c7' },
  analysing:           { label: 'Analysing',           color: '#0d9488', bg: '#ccfbf1' },
  complete:            { label: 'Complete',            color: '#15803d', bg: '#dcfce7' },
  archived:            { label: 'Archived',            color: '#64748b', bg: '#f1f5f9' },
  // Booking statuses
  booked:              { label: 'Booked',              color: '#4338ca', bg: '#e0e7ff' },
  arrived:             { label: 'Arrived',             color: '#0d9488', bg: '#ccfbf1' },
  converted:           { label: 'Converted',           color: '#15803d', bg: '#dcfce7' },
  no_show:             { label: 'No-show',             color: '#64748b', bg: '#f1f5f9' },
  cancelled:           { label: 'Cancelled',           color: '#c0392b', bg: '#fee2e2' }
};

function statusMeta(s) { return STATUS_META[s] || STATUS_META.in_progress; }

// Expose
Object.assign(window, {
  useConsultation,
  useSessionTimer,
  useQuickCapture,
  useConsultationLog,
  usePinGate,
  computeSectionStatus,
  computeOverallProgress,
  STORE_KEY,
  STORAGE_KEY: STORE_KEY, // back-compat alias
  brookUid: uid,
  brookUuid: uuid,
  buildExportEnvelope,
  parseImportEnvelope,
  downloadExport,
  BROOK_APP_KEY,
  BROOK_EXPORT_VERSION,
  migrateConsultationState: migrateRecord,
  statusMeta,
  STATUS_META,
  brookGetDeviceId: getOrCreateDeviceId,
  buildInitialBooking,
  mapBusinessTypeToOrgType
});
