// csr-app.jsx — operator console shell, state, routing, tweaks
const { useState: useStateC, useEffect: useEffectC } = React;

const CSR_DEFAULTS = /*EDITMODE-BEGIN*/{
  "brandColor": "#4D148C",
  "accentColor": "#FF6600",
  "currency": "AUD"
}/*EDITMODE-END*/;

const CSR_LS = "fedexCsr_v2";
function csrLoad() { try { return JSON.parse(localStorage.getItem(CSR_LS)) || {}; } catch (e) { return {}; } }
function csrSave(patch) { try { const cur = csrLoad(); localStorage.setItem(CSR_LS, JSON.stringify({ ...cur, ...patch })); } catch (e) {} }

// Backfill work-type visibility onto users saved before that feature existed (no `workTypes` key).
function migrateUsers(stored) {
  if (!Array.isArray(stored) || !stored.length) return null;
  const seedById = {};
  (window.FX.CONSOLE_USERS || []).forEach(s => { seedById[s.id] = s; });
  return stored.map(u => {
    let n = ("workTypes" in u) ? u : { ...u, workTypes: seedById[u.id] ? seedById[u.id].workTypes : null };
    if (!("timezone" in n)) n = { ...n, timezone: (seedById[n.id] && seedById[n.id].timezone) || "Australia/Sydney" };
    return n;
  });
}

function CapitalClocks({ agentTz }) {
  const [open, setOpen] = useStateC(false);
  const [, setTick] = useStateC(0);
  useEffectC(() => { const id = setInterval(() => setTick(t => t + 1), 1000); return () => clearInterval(id); }, []);
  const Z = window.FX.AU_ZONES;
  const strip = Z.slice(0, 5); // Perth, Darwin, Adelaide, Brisbane, Sydney (distinct offsets)
  const agentCity = window.FX.tzCity(agentTz);
  return (
    <div style={{ position: "relative" }}>
      <button onClick={() => setOpen(o => !o)} title="Time across Australia" style={{ display: "inline-flex", alignItems: "center", gap: 8, padding: "6px 12px", borderRadius: "var(--r-pill)", border: "none", background: open ? "#fff" : "rgba(255,255,255,0.12)", color: open ? "var(--brand)" : "#fff", cursor: "pointer" }}>
        <Icon name="clock" size={15} />
        <span style={{ fontSize: 12.5 }}>{agentCity}</span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 14, fontWeight: 600, letterSpacing: "0.4px" }}>{window.FX.tzTime(agentTz)}</span>
        <Icon name="chevron-down" size={13} />
      </button>
      {open && (
        <React.Fragment>
          <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 190 }} />
          <div style={{ position: "absolute", top: "calc(100% + 8px)", right: 0, width: 268, background: "#fff", color: "var(--fg-body)", borderRadius: "var(--r-lg)", boxShadow: "var(--shadow-pop)", border: "1px solid var(--border)", padding: 8, zIndex: 200 }}>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.8px", textTransform: "uppercase", color: "var(--fg-mute)", padding: "6px 10px 8px" }}>Time across Australia</div>
            {strip.map(z => {
              const isAgent = z.tz === agentTz || window.FX.tzCity(agentTz) === z.city;
              return (
                <div key={z.city} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 10px", borderRadius: "var(--r-sm)", background: isAgent ? "var(--brand-soft)" : "transparent" }}>
                  <span style={{ width: 7, height: 7, borderRadius: "50%", flex: "none", background: isAgent ? "var(--brand)" : "var(--border-strong)" }} />
                  <span style={{ flex: 1, fontSize: 13.5, fontWeight: isAgent ? 700 : 500, color: "var(--fg-strong)" }}>{z.city}{isAgent ? " · you" : ""}</span>
                  <span style={{ fontFamily: "var(--font-mono)", fontSize: 9.5, color: "var(--fg-faint)" }}>{z.abbr}</span>
                  <span style={{ fontFamily: "var(--font-mono)", fontSize: 13.5, fontWeight: 700, color: "var(--fg-strong)", minWidth: 62, textAlign: "right" }}>{window.FX.tzTime(z.tz)}</span>
                </div>
              );
            })}
            <div style={{ fontSize: 11, color: "var(--fg-mute)", padding: "8px 10px 4px", borderTop: "1px solid var(--border)", marginTop: 4 }}>Your timezone is set in Settings → Users.</div>
          </div>
        </React.Fragment>
      )}
    </div>
  );
}

// Backfill booking-alert + reference rules onto customers saved before those fields existed.
function migrateCustomers(stored) {
  if (!Array.isArray(stored) || !stored.length) return null;
  return stored.map(c => {
    const seed = (window.FX.CUSTOMERS || []).find(s => s.id === c.id);
    const extra = {};
    if (!("bookingAlert" in c)) extra.bookingAlert = seed ? (seed.bookingAlert || "") : "";
    if (!("refConfig" in c)) extra.refConfig = seed ? (seed.refConfig || null) : null;
    if (!("accessPhrase" in c) && seed) {
      extra.accessPhrase = seed.accessPhrase || "";
      extra.broker = !!seed.broker;
      if (seed.brokerName) extra.brokerName = seed.brokerName;
      extra.status = seed.status || "active";
      extra.type = seed.type;
      if (seed.stopReason) extra.stopReason = seed.stopReason;
      extra.thirdPartyAuth = seed.thirdPartyAuth || [];
    }
    return { ...c, ...extra };
  });
}

function seedBookings() {
  const C = window.FX.CUSTOMERS, A = window.FX.ADDRESS_BOOK, D = window.FX.DRIVERS, S = window.FX.SUBURBS, BT = window.FX.BOOKING_TYPES;
  const ist = (contact, company, suburb, postcode, phone, email) => ({ contact, company, suburb, postcode, phone, email, line: "Receiving", unit: "" });
  const mk = (over) => ({ pickup: A[0], dropoff: A[2], vehicleId: "courier", serviceId: "standard", date: window.FX.todayISO(), charges: [], notes: "", isReturn: false, ...over });

  // deterministic PRNG so the seed set is stable across reloads
  let seed = 987654321;
  const rnd = () => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; return seed / 0x7fffffff; };
  const pick = arr => arr[Math.floor(rnd() * arr.length)];
  const block = () => "ABCDEFGHJKMNPQRSTUVWXYZ"[Math.floor(rnd() * 23)] + Math.floor(rnd() * 9) + "ABCDEFGHJKMNPQRSTUVWXYZ"[Math.floor(rnd() * 23)] + Math.floor(rnd() * 9);
  const trk = () => "FX-" + block() + "-" + (1000 + Math.floor(rnd() * 8999));

  const nswPickups = ["Mascot", "Sydney CBD", "Alexandria", "Botany", "Chatswood", "Parramatta"];
  const destStates = ["NSW","NSW","NSW","NSW","NSW","VIC","VIC","QLD","QLD","WA","SA","ACT","TAS","NT"];
  const vehicles = ["courier","wagon","halfvan","van"];
  const services = ["standard","premium","direct"];
  const statusPool = ["In transit","In transit","In transit","In transit","Booked","Booked","Booked","Delivered","Delivered","Cancelled"];
  const times = ["08:00","08:30","09:00","09:30","10:00","10:30","11:00","11:30","12:00","12:30","13:00","14:00","15:00","16:00"];
  const byState = st => S.filter(s => s.state === st);

  const made = [];
  for (let i = 0; i < 44; i++) {
    const cust = pick(C);
    const destState = pick(destStates);
    const interstate = destState !== "NSW";
    const puName = pick(nswPickups);
    const destSub = pick(byState(destState)) || window.FX.findSuburb("Sydney CBD");
    const doName = destSub.name;
    const status = pick(statusPool);
    let typeId = pick(BT).id;
    if (interstate && rnd() > 0.45) typeId = "nextflight";
    const bt = BT.find(t => t.id === typeId);
    const vehicleId = pick(vehicles);
    const serviceId = interstate ? pick(["premium","direct"]) : pick(services);
    const assigned = !(status === "Booked" && rnd() > 0.45);
    const driver = assigned ? pick(D) : null;
    const km = window.FX.distanceKm(puName, doName) || (interstate ? 760 : 18);
    const q = window.FX.quote({ vehicleId, km, serviceId, isReturn: false, rateMult: 1 });
    const total = (q.oneWay * (bt ? bt.mult : 1) + (bt && bt.surcharge ? bt.surcharge : 0)) * 1.1;
    const dayOffset = status === "Delivered" ? -(1 + Math.floor(rnd() * 3)) : 0;
    const date = new Date(Date.now() + dayOffset * 86400000).toISOString().slice(0, 10);
    const progress = status === "In transit" ? +(0.18 + rnd() * 0.66).toFixed(2) : (status === "Delivered" ? 1 : 0);

    // due time spread relative to now so Late / at-risk is always realistic
    let dueAt;
    if (status === "In transit" || status === "Booked") {
      const r = rnd();
      const offMin = r < 0.3 ? -(5 + Math.floor(rnd() * 115)) : r < 0.5 ? (5 + Math.floor(rnd() * 50)) : (70 + Math.floor(rnd() * 300));
      dueAt = new Date(Date.now() + offMin * 60000).toISOString();
    } else {
      dueAt = new Date(Date.now() - (2 + Math.floor(rnd() * 6)) * 3600000).toISOString();
    }

    made.push(mk({
      tracking: trk(), customerId: cust.id, customerName: cust.name, customerRef: cust.ref,
      typeId, vehicleId, serviceId, time: pick(times), date, status, state: destState, dueAt,
      pickup: { contact: cust.contacts && cust.contacts[0] ? cust.contacts[0].name : "Dispatch", company: cust.name, suburb: puName, postcode: "", line: "Pickup point", unit: "", phone: cust.phone, email: cust.email },
      dropoff: ist(interstate ? "Receiving Dock" : "Front Desk", cust.name + (interstate ? " " + destState : ""), doName, "", cust.phone, cust.email),
      driver, progress, km, total,
      charges: rnd() > 0.6 ? [{ ...pick(window.FX.SERVICE_CHARGES), qty: 1 }] : [],
    }));
  }

  const curated = [
    mk({ tracking: "FX-RT44-2210", customerId: C[1].id, customerName: C[1].name, customerRef: C[1].ref, typeId: "clinical", pickup: A[3], dropoff: A[1], vehicleId: "courier", serviceId: "premium", time: "10:30", total: 96.40, status: "In transit", driver: D[1], progress: 0.52, km: 14, dueAt: new Date(Date.now() - 18 * 60000).toISOString() }),
    mk({ tracking: "FX-9XB1-7745", customerId: C[3].id, customerName: C[3].name, customerRef: C[3].ref, typeId: "failsafe-critical", pickup: A[1], dropoff: ist("J. Hollis", "Helix Melbourne Lab", "Melbourne CBD", "3000", "+61 3 9000 2210", "lab.mel@helixbio.com.au"), vehicleId: "wagon", serviceId: "direct", time: "09:00", total: 612.80, status: "In transit", state: "VIC", driver: D[0], progress: 0.71, km: 878, dueAt: new Date(Date.now() + 38 * 60000).toISOString() }),
    mk({ tracking: "FX-2KP8-1190", customerId: C[0].id, customerName: C[0].name, customerRef: C[0].ref, typeId: "standard", pickup: A[0], dropoff: A[4], vehicleId: "halfvan", serviceId: "standard", time: "13:00", total: 142.55, status: "Booked", isReturn: true, driver: null, progress: 0, dueAt: new Date(Date.now() + 165 * 60000).toISOString() }),
    mk({ tracking: "FX-8LM3-5521", customerId: C[2].id, customerName: C[2].name, customerRef: C[2].ref, typeId: "nextflight", pickup: A[2], dropoff: ist("Receiving Dock", "Acme Brisbane DC", "Brisbane CBD", "4000", "+61 7 3000 5521", "dock.bne@acmeretail.com.au"), vehicleId: "courier", serviceId: "premium", time: "08:00", total: 328.30, status: "Booked", state: "QLD", driver: D[5], progress: 0, dueAt: new Date(Date.now() + 26 * 60000).toISOString() }),
    mk({ tracking: "FX-6QW2-3380", customerId: C[4].id, customerName: C[4].name, customerRef: C[4].ref, typeId: "failsafe-secure", pickup: A[5], dropoff: ist("B. Okoro", "Perth Cellar Door", "Perth CBD", "6000", "+61 8 6000 3380", "perth@baysidecellars.com.au"), vehicleId: "van", serviceId: "standard", time: "11:15", total: 489.60, status: "In transit", state: "WA", driver: D[4], progress: 0.34, km: 3290, dueAt: new Date(Date.now() + 192 * 60000).toISOString() }),
    mk({ tracking: "FX-1HD7-9904", customerId: C[0].id, customerName: C[0].name, customerRef: C[0].ref, typeId: "standard", pickup: A[0], dropoff: ist("Front Desk", "Meridian Canberra", "Canberra", "2600", "+61 2 6100 9904", "act@meridiangroup.com.au"), vehicleId: "wagon", serviceId: "premium", time: "14:30", total: 214.70, status: "Booked", state: "ACT", driver: null, progress: 0, dueAt: new Date(Date.now() + 295 * 60000).toISOString() }),
    mk({ tracking: "FX-5MS9-3120", customerId: C[3].id, customerName: C[3].name, customerRef: C[3].ref, typeId: "nextflight", flightIata: "QF445", pickup: { contact: "J. Hollis", company: "Helix Melbourne Lab", suburb: "Melbourne CBD", postcode: "3000", line: "Pickup point", unit: "", phone: "+61 3 9000 2210", email: "lab.mel@helixbio.com.au" }, dropoff: ist("Receiving Dock", "Helix Macquarie Park", "Macquarie Park", "2113", "+61 2 8001 4400", "logistics@helixbio.com.au"), vehicleId: "courier", serviceId: "premium", time: "07:30", total: 372.60, status: "In transit", state: "NSW", driver: D[2], progress: 0.42, km: 878, dueAt: new Date(Date.now() + 120 * 60000).toISOString() }),
    mk({ tracking: "FX-3BN7-8845", customerId: C[2].id, customerName: C[2].name, customerRef: C[2].ref, typeId: "nextflight", pickup: { contact: "Dock 4", company: "Acme Brisbane DC", suburb: "Brisbane CBD", postcode: "4000", line: "Pickup point", unit: "", phone: "+61 7 3000 5521", email: "dock.bne@acmeretail.com.au" }, dropoff: ist("Front Desk", "Acme Sydney Flagship", "Sydney CBD", "2000", "+61 412 778 220", "priya@acmeretail.com.au"), vehicleId: "courier", serviceId: "direct", time: "08:15", total: 298.40, status: "Booked", state: "NSW", driver: null, progress: 0, dueAt: new Date(Date.now() + 150 * 60000).toISOString() }),
    mk({ tracking: "FX-7PM2-6610", customerId: C[4].id, customerName: C[4].name, customerRef: C[4].ref, typeId: "nextflight", pickup: { contact: "Cellar Door", company: "Bayside Perth", suburb: "Perth CBD", postcode: "6000", line: "Pickup point", unit: "", phone: "+61 8 6000 3380", email: "perth@baysidecellars.com.au" }, dropoff: ist("Receiving", "Bayside Melbourne", "Melbourne CBD", "3000", "+61 437 901 552", "orders@baysidecellars.com.au"), vehicleId: "wagon", serviceId: "premium", time: "06:45", total: 512.20, status: "In transit", state: "VIC", driver: D[6], progress: 0.62, km: 2720, dueAt: new Date(Date.now() + 95 * 60000).toISOString() }),
  ];
  return [...curated, ...made].map(b => ({ ...b, state: b.state || window.FX.suburbState(b.dropoff.suburb) }));
}

function seedQuotes() {
  const C = window.FX.CUSTOMERS, day = 864e5, now = Date.now();
  const mk = (o) => ({ status: "active", isReturn: false, items: [], ...o, expiresAt: window.FX.quoteExpiryISO(o.createdAt) });
  return [
    mk({ no: "Q-480021", customerId: C[0].id, customerName: C[0].name, customerRef: C[0].ref, source: "customer", from: "Sydney CBD", fromPc: "2000", to: "Chatswood", toPc: "2067", serviceId: "premium", vehicleId: "wagon", total: 96.4, km: 12, createdAt: new Date(now - 2 * day).toISOString() }),
    mk({ no: "Q-480044", customerId: C[1].id, customerName: C[1].name, customerRef: C[1].ref, source: "agent", agentId: "u1", agentName: "Jordan Diaz", from: "Newtown", fromPc: "2042", to: "Parramatta", toPc: "2150", serviceId: "standard", vehicleId: "courier", total: 41.2, km: 23, createdAt: new Date(now - 1 * day).toISOString() }),
    mk({ no: "Q-479980", customerId: C[2].id, customerName: C[2].name, customerRef: C[2].ref, source: "agent", agentId: "u3", agentName: "Chen Wu", from: "Brisbane CBD", fromPc: "4000", to: "Sydney CBD", toPc: "2000", serviceId: "premium", vehicleId: "courier", total: 322.5, km: 920, isInterstate: true, createdAt: new Date(now - 5 * day).toISOString(), status: "booked", convertedBookingTracking: "FX-8LM3-5521", convertedAt: new Date(now - 5 * day + 36e5).toISOString(), convertedBy: "Chen Wu" }),
    mk({ no: "Q-479920", customerId: C[3].id, customerName: C[3].name, customerRef: C[3].ref, source: "customer", from: "Melbourne CBD", fromPc: "3000", to: "Geelong", toPc: "3220", serviceId: "standard", vehicleId: "van", total: 158.0, km: 75, createdAt: new Date(now - 8 * day).toISOString(), status: "booked", convertedBookingTracking: "FX-2KP8-1190", convertedAt: new Date(now - 8 * day + 72e5).toISOString(), convertedBy: C[3].name }),
    mk({ no: "Q-479875", customerId: C[0].id, customerName: C[0].name, customerRef: C[0].ref, source: "agent", agentId: "u1", agentName: "Jordan Diaz", from: "Mascot", fromPc: "2020", to: "Liverpool", toPc: "2170", serviceId: "direct", vehicleId: "wagon", total: 88.9, km: 31, createdAt: new Date(now - 11 * day).toISOString(), status: "booked", convertedBookingTracking: "FX-1HD7-9904", convertedAt: new Date(now - 11 * day + 50e5).toISOString(), convertedBy: "Jordan Diaz" }),
    mk({ no: "Q-479610", customerId: C[4].id, customerName: C[4].name, customerRef: C[4].ref, source: "agent", agentId: "u3", agentName: "Chen Wu", from: "Perth CBD", fromPc: "6000", to: "Fremantle", toPc: "6160", serviceId: "standard", vehicleId: "courier", total: 52.3, km: 19, createdAt: new Date(now - 14 * day).toISOString() }),
    mk({ no: "Q-479540", customerId: C[1].id, customerName: C[1].name, customerRef: C[1].ref, source: "customer", from: "Sydney CBD", fromPc: "2000", to: "Manly", toPc: "2095", serviceId: "premium", vehicleId: "courier", total: 64.0, km: 18, createdAt: new Date(now - 3 * day).toISOString() }),
    mk({ no: "Q-479120", customerId: C[2].id, customerName: C[2].name, customerRef: C[2].ref, source: "agent", agentId: "u2", agentName: "Amara Osei", from: "Brisbane CBD", fromPc: "4000", to: "Gold Coast", toPc: "4217", serviceId: "standard", vehicleId: "van", total: 190.5, km: 78, createdAt: new Date(now - 22 * day).toISOString(), status: "booked", convertedBookingTracking: "FX-6QW2-3380", convertedAt: new Date(now - 22 * day + 40e5).toISOString(), convertedBy: "Amara Osei" }),
    mk({ no: "Q-478980", customerId: C[3].id, customerName: C[3].name, customerRef: C[3].ref, source: "customer", from: "Melbourne CBD", fromPc: "3000", to: "Richmond (VIC)", toPc: "3121", serviceId: "standard", vehicleId: "courier", total: 38.5, km: 6, createdAt: new Date(now - 28 * day).toISOString() }),
    mk({ no: "Q-478740", customerId: C[0].id, customerName: C[0].name, customerRef: C[0].ref, source: "agent", agentId: "u1", agentName: "Jordan Diaz", from: "Sydney CBD", fromPc: "2000", to: "Penrith", toPc: "2750", serviceId: "standard", vehicleId: "halfvan", total: 176.2, km: 55, createdAt: new Date(now - 40 * day).toISOString(), status: "booked", convertedBookingTracking: "FX-7PM2-6610", convertedAt: new Date(now - 40 * day + 30e5).toISOString(), convertedBy: "Jordan Diaz" }),
    mk({ no: "Q-478520", customerId: C[4].id, customerName: C[4].name, customerRef: C[4].ref, source: "agent", agentId: "u3", agentName: "Chen Wu", from: "Adelaide CBD", fromPc: "5000", to: "Glenelg", toPc: "5045", serviceId: "premium", vehicleId: "courier", total: 47.8, km: 12, createdAt: new Date(now - 45 * day).toISOString() }),
  ];
}

function CsrApp() {
  const [t, setTweak] = useTweaks(CSR_DEFAULTS);
  const init = csrLoad();
  const [view, setView] = useStateC("book");
  const [customers, setCustomers] = useStateC(migrateCustomers(init.customers) || window.FX.CUSTOMERS);
  const [bookings, setBookings] = useStateC(init.bookings || seedBookings());
  const [users, setUsers] = useStateC(migrateUsers(init.users) || window.FX.CONSOLE_USERS);
  const [serviceAgents, setServiceAgents] = useStateC(init.serviceAgents || window.FX.SERVICE_AGENTS);
  const [quotes, setQuotes] = useStateC(init.quotes || seedQuotes());
  const [seedBooking, setSeedBooking] = useStateC(null);
  const [quoteLink, setQuoteLink] = useStateC(null);
  const [bookStep, setBookStep] = useStateC("overview");
  const [inquiries, setInquiries] = useStateC(window.FX.loadInquiries());
  const [announcements, setAnnouncements] = useStateC(window.FX.loadAnnouncements());
  const [active, setActive] = useStateC(null);
  const [editing, setEditing] = useStateC(null);
  const [verified, setVerified] = useStateC(false);
  const [caller, setCaller] = useStateC({ name: "", phone: "" });
  const [currentUserId, setCurrentUserId] = useStateC(init.currentUserId || "u1");
  const [addOpen, setAddOpen] = useStateC(false);
  const [prefill, setPrefill] = useStateC("");
  const [toast, setToast] = useStateC(null);
  const [clock, setClock] = useStateC("");

  useEffectC(() => { csrSave({ customers, bookings, users, serviceAgents, quotes, currentUserId }); }, [customers, bookings, users, serviceAgents, quotes, currentUserId]);
  useEffectC(() => { window.FX.saveInquiries(inquiries); }, [inquiries]);
  useEffectC(() => { window.FX.saveAnnouncements(announcements); }, [announcements]);
  useEffectC(() => {
    const tick = () => setClock(new Date().toLocaleTimeString("en-AU", { hour: "2-digit", minute: "2-digit", second: "2-digit" }));
    tick(); const id = setInterval(tick, 1000); return () => clearInterval(id);
  }, []);

  // theme
  useEffectC(() => {
    const r = document.documentElement.style, c = t.brandColor, a = t.accentColor;
    r.setProperty("--brand", c); r.setProperty("--brand-700", `color-mix(in srgb, ${c}, black 24%)`); r.setProperty("--brand-900", `color-mix(in srgb, ${c}, black 48%)`); r.setProperty("--brand-soft", `color-mix(in srgb, ${c}, white 90%)`);
    r.setProperty("--accent", a); r.setProperty("--accent-600", `color-mix(in srgb, ${a}, black 12%)`); r.setProperty("--accent-700", `color-mix(in srgb, ${a}, black 24%)`); r.setProperty("--accent-soft", `color-mix(in srgb, ${a}, white 88%)`);
    r.setProperty("--shadow-cta", `0 8px 22px color-mix(in srgb, ${a}, transparent 70%)`);
  }, [t.brandColor, t.accentColor]);

  function showToast(msg, tone = "success") { setToast({ msg, tone }); setTimeout(() => setToast(null), 3400); }
  function selectCustomer(c) { setActive(c); setEditing(null); setVerified(false); setSeedBooking(null); setQuoteLink(null); setBookStep("overview"); setView("book"); }
  function addCustomer(c) { setCustomers(prev => [c, ...prev]); setActive(c); setAddOpen(false); showToast(`Customer ${c.name} added`); }
  function addAddress(custId, addr) {
    setCustomers(prev => prev.map(c => c.id === custId ? { ...c, addresses: [...(c.addresses || []), addr] } : c));
    setActive(prev => prev && prev.id === custId ? { ...prev, addresses: [...(prev.addresses || []), addr] } : prev);
    showToast(`Address “${addr.label}” added`);
  }

  function placeBooking(b) {
    if (editing) {
      setBookings(prev => prev.map(x => x.tracking === b.tracking ? { ...x, ...b } : x));
      showToast(`Booking ${b.tracking} updated`);
      setEditing(null); setView("queue");
    } else {
      setBookings(prev => [b, ...prev]);
      showToast(`Booking ${b.tracking} placed for ${b.customerName}`);
      setActive(null); setView("queue");
    }
  }
  function editBooking(b) {
    const c = customers.find(x => x.id === b.customerId) || { id: b.customerId, name: b.customerName, ref: b.customerRef, account: "", terms: "", phone: "", email: "", suburb: b.pickup.suburb, contacts: [] };
    setActive(c); setEditing(b); setVerified(true); setBookStep("form"); setView("book"); window.scrollTo({ top: 0 });
  }
  function cancelBooking(b) { setBookings(prev => prev.map(x => x.tracking === b.tracking ? { ...x, status: "Cancelled", audit: [...(x.audit || []), { at: new Date().toISOString(), by: currentUser.name || "CE console", action: "cancelled", detail: "Booking cancelled" }] } : x)); showToast(`Booking ${b.tracking} cancelled`, "danger"); }

  function saveCustomer(c) {
    setCustomers(prev => prev.map(x => x.id === c.id ? { ...x, ...c } : x));
    setActive(prev => prev && prev.id === c.id ? { ...prev, ...c } : prev);
    showToast(`Customer ${c.name} updated`);
  }
  function addContact(custId, p) {
    setCustomers(prev => prev.map(c => c.id === custId ? { ...c, contacts: [...(c.contacts || []), p] } : c));
    setActive(prev => prev && prev.id === custId ? { ...prev, contacts: [...(prev.contacts || []), p] } : prev);
    showToast(`Contact ${p.name} added`);
  }
  function lodgeInquiryCE(cust, { type, tracking, message }) {
    const now = new Date().toISOString();
    const inq = { ref: window.FX.genInquiryRef(), tracking: tracking || "General inquiry", product: "", customerId: cust.id, customerName: cust.name, customerEmail: cust.email || "", type, message, status: "in-progress", agent: currentUser.name || "CE console", comments: [], createdAt: now, updatedAt: now, viaCE: true };
    setInquiries(prev => [inq, ...prev]);
    showToast(`Inquiry ${inq.ref} lodged for ${cust.name}`);
  }
  function createInquiryCE({ customerId, tracking, type, message }) {
    const cust = customers.find(c => c.id === customerId);
    if (cust) lodgeInquiryCE(cust, { type, tracking, message });
  }

  function saveUser(u) { setUsers(prev => prev.some(x => x.id === u.id) ? prev.map(x => x.id === u.id ? u : x) : [...prev, u]); showToast(`User ${u.name} saved`); }
  function deleteUser(id) { setUsers(prev => prev.filter(x => x.id !== id)); showToast("User removed", "danger"); }
  function saveAgent(a) { setServiceAgents(prev => prev.some(x => x.id === a.id) ? prev.map(x => x.id === a.id ? a : x) : [...prev, a]); showToast(`Service agent ${a.name} saved`); }
  function deleteAgent(id) { setServiceAgents(prev => prev.filter(x => x.id !== id)); showToast("Service agent removed", "danger"); }
  function convertQuote(qt) {
    const c = customers.find(x => x.id === qt.customerId) || active;
    const pickup = { ...window.FX.emptyAddr(), suburb: qt.from, postcode: qt.fromPc || "" };
    const dropoff = { ...window.FX.emptyAddr(), suburb: qt.to, postcode: qt.toPc || "" };
    const synth = { stops: [pickup, dropoff], pickup, dropoff, typeId: qt.isInterstate ? "nextflight" : "standard", vehicleId: qt.vehicleId, serviceId: qt.serviceId, isReturn: !!qt.isReturn, lineItems: qt.items || [], ref1: qt.ref1 || "", date: window.FX.todayISO() };
    setActive(c); setEditing(null); setSeedBooking(synth); setQuoteLink(qt.no); setVerified(true); setBookStep("form"); setView("book"); window.scrollTo({ top: 0 });
    showToast(`Quote ${qt.no} — review & place to convert`);
  }
  function dupBooking(b) {
    const synth = { stops: b.stops || [b.pickup, b.dropoff], pickup: b.pickup, dropoff: b.dropoff, typeId: b.typeId, vehicleId: b.vehicleId, serviceId: b.serviceId, isReturn: !!b.isReturn, lineItems: b.lineItems || [], ref1: b.ref1 || "", ref2: b.ref2 || "", date: window.FX.todayISO() };
    setEditing(null); setSeedBooking(synth); setQuoteLink(null); setBookStep("form"); window.scrollTo({ top: 0 });
    showToast(`Duplicating ${b.tracking} — review & place`);
  }
  function saveQuote(qt) { setQuotes(prev => prev.map(x => x.no === qt.no ? { ...x, ...qt } : x)); showToast(`Quote ${qt.no} updated`); }
  function createQuote(qt) { setQuotes(prev => [qt, ...prev]); showToast(`Quote ${qt.no} created`); }
  function quoteFromBooking(partial) {
    const c = active; if (!c) return;
    const createdAt = new Date().toISOString();
    const q = { no: window.FX.genQuoteNo(), customerId: c.id, customerName: c.name, customerRef: c.ref || "", source: "agent", agentId: currentUser.id, agentName: currentUser.name || "CE console", ...partial, createdAt, expiresAt: window.FX.quoteExpiryISO(createdAt), status: "active" };
    setQuotes(prev => [q, ...prev]);
    setActive(null); setSeedBooking(null); setQuoteLink(null); setBookStep("overview"); setView("quotes");
    showToast(`Saved as quote ${q.no}`);
  }
  function respondInquiry(ref, body) { const nm = currentUser.name || "CE console"; setInquiries(prev => prev.map(i => i.ref === ref ? { ...i, comments: [...(i.comments || []), { author: nm, role: "agent", body, at: new Date().toISOString() }], updatedAt: new Date().toISOString(), agent: i.agent || nm } : i)); }
  function setStatusInquiry(ref, status) { const nm = currentUser.name || "CE console"; setInquiries(prev => prev.map(i => i.ref === ref ? { ...i, status, updatedAt: new Date().toISOString(), agent: i.agent || nm } : i)); }
  function assignInquiry(ref, userId) { const u = users.find(x => x.id === userId); setInquiries(prev => prev.map(i => i.ref === ref ? { ...i, assignedTo: userId || null, assignedName: u ? u.name : null, updatedAt: new Date().toISOString() } : i)); showToast(u ? `Inquiry ${ref} assigned to ${u.name}` : `Inquiry ${ref} unassigned`); }
  function saveAnn(a) { setAnnouncements(prev => [a, ...prev.filter(x => x.id !== a.id)]); showToast("Announcement posted"); }
  function deleteAnn(id) { setAnnouncements(prev => prev.filter(x => x.id !== id)); showToast("Announcement removed", "danger"); }

  const currentUser = users.find(u => u.id === currentUserId) || users[0] || {};
  const stationSt = currentUser.station && currentUser.station !== "all" ? currentUser.station : null;
  const wtVisible = Array.isArray(currentUser.workTypes) ? currentUser.workTypes : null;
  const visibleBookings = bookings.filter(b => {
    if (wtVisible && !wtVisible.includes(b.typeId)) return false;
    if (stationSt) {
      const o = b.pickup && b.pickup.suburb ? window.FX.suburbState(b.pickup.suburb) : null;
      const d = b.state || (b.dropoff && b.dropoff.suburb ? window.FX.suburbState(b.dropoff.suburb) : null);
      if (o !== stationSt && d !== stationSt) return false;
    }
    return true;
  });
  const activeCount = visibleBookings.filter(b => b.status !== "Cancelled").length;
  const preAlertCount = visibleBookings.filter(b => window.FX.isNextFlight(b) && b.status !== "Cancelled" && b.status !== "Delivered").length;
  const inqOpen = inquiries.filter(i => i.status === "in-progress").length;
  function openBookingTab(b) { window.location.href = "Fedex Booking.html#t=" + encodeURIComponent(b.tracking); }

  return (
    <div style={{ minHeight: "100vh", display: "flex", flexDirection: "column", background: "var(--bg-page)" }}>
      {/* Top bar */}
      <header style={{ height: 64, flex: "none", display: "flex", alignItems: "center", justifyContent: "space-between", padding: "0 24px", background: "var(--brand)", color: "#fff", position: "sticky", top: 0, zIndex: 100 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <Wordmark size={22} onDark />
          <span style={{ width: 1, height: 26, background: "rgba(255,255,255,0.2)" }} />
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <Icon name="headset" size={17} />
            <span style={{ fontWeight: 600, fontSize: 14.5 }}>Customer Service Console</span>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 6, background: "rgba(255,255,255,0.1)", padding: 4, borderRadius: "var(--r-pill)" }}>
          {[["book", "Take a booking", "plus"], ["queue", "Bookings queue", "history"], ["quotes", "Quotes", "calculator"], ["customers", "Customers", "users"], ["prealerts", "Pre-Alerts", "bell"], ["inquiries", "Inquiries", "message-square"], ["agents", "Service Agents", "shield"], ["flights", "Flights", "plane"]].map(([k, lbl, ic]) => (
            <button key={k} onClick={() => setView(k)} style={{ display: "inline-flex", alignItems: "center", gap: 7, padding: "8px 14px", borderRadius: "var(--r-pill)", border: "none", fontSize: 13.5, fontWeight: 500, background: view === k ? "#fff" : "transparent", color: view === k ? "var(--brand)" : "rgba(255,255,255,0.85)", whiteSpace: "nowrap" }}>
              <Icon name={ic} size={15} /> {lbl}
              {k === "queue" && <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, background: view === k ? "var(--brand-soft)" : "rgba(255,255,255,0.18)", color: view === k ? "var(--brand)" : "#fff", padding: "1px 7px", borderRadius: 999 }}>{activeCount}</span>}
              {k === "prealerts" && preAlertCount > 0 && <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, background: view === k ? "var(--accent-soft)" : "rgba(255,255,255,0.18)", color: view === k ? "var(--accent-700)" : "#fff", padding: "1px 7px", borderRadius: 999 }}>{preAlertCount}</span>}
              {k === "inquiries" && inqOpen > 0 && <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, background: view === k ? "var(--warn-soft)" : "rgba(255,255,255,0.18)", color: view === k ? "#8a5a00" : "#fff", padding: "1px 7px", borderRadius: 999 }}>{inqOpen}</span>}
            </button>
          ))}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <CapitalClocks agentTz={currentUser.timezone || "Australia/Sydney"} />
          <button onClick={() => setView("settings")} title="Settings" style={{ width: 34, height: 34, borderRadius: "50%", border: "none", background: view === "settings" ? "#fff" : "rgba(255,255,255,0.12)", color: view === "settings" ? "var(--brand)" : "#fff", display: "inline-flex", alignItems: "center", justifyContent: "center", cursor: "pointer" }}><Icon name="settings" size={17} /></button>
          <span style={{ width: 1, height: 26, background: "rgba(255,255,255,0.2)" }} />
          <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
            <span style={{ width: 34, height: 34, borderRadius: "50%", background: "var(--accent)", color: "#fff", display: "inline-flex", alignItems: "center", justifyContent: "center", fontWeight: 700, fontSize: 13 }}>{(currentUser.name || "?").split(/\s+/).map(w => w[0]).slice(0, 2).join("").toUpperCase()}</span>
            <div style={{ lineHeight: 1.2 }}>
              <select value={currentUserId} onChange={e => setCurrentUserId(e.target.value)} title="Switch signed-in user (demo)" style={{ background: "transparent", border: "none", color: "#fff", fontSize: 13, fontWeight: 600, outline: "none", cursor: "pointer", padding: 0, marginLeft: -2 }}>
                {users.filter(u => u.active).map(u => <option key={u.id} value={u.id} style={{ color: "#111" }}>{u.name}</option>)}
              </select>
              <div style={{ fontSize: 11, opacity: 0.7 }}>{(window.FX.roleById(currentUser.roleId) || {}).label || "Operator"} · {stationSt ? window.FX.AIRPORTS[stationSt].code + " station" : "All stations"}</div>
            </div>
          </div>
        </div>
      </header>

      <main style={{ flex: 1, padding: "26px 28px 80px", maxWidth: 1340, width: "100%", margin: "0 auto" }}>
        {(() => { const team = window.FX.activeAnnouncements(announcements).filter(a => a.audience === "Customer service team (internal)"); if (!team.length) return null; const a = team[0]; return (
          <div style={{ display: "flex", gap: 12, alignItems: "flex-start", padding: "13px 17px", borderRadius: "var(--r-lg)", background: "var(--accent-soft)", border: "1px solid var(--accent)", marginBottom: 18 }}>
            <Icon name="megaphone" size={18} color="var(--accent-700)" style={{ flex: "none", marginTop: 1 }} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <span style={{ fontWeight: 700, fontSize: 14, color: "var(--fg-strong)" }}>{a.title}</span>
              <span style={{ fontSize: 13.5, color: "var(--fg-body)", marginLeft: 8 }}>{a.body}</span>
            </div>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, letterSpacing: "0.8px", textTransform: "uppercase", color: "var(--accent-700)", whiteSpace: "nowrap" }}>Team notice · {window.FX.quoteDaysLeft(a.validUntil)}d left</span>
          </div>
        ); })()}
        {view === "queue" && (
          <BookingsQueue bookings={visibleBookings} currency={t.currency} onEdit={editBooking} onCancel={cancelBooking} onNew={() => { setEditing(null); setActive(null); setView("book"); }} onNotify={showToast} />
        )}

        {view === "prealerts" && <PreAlerts bookings={visibleBookings} onOpen={openBookingTab} onNotify={showToast} />}

        {view === "customers" && <CsrCustomers customers={customers} bookings={bookings} currency={t.currency} onSaveCustomer={saveCustomer} onAddCustomer={c => { setCustomers(prev => [c, ...prev]); showToast(`Customer ${c.name} added`); }} onAddAddress={addAddress} onAddContact={addContact} onLodgeInquiry={lodgeInquiryCE} onBook={selectCustomer} />}

        {view === "flights" && <CsrFlights />}

        {view === "quotes" && <CsrQuotes quotes={quotes} customers={customers} currency={t.currency} currentUser={currentUser} onConvert={convertQuote} onSaveQuote={saveQuote} onCreateQuote={createQuote} onNotify={showToast} />}

        {view === "agents" && <CsrServiceAgents agents={serviceAgents} onSaveAgent={saveAgent} onDeleteAgent={deleteAgent} />}

        {view === "inquiries" && <CsrInquiries inquiries={inquiries} users={users} currentUser={currentUser} customers={customers} bookings={bookings} onCreate={createInquiryCE} onRespond={respondInquiry} onSetStatus={setStatusInquiry} onAssign={assignInquiry} onNotify={showToast} />}

        {view === "settings" && <CsrSettings users={users} roles={window.FX.USER_ROLES} serviceAgents={serviceAgents} announcements={announcements} onSaveAnn={saveAnn} onDeleteAnn={deleteAnn} onSaveUser={saveUser} onDeleteUser={deleteUser} onSaveAgent={saveAgent} onDeleteAgent={deleteAgent} />}

        {view === "book" && !active && (
          <div className="fade-up" style={{ maxWidth: 720, margin: "8vh auto 0", textAlign: "center" }}>
            <div style={{ width: 60, height: 60, borderRadius: "50%", background: "var(--brand-soft)", color: "var(--brand)", display: "inline-flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }}><Icon name="headset" size={28} /></div>
            <h1 style={{ margin: 0, fontSize: 26, fontWeight: 700, letterSpacing: "-0.6px", color: "var(--fg-strong)" }}>Who's calling?</h1>
            <p style={{ margin: "8px 0 22px", fontSize: 15, color: "var(--fg-mute)" }}>Search for the customer by name or reference number to start a booking.</p>
            <div style={{ textAlign: "left" }}>
              <CustomerSearch customers={customers} autoFocus onSelect={selectCustomer} onAddNew={(name) => { setPrefill(name); setAddOpen(true); }} />
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 10, justifyContent: "center", marginTop: 18 }}>
              <span style={{ fontSize: 13, color: "var(--fg-mute)" }}>New customer?</span>
              <Button variant="secondary" size="sm" icon="plus" onClick={() => { setPrefill(""); setAddOpen(true); }}>Add to system</Button>
            </div>
          </div>
        )}

        {view === "book" && active && (
          <div className="fade-up">
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 16, marginBottom: 18, flexWrap: "wrap" }}>
              <div>
                <div className="eyebrow">{editing ? "Updating booking · " + editing.tracking : "New booking"}</div>
                <h1 style={{ margin: "8px 0 0", fontSize: 25, fontWeight: 700, letterSpacing: "-0.6px", color: "var(--fg-strong)", whiteSpace: "nowrap" }}>{editing ? "Edit delivery" : "Take a booking"}</h1>
                {(caller.name || caller.phone) && !editing && (
                  <div style={{ display: "inline-flex", alignItems: "center", gap: 7, marginTop: 8, padding: "5px 12px", background: "var(--brand-soft)", borderRadius: "var(--r-pill)", fontSize: 12.5, color: "var(--brand)", fontWeight: 500 }}>
                    <Icon name="headset" size={13} /> On the line: <strong>{caller.name || "Caller"}</strong>{caller.phone && <span style={{ fontFamily: "var(--font-mono)", fontSize: 12 }}>· {caller.phone}</span>}
                  </div>
                )}
              </div>
              <div style={{ width: 320, maxWidth: "100%" }}>
                <CustomerSearch customers={customers} onSelect={selectCustomer} onAddNew={(name) => { setPrefill(name); setAddOpen(true); }} />
              </div>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "296px minmax(0,1fr)", gap: 20, alignItems: "start" }}>
              <div style={{ position: "sticky", top: 16 }}>
                <CustomerCard customer={active} onClear={() => { setActive(null); setEditing(null); }} onAddAddress={addAddress} />
              </div>
              {(() => {
                if (!editing && bookStep === "overview") return <CustomerOverview customer={active} bookings={bookings.filter(b => b.customerId === active.id)} quotes={quotes.filter(q => q.customerId === active.id)} currency={t.currency} onNewBooking={() => { setSeedBooking(null); setQuoteLink(null); setBookStep("form"); }} onOpen={openBookingTab} onDuplicate={dupBooking} onConvertQuote={convertQuote} onEditBooking={editBooking} />;
                const acctBlocked = active.status === "stop-trade" || active.status === "inactive";
                const acctNeedsPhrase = active.status === "active" && !!active.accessPhrase;
                const showBooking = editing || (!acctBlocked && (!acctNeedsPhrase || verified));
                return showBooking
                  ? <CsrBooking key={active.id + "|" + (editing ? editing.tracking : (quoteLink || "new"))} customer={active} seedBooking={seedBooking} caller={caller} setCaller={setCaller} currency={t.currency} rateMult={1} onQuote={quoteFromBooking} onPlace={b2 => {
                      const now = new Date().toISOString();
                      const entry = editing
                        ? { at: now, by: currentUser.name || "CE console", action: "edited", detail: "Booking details updated" }
                        : { at: now, by: currentUser.name || "CE console", action: "created", detail: "Booked via CE console" + (caller.name ? " · caller: " + caller.name : "") };
                      let callLog = (editing && editing.callLog) || [];
                      if (b2.callerNote) callLog = [{ id: "n" + Date.now(), kind: "Caller note", text: b2.callerNote, by: currentUser.name || "CE console", at: now }, ...callLog];
                      const { callerNote, ...rest } = b2;
                      placeBooking({ ...rest, callLog, caller: (caller.name || caller.phone) ? { ...caller } : (editing ? editing.caller : null), audit: [...((editing && editing.audit) || []), entry] });
                      if (quoteLink) { const trk = rest.tracking; setQuotes(prev => prev.map(x => x.no === quoteLink ? { ...x, status: "booked", convertedBookingTracking: trk, convertedAt: now, convertedBy: currentUser.name || "CE console" } : x)); setQuoteLink(null); setSeedBooking(null); }
                      setCaller({ name: "", phone: "" });
                    }} editing={editing} onClear={() => { setEditing(null); setActive(null); setSeedBooking(null); setQuoteLink(null); setView("queue"); }} />
                  : <AccountGate customer={active} onVerified={() => setVerified(true)} />;
              })()}
            </div>
          </div>
        )}
      </main>

      <AddCustomerModal open={addOpen} onClose={() => setAddOpen(false)} onSave={addCustomer} prefillName={prefill} />

      {/* toast */}
      {toast && (
        <div className="fade-up" style={{ position: "fixed", bottom: 24, left: "50%", transform: "translateX(-50%)", zIndex: 300, display: "flex", alignItems: "center", gap: 11, background: "var(--fg-strong)", color: "#fff", padding: "13px 20px", borderRadius: "var(--r-pill)", boxShadow: "var(--shadow-pop)" }}>
          <Icon name={toast.tone === "danger" ? "info" : "check-circle"} size={18} color={toast.tone === "danger" ? "#ff8b8b" : "#5fe0a0"} />
          <span style={{ fontSize: 14, fontWeight: 500 }}>{toast.msg}</span>
        </div>
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Brand colours" />
        <TweakColor label="Primary" value={t.brandColor} options={["#4D148C", "#2A1A6B", "#0C1A2C", "#1F4E79", "#7A1F6B"]} onChange={v => setTweak("brandColor", v)} />
        <TweakColor label="Accent" value={t.accentColor} options={["#FF6600", "#FF8A00", "#E5484D", "#1A8FF0", "#22B86E"]} onChange={v => setTweak("accentColor", v)} />
        <TweakSection label="Pricing" />
        <TweakSelect label="Currency" value={t.currency} options={["AUD", "NZD", "USD"]} onChange={v => setTweak("currency", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<CsrApp />);
