// Seed data + demo identity + offline intake fallback.
// Kept intentionally light: mock data is a feature (a self-explaining demo),
// not a shortcut. The live path replaces all of this with real Jira reads.

(function () {
  // The "signed-in" customer for the demo (single-tenant demo, no real auth).
  const ME = {
    name: "Priya Nair",
    email: "priya@acme.co",
    account: "Acme Co",
  };

  // Product areas the agent can tag a bug against.
  const COMPONENTS = [
    "Deal Rooms", "Forecast", "Addy (AI copilot)", "Slack integration",
    "Openings / Outbound", "Notifications", "Settings", "Other",
  ];

  // Seed a couple of tickets so both dashboards are populated on first open.
  // Only runs when the local mirror is empty.
  // Demo fixtures. These are invented tickets used to make the dashboards
  // self-explaining before anything real exists — they must NEVER appear on a
  // production deployment wired to Jira, where a customer would see other
  // people's fabricated bugs. Seed only on localhost, or explicitly via ?demo=1.
  function isDemoHost() {
    try {
      const h = location.hostname;
      if (h === "localhost" || h === "127.0.0.1" || h === "" || h.endsWith(".local")) return true;
      return new URLSearchParams(location.search).get("demo") === "1";
    } catch (e) { return false; }
  }

  function seedIfEmpty() {
    if (!window.NS || !window.NS.tickets) return;
    if (!isDemoHost()) return;
    if (window.NS.tickets.all().length) return;
    const now = Date.now();
    const seeds = [
      {
        key: "SUP-1042", url: null, live: false,
        reporter: "priya@acme.co", account: "Acme Co",
        title: "Forecast export drops the weighted column",
        component: "Forecast", severity: "SEV3",
        statusCategory: "In Progress", status: "Being worked on",
        createdAt: now - 1000 * 60 * 60 * 26, updatedAt: now - 1000 * 60 * 60 * 3,
        reproSteps: "Open Forecast with a custom view active, click Export, choose CSV.",
        expected: "The CSV includes the weighted-forecast column.",
        actual: "The weighted column is missing from the file.",
        environment: "Chrome 121 / macOS",
        blastRadius: "Just me, but I export weekly.",
        severityRationale: "Degraded export with a documented workaround; one user, not blocked.",
        kbVerdict: "known-issue",
        kbCitation: "Known issue: forecast CSV export can omit the weighted column",
      },
      {
        key: "SUP-1039", url: null, live: false,
        reporter: "priya@acme.co", account: "Acme Co",
        title: "Addy returns a blank answer on the Atlas deal",
        component: "Addy (AI copilot)", severity: "SEV2",
        statusCategory: "Done", status: "Resolved",
        createdAt: now - 1000 * 60 * 60 * 72, updatedAt: now - 1000 * 60 * 60 * 20,
        reproSteps: "Open the Atlas deal room, ask Addy 'what's the risk here?'",
        expected: "Addy answers with a read on the deal.",
        actual: "The response comes back empty.",
        environment: "Chrome / macOS",
        blastRadius: "Me and two others on the Atlas account.",
        severityRationale: "Core copilot broken on a live deal with no workaround.",
      },
      {
        key: "SUP-1051", url: null, live: false,
        reporter: "marcus@northwind.io", account: "Northwind",
        title: "Slack daily brief posted twice at 11:00",
        component: "Slack integration", severity: "SEV3",
        statusCategory: "To Do", status: "Logged",
        createdAt: now - 1000 * 60 * 90, updatedAt: now - 1000 * 60 * 90,
        reproSteps: "Reconnected Slack yesterday; this morning the 11:00 brief arrived twice.",
        expected: "One brief per morning.",
        actual: "Two identical briefs, a few seconds apart.",
        blastRadius: "Whole team sees the duplicate.",
        severityRationale: "Noisy duplicate, but the brief still works — reconnecting clears it.",
        kbVerdict: "known-issue",
        kbCitation: "Known issue: daily Slack brief posts twice at 11:00",
      },
    ];
    seeds.forEach((t) => window.NS.tickets.upsert(t));
  }

  // --- Offline fallback intake -------------------------------------------
  // Only used when the API endpoint genuinely isn't deployed (static server, no
  // /api). This is a DEMO stand-in, not the agent — agent-chat announces that in
  // chat so nobody mistakes a regex for Claude.
  //
  // Even so it must not feel like a script: it infers component + blast radius
  // from the first message and asks NOTHING when they're already clear (which is
  // most of the time). When it does ask, it asks one question, phrased freshly.

  const COMPONENT_HINTS = [
    [/\bdeal ?room|room\b/i, "Deal Rooms"],
    [/\bforecast|pipeline|weighted\b/i, "Forecast"],
    [/\baddy|copilot|ai (assistant|reply)\b/i, "Addy (AI copilot)"],
    [/\bslack\b/i, "Slack integration"],
    [/\bopening|outbound|sequence|prospect\b/i, "Openings / Outbound"],
    [/\bnotification|alert|digest|brief\b/i, "Notifications"],
    [/\bsetting|preference|account|billing\b/i, "Settings"],
  ];

  // "Whole team" vs "just me" is the one fact that actually moves severity, so
  // it's the only thing worth a follow-up — and only when it's truly absent.
  const BLAST_WIDE = /\b(everyone|whole team|entire team|all of us|our team|the team|nobody can|no one can|whole (company|org)|everybody)\b/i;
  const BLAST_NARROW = /\b(just me|only me|only i\b|my account|for me\b|i'?m the only)\b/i;

  function offlineInfer(text) {
    const t = String(text || "").trim();
    const hit = COMPONENT_HINTS.find(([re]) => re.test(t));
    const f = { title: toTitle(t), actual: t };
    if (hit) f.component = hit[1];
    if (BLAST_WIDE.test(t)) f.blastRadius = "Multiple people affected";
    else if (BLAST_NARROW.test(t)) f.blastRadius = "Just the reporter";
    return f;
  }

  // First message → a title: one clause, no trailing period, sentence-cased.
  // Sentence end = [.!?] followed by space/end, so "3.5x load" stays intact.
  // Lookahead (not lookbehind) — the widget has to parse on older Safari.
  function toTitle(s) {
    let t = String(s || "").trim().split("\n")[0].trim();
    const m = t.match(/^(.*?[.!?])(?=\s|$)/);
    if (m) t = m[1];
    t = t.replace(/[.!?]+$/, "").trim();
    if (t.length > 90) t = t.slice(0, 87).replace(/\s+\S*$/, "") + "…";
    return t.charAt(0).toUpperCase() + t.slice(1);
  }

  // Rotated so a demo run twice doesn't repeat itself verbatim.
  const FOLLOW_UPS = [
    "Got it, I'll log this. Is it hitting just you, or the wider team?",
    "Thanks — one thing so I can route it: are other people seeing this too?",
    "Understood. Quick check before I file it — is this blocking you, or more of an annoyance?",
    "On it. Is this just on your account, or is the team stuck as well?",
  ];
  function offlineFollowUp(fields) {
    if (fields.blastRadius) return null;                 // already know → ask nothing
    if (offlineSeverity(fields) === "SEV4") return null;  // cosmetic — who else sees it doesn't change routing
    return FOLLOW_UPS[Math.floor(Math.random() * FOLLOW_UPS.length)];
  }

  // Guess severity from keywords when running offline. The live agent reasons
  // about it properly; this keeps the demo honest without a model call. Uses
  // word boundaries so e.g. "downloads" doesn't trip the "down" (outage) rule.
  function offlineSeverity(fields) {
    const blob = Object.values(fields).join(" ").toLowerCase();
    const has = (re) => re.test(blob);
    // SEV1: true outage / data loss / security / whole org down
    if (has(/\b(outage|offline|down for everyone|data ?loss|lost (my|our|the) data|security|breach|hacked|can'?t log ?in|cannot log ?in|nobody can|whole (company|org|company))\b/))
      return "SEV1";
    // SEV2: major feature broken / crash / whole team blocked (no easy workaround)
    if (has(/\b(crash\w*|broken|unusable|blocked|blocks|can'?t|cannot|no workaround|whole team|entire team|all of us)\b/))
      return "SEV2";
    // SEV4: cosmetic / minor. ("annoyance"/"annoying" share no keyword with the
    // SEV2 rule above, so reaching here means nothing was reported as broken.)
    if (has(/\b(cosmetic|typo|minor|slightly|alignment|spacing|wording|annoyance|annoying|off by a pixel)\b/))
      return "SEV4";
    return "SEV3";
  }

  window.NS = Object.assign(window.NS || {}, {
    ME, COMPONENTS, seedIfEmpty,
    offline: { infer: offlineInfer, followUp: offlineFollowUp, severity: offlineSeverity },
  });
})();
