// rwic-state.jsx — App state + mock data for the RWIC tablet.
// RWIC-first: only the employee shift data is the "real" log. Contractor
// info is captured as compliance evidence (signatures, attendance) only.

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

// ── helpers ───────────────────────────────────────────────────
const uid = (() => { let n = 1; return (p = 'id') => `${p}_${n++}_${Date.now().toString(36).slice(-4)}`; })();
const todayISO = (o = 0) => { const d = new Date(); d.setDate(d.getDate() + o); return d.toISOString().slice(0, 10); };
const nowHM = () => { const d = new Date(); return d.toTimeString().slice(0, 5); };
const todayAt = (h, m = 0, off = 0) => { const d = new Date(); d.setDate(d.getDate() + off); d.setHours(h, m, 0, 0); return d.toISOString(); };
const hoursBetween = (start, end) => {
  if (!start) return 0;
  const a = new Date(start).getTime();
  const b = end ? new Date(end).getTime() : Date.now();
  return Math.max(0, (b - a) / 3600000);
};
const fmtHours = (h) => { const hr = Math.floor(h); const min = Math.floor((h - hr) * 60); return `${hr}h ${min.toString().padStart(2, '0')}m`; };
const fmtClock = (h) => {
  const hr = Math.floor(h); const min = Math.floor((h - hr) * 60); const sec = Math.floor(((h - hr) * 60 - min) * 60);
  return `${hr.toString().padStart(2, '0')}:${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
};
const fmtTime = (iso) => iso ? new Date(iso).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }) : '—';
const fmtDateLong = (iso) => {
  if (!iso) return '—';
  const d = new Date(iso + (iso.length === 10 ? 'T00:00' : ''));
  return d.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' });
};

// Time helpers for timecard
const calcMins = (start, end) => {
  if (!start || !end) return null;
  const [sh, sm] = start.split(':').map(Number);
  const [eh, em] = end.split(':').map(Number);
  let m = (eh * 60 + em) - (sh * 60 + sm);
  if (m < 0) m += 1440;
  return m;
};
const minsToDecimal = (m) => m == null ? null : +(m / 60).toFixed(2);

// ── RWIC employee (single user — this is their tablet) ──────────
const RWIC_USER = {
  id: 'rwic-2241',
  name: 'Marcus Hale',
  initials: 'MH',
  phone: '(406) 555-0142',
  certification: 'RWIC · NS RWP · CSX FCI',
  certExpires: '2026-09-14',
  badge: 'FLG-2241',
  pin: '4827',
};

// ── Master / diagnostic backdoor ─────────────────────────────────
// Total access, but every action is recorded in the audit trail and
// surfaced in the UI. Designed for support staff, internal QA, and
// regulator-visible diagnostic sessions — never for production use as
// a personal account.
const MASTER_PIN = '8282';
const MASTER_USER = {
  id: 'master-diag',
  name: 'Master Diagnostic',
  initials: 'MX',
  phone: '—',
  certification: 'BACKDOOR · ALL ACCESS · TRACKED',
  certExpires: '—',
  badge: 'MASTER',
  pin: MASTER_PIN,
  isMaster: true,
};

// ── Activity codes — RFL letter-prefix series ─────────────────
// F = Field, T = Training, M = Management, A = Admin, L = Leave, V = Travel.
// 10-increment spacing leaves room for new codes between existing ones
// without disrupting the sequence.
const ACTIVITY_CODES = [
  // Field — RFL-F
  { code: 'F10', label: 'RWIC — Railroad Flagger',           group: 'Field',      short: 'Flagger',    isDefault: true },
  { code: 'F20', label: 'RWIC — Supervisor / Manager',       group: 'Field',      short: 'Supervisor' },
  { code: 'F30', label: 'Track Inspector',                   group: 'Field',      short: 'Inspector' },
  { code: 'F40', label: 'Observer / Construction Inspector', group: 'Field',      short: 'Observer' },
  { code: 'F50', label: 'Field Supervision',                 group: 'Field',      short: 'Field Sup.' },
  { code: 'F60', label: 'CSX FCI',                           group: 'Field',      short: 'FCI' },

  // Training — RFL-T
  { code: 'T10', label: 'Contractor Orientation',            group: 'Training',   short: 'Orientation' },
  { code: 'T20', label: 'Watchman / Lookout',                group: 'Training',   short: 'Watchman' },
  { code: 'T30', label: 'RWP FRA 214',                       group: 'Training',   short: 'RWP 214' },
  { code: 'T40', label: 'Manager On-site RWIC',              group: 'Training',   short: 'Mgr RWIC' },
  { code: 'T50', label: 'Safety Audit',                      group: 'Training',   short: 'Safety Audit' },
  { code: 'T60', label: 'NS RWP',                            group: 'Training',   short: 'NS RWP' },
  { code: 'T70', label: 'EIC Training',                      group: 'Training',   short: 'EIC' },
  { code: 'T80', label: 'Classroom / On-site',               group: 'Training',   short: 'Classroom' },

  // Management — RFL-M
  { code: 'M10', label: 'Project Manager',                   group: 'Management', short: 'PM' },
  { code: 'M20', label: 'Project Engineer',                  group: 'Management', short: 'PE' },
  { code: 'M30', label: 'Supervisor Safety Audit',           group: 'Management', short: 'Sup. Audit' },

  // Admin — RFL-A
  { code: 'A10', label: 'Clerical',                          group: 'Admin',      short: 'Clerical' },
  { code: 'A20', label: 'Scheduling',                        group: 'Admin',      short: 'Scheduling' },
  { code: 'A30', label: 'Timesheet / Report Review',         group: 'Admin',      short: 'Timesheets' },
  { code: 'A40', label: 'Administration',                    group: 'Admin',      short: 'Admin' },
  { code: 'A50', label: 'Correspondence',                    group: 'Admin',      short: 'Email' },
  { code: 'A60', label: 'Marketing',                         group: 'Admin',      short: 'Marketing' },
  { code: 'A70', label: 'Proposals',                         group: 'Admin',      short: 'Proposals' },
  { code: 'A80', label: 'Recon',                             group: 'Admin',      short: 'Recon' },
  { code: 'A90', label: 'Errands',                           group: 'Admin',      short: 'Errands' },

  // Travel — RFL-V
  { code: 'V10', label: 'Travel Time',                       group: 'Travel',     short: 'Travel' },

  // Leave — RFL-L
  { code: 'L10', label: 'Vacation',                          group: 'Leave',      short: 'Vacation',  nonWorking: true },
  { code: 'L20', label: 'Holiday',                           group: 'Leave',      short: 'Holiday',   nonWorking: true },
  { code: 'L30', label: 'Sick',                              group: 'Leave',      short: 'Sick',      nonWorking: true },
  { code: 'L40', label: 'Unpaid Leave',                      group: 'Leave',      short: 'Unpaid',    nonWorking: true },
  { code: 'L50', label: 'Funeral Leave',                     group: 'Leave',      short: 'Funeral',   nonWorking: true },
  { code: 'L60', label: 'Paid Time Off',                     group: 'Leave',      short: 'PTO',       nonWorking: true },
];

const GROUP_ORDER  = ['Field', 'Training', 'Management', 'Admin', 'Travel', 'Leave'];
const GROUP_PREFIX = { Field: 'F', Training: 'T', Management: 'M', Admin: 'A', Travel: 'V', Leave: 'L' };
const GROUP_COLORS = {
  Field:      'var(--accent)',    // safety orange — primary field work
  Training:   '#1D6FB8',          // rail blue
  Management: '#5B3FB8',          // indigo
  Admin:      '#6F6F6F',          // graphite
  Travel:     '#B45309',          // amber/rust
  Leave:      '#137C50',          // signal green
};
const codeByValue = (c) => ACTIVITY_CODES.find(t => t.code === c);

// ── Domain options ─────────────────────────────────────────────
const RAILROADS = ['UPRR', 'BNSF', 'CSX', 'NS', 'KCS', 'CN', 'CP', 'Short Line', 'Other'];
const WEATHER = ['Clear', 'Cloudy - Partial', 'Cloudy - Heavy', 'Rain - Light', 'Rain - Heavy', 'Fog', 'Snow', 'High Wind', 'Extreme Heat'];
const OTS_TYPES = [
  'Form B / C / Y',
  'Watchman / Lookout',
  'Track Out of Service',
  'Track Warrant / Authority',
  'Track & Time',
  'Derails / Lockout',
  '704 / 707 Authority',
  'Individual Train Detection',
];

// ── Today's job — a single active assignment ───────────────────
const TODAY_JOB = {
  projectNumber: 'N-0224.3.014.02',
  projectName: 'Mainline Tie Renewal — Cascade Sub',
  railroad: 'BNSF',
  subdivision: 'Cascade Sub',
  milepost: '142.3 – 143.1',
  location: 'Skykomish, WA',
  workType: 'Replace 280 ties between MP 142.3 – 142.9. Maintain 10 mph slow order on Track 1.',
  contractor: {
    company: 'Northwest Track Construction',
    foreman: 'D. Reyes',
    foremanPhone: '(425) 555-0188',
    peopleOnSite: 8,
    equipOnSite: 3,
  },
  scheduled: { start: '07:00', end: '17:00' },
  expectedTrains: [
    { train: 'Q-EVELAU-12', dir: 'EB', eta: '07:45' },
    { train: 'M-SEAPSC-14', dir: 'WB', eta: '10:20' },
    { train: 'AMTK 7',      dir: 'EB', eta: '13:55' },
  ],
};

// ── Crew on site (contractor — compliance evidence only) ───────
// These are NOT employees of the flagging co. They sign the briefing as
// proof of attendance. RWIC certifies the briefing happened.
const CONTRACTOR_CREW = [
  { id: 'c1', name: 'Eric Diaz',     company: 'Northwest Track Construction', trainingExp: '2026-04-12', signed: true,  signedAt: todayAt(6, 28) },
  { id: 'c2', name: 'Anthony Kim',   company: 'Northwest Track Construction', trainingExp: '2026-08-01', signed: true,  signedAt: todayAt(6, 30) },
  { id: 'c3', name: 'Brooke Ellis',  company: 'Northwest Track Construction', trainingExp: '2025-11-08', signed: true,  signedAt: todayAt(6, 31) },
  { id: 'c4', name: 'Mike Halverson', company: 'Pacific Rail Welding',         trainingExp: '2026-02-18', signed: false, signedAt: null },
  { id: 'c5', name: 'Janet Wu',      company: 'Pacific Rail Welding',         trainingExp: '2027-01-22', signed: true,  signedAt: todayAt(6, 33) },
];

// ── Recent history (employee shift records) ──────────────────
const HISTORY = [
  { date: todayISO(-1), hours: 10.8, job: 'JOB-24179 · Cascade Sub MP 142.3', code: 'F10', ot: 2.8, status: 'submitted', gps: true },
  { date: todayISO(-2), hours: 11.2, job: 'JOB-24179 · Cascade Sub MP 142.3', code: 'F10', ot: 3.2, status: 'approved',  gps: true },
  { date: todayISO(-3), hours: 9.1,  job: 'JOB-24179 · Cascade Sub MP 142.3', code: 'F10', ot: 1.1, status: 'approved',  gps: true },
  { date: todayISO(-4), hours: 12.2, job: 'JOB-24166 · Stampede Sub MP 67.4', code: 'F30', ot: 4.2, status: 'approved',  gps: true },
  { date: todayISO(-5), hours: 8.0,  job: '— Holiday',                        code: 'L20', ot: 0,   status: 'approved',  gps: false },
];

// ── Provider ───────────────────────────────────────────────────
const AppCtx = createContext(null);

function AppProvider({ children }) {
  const [now, setNow] = useState(Date.now());
  useEffect(() => { const t = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(t); }, []);

  // Auth state — 'setup' → 'login' → 'ready'.
  // For demo, jump straight to 'login' since setup completed.
  const [authStage, setAuthStage] = useState('login');
  const [user, setUser] = useState(null); // RWIC_USER once authed

  // The shared shift context — fields auto-flow between Briefing / Report / Timecard
  // Pre-seeded with today's job for the demo.
  const [shift, setShift] = useState(() => ({
    date: todayISO(),
    time: nowHM(),
    flagmanName: RWIC_USER.name,
    flagmanPhone: RWIC_USER.phone,
    flagmanBadge: RWIC_USER.badge,
    railroad: TODAY_JOB.railroad,
    subdivision: TODAY_JOB.subdivision,
    milepost: TODAY_JOB.milepost,
    location: TODAY_JOB.location,
    projectNumber: TODAY_JOB.projectNumber,
    projectName: TODAY_JOB.projectName,
    workType: TODAY_JOB.workType,
    weather: 'Clear',
    projectStart: TODAY_JOB.scheduled.start,
    projectEnd: TODAY_JOB.scheduled.end,
    foremanName: TODAY_JOB.contractor.foreman,
    foremanPhone: TODAY_JOB.contractor.foremanPhone,
    contractorCompany: TODAY_JOB.contractor.company,
    peopleOnSite: TODAY_JOB.contractor.peopleOnSite,
    equipOnSite: TODAY_JOB.contractor.equipOnSite,
    otsProvided: 'Yes',
    otsTypes: { 'Form B / C / Y': true, 'Watchman / Lookout': true },
    otsItems: [{ id: 1, itemNum: '4814', tracks: 'Main 1', limits: 'MP 142.3 – MP 143.1' }],
    ppos: '25 ft from track on north shoulder',
    erLocation: 'Gravel turnout at MP 142.5',
    traumaCenter: 'EvergreenHealth Monroe — 14701 179th SE, Monroe WA · 25 min',
    caller911: 'D. Reyes (foreman)',
    evEscort: 'M. Hale (RWIC)',
    cprPerson: 'M. Hale',
    cprPhone: '911',
    firstAidKit: 'RWIC truck — right rear seat',
    toolsPPE: 'No',
    hazards: 'Yes',
    risks: {
      adjTrack: 'Yes', occupiedTrack: 'No', ots: 'Yes',
      crossing: 'No', equipment: 'Yes', cranes: 'No',
      bridge: 'No', cables: 'No', otherRisk: 'No',
    },
    riskPlan: 'Briefing on adjacent-track exposure, OTS protection types in effect, and equipment swing radius. PPOS confirmed before fouling.',
  }));

  const updateShift = useCallback((patch) => setShift(s => ({ ...s, ...patch })), []);

  // ── Shift clock — single source of truth for "on duty" status ──
  const [clockState, setClockState] = useState({
    status: 'off',          // 'off' | 'on' | 'break'
    startedAt: null,        // ISO when shift opened
    breakStartedAt: null,
    breakMinutes: 0,
    currentCode: 'F10',
    activityLog: [],        // closed segments for today
  });

  const clockIn = useCallback((code = 'F10') => {
    setClockState({
      status: 'on',
      startedAt: new Date().toISOString(),
      breakStartedAt: null,
      breakMinutes: 0,
      currentCode: code,
      activityLog: [],
    });
    toast('Shift started · GPS captured', 'success');
  }, []);

  const clockOut = useCallback(() => {
    setClockState(cs => {
      const closed = cs.startedAt
        ? [...cs.activityLog, { code: cs.currentCode, start: cs.startedAt, end: new Date().toISOString() }]
        : cs.activityLog;
      return { ...cs, status: 'off', activityLog: closed };
    });
    toast('Shift ended · timecard ready to submit', 'success');
  }, []);

  const toggleBreak = useCallback(() => {
    setClockState(cs => {
      if (cs.status === 'on') {
        return { ...cs, status: 'break', breakStartedAt: new Date().toISOString() };
      }
      if (cs.status === 'break') {
        const mins = (Date.now() - new Date(cs.breakStartedAt).getTime()) / 60000;
        return { ...cs, status: 'on', breakStartedAt: null, breakMinutes: cs.breakMinutes + Math.round(mins) };
      }
      return cs;
    });
  }, []);

  const switchCode = useCallback((newCode) => {
    setClockState(cs => {
      if (cs.status === 'off' || !cs.startedAt) return cs;
      const closed = [...cs.activityLog, { code: cs.currentCode, start: cs.startedAt, end: new Date().toISOString() }];
      return { ...cs, currentCode: newCode, startedAt: new Date().toISOString(), activityLog: closed };
    });
    const ac = codeByValue(newCode);
    toast(`Activity switched to ${newCode} · ${ac?.short || ac?.label}`, 'success');
  }, []);

  // ── Briefing completion state ─────────────────────────────────
  const [briefingSubmitted, setBriefingSubmitted] = useState(false);
  const [briefingSubmittedAt, setBriefingSubmittedAt] = useState(null);
  const [briefingSigCount, setBriefingSigCount] = useState(
    CONTRACTOR_CREW.filter(c => c.signed).length
  );

  const submitBriefing = useCallback((payload) => {
    setBriefingSubmitted(true);
    setBriefingSubmittedAt(new Date().toISOString());
    const rec = payload && {
      id: `bf-tablet-${payload.employeeBadge}-${payload.date}`,
      kind: 'briefing',
      employeeBadge: payload.employeeBadge,
      projectNumber: payload.projectNumber,
      date: payload.date,
      sigCount: payload.sigCount,
      crewTotal: payload.crewTotal,
      riskFlags: payload.riskFlags,
      otsTypes: payload.otsTypes,
      submittedAt: new Date().toISOString(),
    };
    // Always push to localStorage (console reads from there)
    pushToConsole('rfp_submitted_briefings', rec);
    // Also write to Supabase when available
    if (rec && window._rfpSupabase && window.RFP && RFP.session()) {
      const sess = RFP.session();
      window._rfpSupabase.from("briefings").insert({
        job_id: null, // resolved by project number on console side
        submitted_by: sess.empId || null,
        briefing_date: payload.date,
        content: rec,
        status: 'submitted',
        company_id: sess.companyId || null,
      }).then(({ error }) => {
        if (error) console.warn("Briefing Supabase write:", error.message);
      });
    }
    toast('Job briefing certified · sent to Safety for review', 'success');
  }, []);

  // ── Field report completion ──────────────────────────────────
  const [reportSubmitted, setReportSubmitted] = useState(false);
  const [reportSubmittedAt, setReportSubmittedAt] = useState(null);
  const submitReport = useCallback((payload) => {
    setReportSubmitted(true);
    setReportSubmittedAt(new Date().toISOString());
    const rec = payload && {
      id: `fr-tablet-${payload.employeeBadge}-${payload.date}`,
      kind: 'report',
      employeeBadge: payload.employeeBadge,
      projectNumber: payload.projectNumber,
      date: payload.date,
      photoCount: payload.photoCount,
      trainCount: payload.trainCount,
      hours: payload.hours,
      violations: payload.violations,
      submittedAt: new Date().toISOString(),
    };
    // Always push to localStorage (console reads from there)
    pushToConsole('rfp_submitted_reports', rec);
    // Also write to Supabase when available
    if (rec && window._rfpSupabase && window.RFP && RFP.session()) {
      const sess = RFP.session();
      window._rfpSupabase.from("daily_field_reports").insert({
        job_id: null, // resolved by project number on console side
        employee_id: sess.empId || null,
        report_date: payload.date,
        weather: payload.weather || 'Clear',
        temperature: '',
        work_performed: '',
        personnel_on_site: [],
        equipment_used: [],
        incidents: null,
        photos: [],
        submitted_at: new Date().toISOString(),
        company_id: sess.companyId || null,
      }).then(({ error }) => {
        if (error) console.warn("DFR Supabase write:", error.message);
      });
    }
    toast('Daily field report submitted · sent to Safety for review', 'success');
  }, []);

  // ── Timecard completion ──────────────────────────────────────
  const [timecardSubmitted, setTimecardSubmitted] = useState(false);
  const [timecardSubmittedAt, setTimecardSubmittedAt] = useState(null);
  const submitTimecard = useCallback((payload) => {
    setTimecardSubmitted(true);
    setTimecardSubmittedAt(new Date().toISOString());
    if (payload) {
      // Always write to localStorage (console reads from there, polling every 4s)
      try {
        const KEY = 'rfp_submitted_timecards';
        const prev = JSON.parse(localStorage.getItem(KEY) || '[]');
        const rec = {
          id: `tc-tablet-${payload.employeeBadge}-${payload.date}`,
          employeeBadge: payload.employeeBadge,
          projectNumber: payload.projectNumber,
          date: payload.date,
          blocks: payload.blocks,
          submittedAt: new Date().toISOString(),
        };
        const next = [rec, ...prev.filter(r => r.id !== rec.id)].slice(0, 50);
        localStorage.setItem(KEY, JSON.stringify(next));
      } catch (e) { /* storage unavailable — non-fatal */ }

      // Also write to Supabase when available
      if (window._rfpSupabase && window.RFP && RFP.session()) {
        const sess = RFP.session();
        const totalHrs = (payload.blocks || []).reduce((s, b) => {
          if (!b.start || !b.end) return s;
          const sm = b.start.split(':').map(Number); const em = b.end.split(':').map(Number);
          let m = (em[0] * 60 + em[1]) - (sm[0] * 60 + sm[1]);
          if (m < 0) m += 1440;
          return s + m / 60;
        }, 0);
        const regularHrs = Math.min(8, totalHrs);
        const otHrs = Math.max(0, totalHrs - 8);
        const stateAlloc = (payload.blocks || []).map(b => ({
          state: b.state || 'TX',
          hours: (() => {
            if (!b.start || !b.end) return 0;
            const sm = b.start.split(':').map(Number); const em = b.end.split(':').map(Number);
            let m = (em[0] * 60 + em[1]) - (sm[0] * 60 + sm[1]);
            if (m < 0) m += 1440; return +(m / 60).toFixed(2);
          })(),
          rate: 0,
        }));

        window._rfpSupabase.from("timecards").insert({
          employee_id: sess.empId || null,
          job_id: null, // resolved by projectNumber on console side
          work_date: payload.date,
          time_in: payload.blocks && payload.blocks[0] ? payload.blocks[0].start : null,
          time_out: payload.blocks && payload.blocks[payload.blocks.length - 1] ? payload.blocks[payload.blocks.length - 1].end : null,
          rfl_activity_code: payload.blocks && payload.blocks[0] ? payload.blocks[0].code : 'F10',
          regular_hours: +regularHrs.toFixed(2),
          overtime_hours: +otHrs.toFixed(2),
          doubletime_hours: 0,
          state_allocation: stateAlloc,
          rrb_rrta_exempt: false,
          status: 'submitted',
          submitted_at: new Date().toISOString(),
          company_id: sess.companyId || null,
        }).then(({ error }) => {
          if (error) console.warn("Timecard Supabase write:", error.message);
        });
      }
    }
    toast('Timecard submitted · routed to manager for approval', 'success');
  }, []);

  // ── Toast queue ──────────────────────────────────────────────
  const [toasts, setToasts] = useState([]);
  const toast = useCallback((text, kind = 'info', ms = 2800) => {
    const id = uid('t');
    setToasts(t => [...t, { id, text, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), ms);
  }, []);

  // ── Master-mode audit trail ────────────────────────────────────
  // Every meaningful action taken while the diagnostic backdoor PIN
  // is active is appended here. The trail is visible inside the app,
  // and (in production) would be streamed to the audit server.
  const [auditTrail, setAuditTrail] = useState([]);
  const audit = useCallback((event, detail) => {
    setAuditTrail(t => [{
      id: uid('audit'),
      ts: new Date().toISOString(),
      event,
      detail: detail || '',
    }, ...t].slice(0, 200));
  }, []);

  // Live shift duration (hours)
  const liveHrs = useMemo(() => {
    if (clockState.status === 'off' || !clockState.startedAt) return 0;
    return hoursBetween(clockState.startedAt) - (clockState.breakMinutes / 60);
  }, [clockState, now]);

  // ── Auth actions ─────────────────────────────────────────────
  const login = useCallback((pin) => {
    if (pin === MASTER_PIN) {
      setUser(MASTER_USER);
      setAuthStage('ready');
      audit('LOGIN_MASTER', 'Diagnostic backdoor PIN used — full access granted, all activity recorded');
      toast('Master access · all activity is tracked', 'warn', 4200);
      return true;
    }
    // Try Supabase PIN lookup if available (async path)
    if (window.RFP && RFP.loginPinAsync) {
      RFP.loginPinAsync(pin, 'tablet').then(res => {
        if (res.ok) {
          // Map the session back to a RWIC_USER-like object
          const s = res.session;
          setUser({
            id: s.userId || s.empId || 'rwic-live',
            name: s.name || 'RWIC',
            initials: (s.name || 'RW').split(/\s+/).map(p => p[0]).join('').slice(0, 2).toUpperCase(),
            phone: '',
            certification: 'RWIC',
            certExpires: '—',
            badge: s.empId || 'RWIC',
            pin,
          });
          setAuthStage('ready');
        }
        // If async fails, fall through (login() already returned false below)
      }).catch(() => {});
    }
    // Synchronous local PIN check (prototype mode / offline)
    if (pin === RWIC_USER.pin) {
      setUser(RWIC_USER);
      setAuthStage('ready');
      return true;
    }
    return false;
  }, [audit, toast]);
  const logout = useCallback(() => {
    setUser(curr => {
      if (curr?.isMaster) {
        audit('LOGOUT_MASTER', 'Diagnostic session ended');
      }
      return null;
    });
    setAuthStage('login');
  }, [audit]);

  // Wrap submit + clock actions so they audit when called by master
  const isMaster = !!user?.isMaster;
  const wrap = (label, fn) => (...args) => {
    if (isMaster) audit(label, args[0] ? JSON.stringify(args[0]).slice(0, 120) : '');
    return fn(...args);
  };

  const auditedClockIn      = wrap('CLOCK_IN', clockIn);
  const auditedClockOut     = wrap('CLOCK_OUT', clockOut);
  const auditedToggleBreak  = wrap('TOGGLE_BREAK', toggleBreak);
  const auditedSwitchCode   = wrap('SWITCH_ACTIVITY', switchCode);
  const auditedSubmitBrief  = wrap('SUBMIT_BRIEFING', submitBriefing);
  const auditedSubmitReport = wrap('SUBMIT_REPORT', submitReport);
  const auditedSubmitTC     = wrap('SUBMIT_TIMECARD', submitTimecard);
  const auditedUpdateShift  = wrap('SHIFT_FIELD_EDIT', updateShift);

  const value = useMemo(() => ({
    now,
    authStage, setAuthStage, user, login, logout,
    isMaster,
    auditTrail, audit,
    job: TODAY_JOB,
    contractorCrew: CONTRACTOR_CREW,
    history: HISTORY,
    shift,
    updateShift: isMaster ? auditedUpdateShift : updateShift,
    clockState,
    clockIn:      isMaster ? auditedClockIn      : clockIn,
    clockOut:     isMaster ? auditedClockOut     : clockOut,
    toggleBreak:  isMaster ? auditedToggleBreak  : toggleBreak,
    switchCode:   isMaster ? auditedSwitchCode   : switchCode,
    liveHrs,
    briefingSubmitted, briefingSubmittedAt, briefingSigCount,
    submitBriefing: isMaster ? auditedSubmitBrief : submitBriefing,
    reportSubmitted, reportSubmittedAt,
    submitReport: isMaster ? auditedSubmitReport : submitReport,
    timecardSubmitted, timecardSubmittedAt,
    submitTimecard: isMaster ? auditedSubmitTC : submitTimecard,
    toasts, toast,
  }), [now, authStage, user, login, logout, isMaster, auditTrail, audit,
       shift, updateShift, auditedUpdateShift,
       clockState, clockIn, clockOut, toggleBreak, switchCode,
       auditedClockIn, auditedClockOut, auditedToggleBreak, auditedSwitchCode, liveHrs,
       briefingSubmitted, briefingSubmittedAt, briefingSigCount, submitBriefing, auditedSubmitBrief,
       reportSubmitted, reportSubmittedAt, submitReport, auditedSubmitReport,
       timecardSubmitted, timecardSubmittedAt, submitTimecard, auditedSubmitTC, toasts, toast]);

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

const useApp = () => useContext(AppCtx);

// Shared helper: push a record to a localStorage key the Operations Console
// reads. De-dupes by id, caps the list. Non-fatal if storage is unavailable.
function pushToConsole(key, rec) {
  if (!rec) return;
  try {
    const prev = JSON.parse(localStorage.getItem(key) || '[]');
    const next = [rec, ...prev.filter(r => r.id !== rec.id)].slice(0, 50);
    localStorage.setItem(key, JSON.stringify(next));
  } catch (e) { /* storage unavailable — non-fatal */ }
}

Object.assign(window, {
  AppCtx, AppProvider, useApp,
  RWIC_USER, MASTER_USER, MASTER_PIN, TODAY_JOB, CONTRACTOR_CREW, HISTORY,
  ACTIVITY_CODES, GROUP_ORDER, GROUP_COLORS, codeByValue,
  RAILROADS, WEATHER, OTS_TYPES,
  uid, todayISO, todayAt, nowHM, hoursBetween, calcMins, minsToDecimal,
  fmtHours, fmtClock, fmtTime, fmtDateLong,
});
