/* The agent layer — what the renewal agent did, discovered, resolved, and prepared.
   Reads the same Renewal State Engine data as the operational views. */
const MDx = window.MD;
const openC = (r) => r.conditions.filter((c) => c.status !== 'resolved');
const activeR = (r) => r.stage !== 'Renewed';
const prim = (r) => { const p = { overdue: 0, crit: 1, tight: 2, ok: 3 }; return openC(r).slice().sort((a, b) => (p[a.pressure] - p[b.pressure]) || (b.age - a.age))[0] || null; };
const needsA = (r) => activeR(r) && ['Draft ready', 'Draft ready · source stale', 'Escalated', 'Routing recommended', 'Internal ageing'].indexOf(r.engine) >= 0;
const advOf = (r) => MDx.advisors.find((a) => a.id === r.advisor);
const acct = (id) => (MDx.byId[id] || {}).account || id;

/* ---------- morning run ---------- */
const RUN_STEPS = [
  { label: 'Pulled Salesforce renewal state', at: '06:09', detail: '363 opportunities, 4 mailbox groups authorized' },
  { label: 'Read new renewal correspondence', at: '06:11', detail: '212 messages since yesterday 06:08' },
  { label: 'Matched 34 incoming messages to renewals', at: '06:13', detail: '33 deterministic, 1 sent to exceptions' },
  { label: 'Filed 11 supporting documents', at: '06:15', detail: '10 filed to Salesforce Files, 1 write rejected' },
  { label: 'Re-evaluated 363 renewals', at: '06:18', detail: 'conditions, ageing, deadline pressure, evidence' },
  { label: 'Built advisor consolidations', at: '06:21', detail: '3 advisors would have received duplicate messages' },
  { label: "Prepared today's decision worklist", at: '06:24', detail: '12 communications, 5 decisions, 3 exceptions' },
];

const HELP_IDS = ['X1', 'X5', 'X11'];
function help() { return MDx.exceptions.filter((x) => x.status === 'open' && HELP_IDS.indexOf(x.id) >= 0); }
function runCounts() {
  const act = MDx.renewals.filter(activeR);
  const drafts = MDx.drafts.filter((d) => d.state === 'ready').length;
  const dec = decisions().length;
  const exc = help().length;
  return {
    evaluated: MDx.renewals.length, active: act.length, auto: 41, drafts, decisions: dec, exceptions: exc,
    normal: act.length - drafts - dec - exc,
    queue: MDx.exceptions.filter((x) => x.status === 'open').length - exc,
  };
}

/* ---------- settled automatically ---------- */
const LEDGER = [
  { id: 'L1', ico: 'check', tone: '', count: 18, text: '<b>18 blocking conditions resolved</b> from documents and replies that arrived overnight', meta: 'No reminder was sent for any of them',
    items: ['Brightlin Foods — 2025 P&L observed and filed, B3 closed', 'Northbay Marine Services — client selection returned, B8 closed', 'Ridgeway Cold Chain — signed affidavit observed, B4 closed', 'Wexford Analytics — questionnaire submitted, B2 closed', 'Harborline Freight — response detected, renewal moved to monitoring', '13 more across the book'] },
  { id: 'L2', ico: 'mail', tone: 'blue', count: 34, text: '<b>34 incoming messages matched</b> to the right renewal and thread', meta: '33 matched deterministically · 1 could not be matched safely and is in exceptions',
    items: ['Sender identity matched a known Salesforce contact — 21', 'Existing observed thread — 9', 'Attachment association — 3', 'Low confidence, escalated to a person — 1'] },
  { id: 'L3', ico: 'file', tone: 'blue', count: 11, text: '<b>11 client documents filed</b> against their renewal with provenance', meta: '1 filing write was rejected by Salesforce and is being retried',
    items: ['Prior-year P&L — 4', 'Tax return — 3', 'Financial statement — 2', 'Organizational document — 1', 'Photo ID — 1 (filing failed, exception X9)'] },
  { id: 'L4', ico: 'ban', tone: 'slate', count: 7, text: '<b>7 reminders suppressed</b> because a response had already arrived or a native message was in flight', meta: 'Duplicate outreach prevented before it reached a client',
    items: ['Grayson Precision — native Salesforce payment reminder sent Nov 15', 'Kilroy Structural Steel — inbound worksheet may already satisfy the condition', 'Sable Plastics — client replied 40 minutes before the send window', 'Union Point Contractors — duplicate document request prevented pre-send', '3 more inside the five-day suppression window'] },
  { id: 'L5', ico: 'arrow-right', tone: '', count: 5, text: '<b>5 renewals advanced</b> to a new operational state without human involvement', meta: 'Close Chain states published back into the shared renewal state',
    items: ['Harlow & Reese LLP — selection verified, coverage grid staged', 'Northbay Marine Services — entered Lock pending', 'Pinnacle Orthopedics — grid restaged on the corrected mapping', 'Fairmount Metal Finishing — release gate opened (shadow)', 'Ashfield Plastics — billing state opened'] },
  { id: 'L6', ico: 'layers', tone: 'blue', count: 3, text: '<b>3 advisor books consolidated</b> so no advisor receives duplicate messages today', meta: 'Halbrook Risk Partners, Ferris & Lowe Advisory, Meridian Trust Advisors',
    items: ['Halbrook Risk Partners — 4 renewals into 1 brief', 'Ferris & Lowe Advisory — 2 renewals into 1 brief', 'Meridian Trust Advisors — 2 renewals into 1 brief'] },
];

/* ---------- decisions the agent prepared for a person ---------- */
const DEC = [
  {
    id: 'DEC-1', conf: 'High', expected: 'Unblock the submission', source: "Inbound Nov 14 16:02 — Joel Mercer acknowledged the request", renewal: 'R-2026-0147', draft: 'DR-1', when: 'Act today', tone: 'today', autonomy: 'recommends', perm: 'send',
    verb: 'Escalate the 2025 P&L through Dana Halbrook',
    body: "The P&L has been outstanding <b>14 days</b> despite three attempts, and Joel confirmed on Nov 14 that it is coming without sending it. Underwriting is separately waiting on the ownership clarification it raised the same day. Both are owed by the same party, so one advisor message covers them; the AmRisk request is still inside its normal window and needs nothing today.",
    chips: [{ t: 'red', l: 'Submission 17 days overdue' }, { t: 'red', l: 'B3 · 14 days' }, { t: 'amber', l: 'B5 · 3 days' }, { t: '', l: 'B7 with AmRisk · monitor' }],
    approveLabel: 'Approve and send',
    reasoning: {
      condition: '2025 P&L statement outstanding', age: '14 days', attempts: '3 · Oct 30, Nov 6, Nov 12', response: 'Acknowledged Nov 14, not delivered', deadline: 'Submission passed · selection in 13 days',
      conclusion: 'Escalation through the advisor is warranted because three direct attempts have not produced the document, the client has confirmed awareness without acting, and calendar pressure has moved from the submission window into the selection window. The message covers the ownership clarification in the same breath because it is owed by the same party and has never been chased on its own.',
      alt: 'Sending client-direct again would repeat an attempt that has already failed twice. Holding would leave a renewal with three open conditions unattended for a fourth day.',
    },
  },
  {
    id: 'DEC-2', conf: 'High', expected: 'Avoid four separate advisor emails', source: "Four prepared drafts, all routed to dana.halbrook@halbrookrisk.com", advisor: 'A1', when: 'Act today', tone: 'today', autonomy: 'prepared', perm: 'send',
    verb: 'Consolidate 4 Halbrook renewals into one advisor brief',
    body: "Four renewals in Dana Halbrook's book need advisor contact today. Sent as drafted that is four engine messages to one person in one morning, on unrelated accounts. I combined them into a single brief that keeps each account in its own section with its own ask.",
    chips: [{ t: 'blue', l: '4 renewals' }, { t: '', l: '43 policies in book' }, { t: 'amber', l: 'Last advisor touch 6 days ago' }],
    covers: ['R-2026-0147', 'R-2026-0311', 'R-2026-0283', 'R-2026-0143'],
    approveLabel: 'Approve brief',
    reasoning: {
      condition: 'Same-day advisor duplication', age: '—', attempts: '4 individual drafts prepared', response: 'n/a', deadline: 'Selection in 13 days on 3 of 4',
      conclusion: 'One advisor should not receive several unrelated engine messages on the same day when they can reasonably be consolidated. All four renewals are advisor-owed or advisor-routed, so a single brief preserves the specific ask per account while protecting the relationship.',
      alt: 'Sending individually is available per renewal if you want one account handled separately.',
    },
  },
  {
    id: 'DEC-3', conf: 'Blocked', expected: 'Human clarification required', source: "Wire observed Nov 16 · WIRE-77120 · remitter \"SBP Holdings LLC\"", renewal: 'R-2026-0143', when: 'Act today', tone: 'blocked', autonomy: 'blocked', perm: 'receipt',
    verb: 'Decide which renewal the $92,300 wire belongs to',
    body: "A wire arrived Nov 16 for exactly the premium expected on two different renewals, and the remitter name resolves to neither account cleanly. I will not guess between them, so I blocked automatic confirmation and held the payment reminder that was otherwise due today.",
    chips: [{ t: 'red', l: 'Ambiguous receipt' }, { t: '', l: 'Sunbelt Poultry · $92,300' }, { t: '', l: 'Bellweather Textiles · $92,300' }, { t: 'amber', l: 'Reminder held' }],
    approveLabel: 'Open receipt decision', route: '#/close/R-2026-0143/receipt',
    reasoning: {
      condition: 'Receipt matching ambiguity', age: '18 hours', attempts: 'Invoice INV-4471 open since Nov 6', response: 'Wire observed Nov 16, remitter name does not match either account exactly', deadline: 'Payment due Dec 31',
      conclusion: 'Two renewals carry the same expected premium and the remitter name resolves to neither account cleanly. Confirming the wrong one would close a payment condition on a renewal that has not paid and leave a paid renewal blocked at the release gate. Accounting decides.',
      alt: 'The agent has already withheld the payment reminder on both renewals to avoid chasing money that may have arrived.',
    },
  },
  {
    id: 'DEC-4', conf: 'High', expected: 'Clear the grid for comparison', source: "HarlowReese_Selection_2026.xlsx and HarlowReese_AmRisk_Lock.pdf", renewal: 'R-2026-0158', when: 'Act today', tone: 'week', autonomy: 'prepared', perm: 'approve',
    verb: 'Approve the prepared coverage grid',
    body: "I found the returned selection, matched the AmRisk lock, parsed seven coverage lines, mapped every risk code, and reconciled all premiums exactly. Nothing disagrees. What remains is the judgment I do not make — whether this is the coverage Madison intends to issue.",
    chips: [{ t: 'green', l: '7 of 7 exact match' }, { t: '', l: '$128,450 total premium' }, { t: '', l: 'Lock received Nov 15' }, { t: 'amber', l: '2026 shadow — no production write' }],
    approveLabel: 'Review and approve grid', route: '#/close/R-2026-0158/grid',
    reasoning: {
      condition: 'Coverage grid staged for approval', age: '1 day', attempts: 'Worksheet Nov 10 · AmRisk lock Nov 15', response: 'Both artifacts parsed cleanly', deadline: 'Payment due Dec 31',
      conclusion: 'Every required value reconciles between the client worksheet and the authoritative locked selection, so the grid met the staging bar automatically. The remaining judgment — is this the coverage Madison intends to issue — is not one the agent makes.',
      alt: 'Rejecting returns the grid to staging with a reason and commits nothing.',
    },
  },
  {
    id: 'DEC-5', conf: 'Moderate', expected: 'Stop spending attempts on a dead channel', source: "Five outbound messages Oct 14 – Nov 10, no inbound reply observed", renewal: 'R-2026-0260', when: 'Act today', tone: 'blocked', autonomy: 'recommends', perm: 'hold',
    verb: 'Route Larkspur Senior Living to concierge, or keep chasing',
    body: "Five attempts across <b>34 days</b> have produced no response from the client or the advisor, and the advisor's portal access expired in September, so the native reminders are not landing either. Continuing the same cadence spends attempts on a channel that demonstrably is not working. I recommend concierge routing; I do not route renewals.",
    chips: [{ t: 'red', l: '5 attempts · no response' }, { t: 'red', l: 'Submission overdue' }, { t: 'amber', l: 'Advisor portal expired' }, { t: '', l: '$132,700 prior premium' }],
    approveLabel: 'Approve routing recommendation',
    reasoning: {
      condition: 'B15 · dormant or closing', age: '34 days', attempts: '5 · Oct 14 through Nov 10', response: 'None observed on any channel', deadline: 'Submission passed · selection in 13 days',
      conclusion: 'Sustained non-response has met the dormant threshold with closing calendar pressure. Continuing the same cadence spends attempts on a channel that is demonstrably not working. Whether this becomes a concierge case is an operational judgment, so the agent stops here.',
      alt: 'Keeping active chase is available, and the expired advisor portal account is already on the standing audit.',
    },
  },
  {
    id: 'DEC-6', conf: 'Blocked', expected: 'Human clarification required', source: "Quinault_Selection.xlsx vs Quinault_AmRisk_Lock_2026.pdf", renewal: 'R-2026-0178', when: 'Act this week', tone: 'blocked', autonomy: 'blocked', perm: 'map',
    verb: 'Resolve one unmapped coverage and one premium conflict',
    body: "Six of seven coverage lines reconciled. \"Equipment breakdown — contingent\" has no confirmed risk code, and excess liability reads $41,880 on the worksheet against <b>$41,180</b> on the AmRisk lock. Premium comparison is exact with no tolerance, so I staged nothing.",
    chips: [{ t: 'amber', l: 'Unmapped coverage' }, { t: 'red', l: '$700 premium conflict' }, { t: '', l: 'Lock is authoritative' }],
    approveLabel: 'Open verification', route: '#/close/R-2026-0178/verify',
    reasoning: {
      condition: 'Selection verification failed', age: '2 days', attempts: 'Worksheet Nov 11 · lock Nov 15', response: 'Parsed, compared, blocked', deadline: 'Payment due Dec 31',
      conclusion: 'Premium comparison against the locked artifact is exact with no tolerance, and an unknown coverage label may never become a Salesforce coverage record. A partial parse does not become a partial grid, so nothing was staged.',
      alt: 'The mapping decision is preserved in the registry as an alias, so this label maps automatically next time.',
    },
  },
  {
    id: 'DEC-7', conf: 'Moderate', expected: 'Restore a working outreach path', source: "Nov 1 message to errol@whitlockandsons.com returned undeliverable", renewal: 'R-2026-0294', when: 'Act this week', tone: 'week', autonomy: 'recommends', perm: 'hold',
    verb: 'Confirm or replace a stale advisor relationship',
    body: "Iron Kettle has not started its questionnaire and the only outreach path is broken: <b>412 days</b> since any Whitlock & Sons activity and the Nov 1 message bounced. Chasing through a dead address wastes the attempt and leaves the client unaware anything is outstanding, so I am holding rather than sending.",
    chips: [{ t: 'amber', l: 'Advisor undeliverable' }, { t: '', l: 'B1 · 19 days' }, { t: '', l: 'Held by the agent' }],
    approveLabel: 'Approve hold and flag advisor',
    reasoning: {
      condition: 'B1 · questionnaire not started', age: '19 days', attempts: '1 · Nov 1, delivery failed', response: 'Bounce observed', deadline: 'Submission passed',
      conclusion: 'Chasing through an advisor whose address bounces wastes the attempt and leaves the client unaware anything is outstanding. The relationship needs confirming or reassigning before outreach resumes, which is a Madison decision.',
      alt: 'Redirecting client-direct is available, but 95% of the book is advisor-originated and bypassing the advisor changes the relationship.',
    },
  },
];
function decisions() { return DEC; }

/* ---------- prepared and ready ---------- */
function prepared() {
  const ready = MDx.drafts.filter((d) => d.state === 'ready');
  return [
    { id: 'P1', ico: 'mail', label: 'Outreach drafts awaiting approval', n: ready.filter((d) => !d.internal).length, note: 'each with the condition, the attempt history, and the reason another touch adds something', route: '#/outreach' },
    { id: 'P2', ico: 'layers', label: 'Advisor briefs assembled', n: 3, note: 'consolidating 8 renewals that would otherwise send separately', route: '#/outreach' },
    { id: 'P3', ico: 'file', label: 'Internal notes to underwriting and AmRisk', n: ready.filter((d) => d.internal).length, note: 'internal ageing surfaces, no client contact implied', route: '#/outreach' },
    { id: 'P4', ico: 'layers', label: 'Coverage grids staged for approval', n: MDx.renewals.filter((r) => r.close && r.close.state === 'GRID_STAGED').length, note: 'parsed, mapped, and reconciled against the AmRisk lock', route: '#/close' },
    { id: 'P5', ico: 'receipt', label: 'Receipts ready to confirm', n: MDx.renewals.filter((r) => r.close && r.close.receipt && r.close.receipt.match !== 'confirmed').length, note: 'structured accounting confirmation — no bank feed is connected', route: '#/close' },
  ];
}

/* ---------- monitoring ---------- */
function monitoring() {
  const heroes = ['R-2026-0166', 'R-2026-0246', 'R-2026-0233', 'R-2026-0189', 'R-2026-0151', 'R-2026-0222'];
  const why = {
    'R-2026-0166': ['Awaiting AmRisk locked selection', 'Requested Nov 13 · normal turnaround 5 days'],
    'R-2026-0246': ['Awaiting revised actuarial output after a limit change', 'Change request Nov 13 · inside turnaround'],
    'R-2026-0233': ['Awaiting actuarial output; NKLL request already prepared', 'Submitted Nov 9 · day 8'],
    'R-2026-0189': ['Waiting on underwriting with a complete submission', 'Day 11 · above the 7-day internal threshold, escalation note drafted'],
    'R-2026-0151': ['DEB form signature outstanding in a live DocuSign envelope', 'Signed Nov 14 on 1 of 2 documents'],
    'R-2026-0222': ['Onboarding form outstanding — funding choice unknown', 'Reminder prepared, not yet due'],
  };
  return heroes.map((id) => ({ r: MDx.byId[id], why: why[id][0], meta: why[id][1] })).filter((x) => x.r);
}

/* ---------- agent activity ---------- */
function activity() {
  const runEvents = [
    { at: '06:09', text: 'Pulled Salesforce renewal state for 363 renewals', tone: 'run' },
    { at: '06:13', text: 'Matched 34 incoming messages to renewals · 1 escalated', tone: 'run' },
    { at: '06:15', text: 'Filed 11 client documents with provenance', tone: 'approve' },
    { at: '06:18', text: 'Re-evaluated 363 renewals · 18 blocking conditions resolved', tone: 'approve' },
    { at: '06:19', text: 'Suppressed 7 unnecessary reminders', tone: 'dismiss' },
    { at: '06:21', text: 'Consolidated 3 advisor books · 8 renewals', tone: 'flag' },
    { at: '06:24', text: 'Prepared 12 communications for review', tone: 'run' },
    { at: '06:26', text: 'Escalated 5 renewals for human decision', tone: 'override' },
    { at: '06:28', text: 'Flagged 3 items the agent could not safely determine', tone: 'alert' },
  ];
  const human = [];
  MDx.renewals.forEach((r) => (r.timeline || []).forEach((e) => { if (e.src === 'user' || e.src === 'accounting') human.push({ at: e.at, text: e.title + ' — ' + r.account, meta: e.detail, tone: e.title.indexOf('sent') >= 0 ? 'approve' : 'override', renewal: r.id }); }));
  human.sort((a, b) => new Date(b.at) - new Date(a.at));
  return { runEvents, human: human.slice(0, 14) };
}

/* ---------- synthesis for the briefing ---------- */
const ASSESS = {
  'R-2026-0147': ['Submission is at risk and the client is the constraint.', 'Three conditions are active, but only one needs action today. The <b>2025 P&L</b> has been outstanding 14 days across three attempts and the client acknowledged the request on Nov 14 without delivering it. The <b>ownership clarification</b> underwriting asked for is only 3 days old but has never been chased on its own, and it is owed by the same party — so it belongs in the same message. The <b>actuarial return</b> is 6 days out with AmRisk, inside normal turnaround, and needs nothing from Madison today.', 'The agent recommends escalating both client-owed items through Dana Halbrook with Joel Mercer copied, and continuing to monitor AmRisk.'],
  'R-2026-0212': ['Two sources disagree and the agent stopped rather than guess.', 'Salesforce still shows this renewal at <b>Pricing With Client</b>, but an inbound message on Nov 14 carries an attachment that looks like the completed selection worksheet. Confidence was too low to attach it. Sending the selection reminder that was otherwise due would contradict a document the client believes they have already returned.', 'The agent is holding all outreach and has raised an exception for a person to confirm what the attachment is.'],
  'R-2026-0260': ['Five attempts have produced nothing and the channel itself looks broken.', 'No response from client or advisor has been observed in <b>34 days</b>, and the advisor\'s portal access expired in September, so native reminder jobs are not reaching them either. Submission has passed and selection closes in 13 days.', 'The agent recommends concierge routing. It does not route renewals — that decision is yours.'],
  'R-2026-0311': ['Nothing has started and four attempts have gone unanswered.', 'The questionnaire has never been opened since the renewal was created on Oct 22. Four messages between Oct 27 and Nov 13 produced no observed reply from the client or from Halbrook Risk Partners.', 'The agent has escalated to rung 4 of 5 and drafted an advisor escalation. The next rung is a routing recommendation, which requires a person.'],
  'R-2026-0283': ['A partial delivery arrived, so the next message has to acknowledge it.', 'Underwriting asked for VIN-level fleet detail on Nov 4. A partial schedule came in on Nov 15 — real progress — but the VINs for 14 units added in September and the driver MVR summary are still missing. Repeating the original request would read as if nothing had arrived.', 'The agent folded both outstanding items into the Halbrook advisor brief and noted what did arrive.'],
};
function assess(r) {
  if (ASSESS[r.id]) return ASSESS[r.id];
  const oc = openC(r); const p = prim(r);
  if (!oc.length) return [r.close ? 'No chase condition is open — the renewal sits with the Close Chain.' : 'Nothing is blocking this renewal today.', 'The agent evaluated it during the morning run and found no condition requiring outreach.', r.close ? 'Close Chain state is ' + (window.CLOSE_LABEL ? window.CLOSE_LABEL[r.close.state] : r.close.state) + '.' : 'It stays under monitoring.'];
  const lead = oc.length > 1 ? oc.length + ' conditions are active on this renewal.' : 'One condition is holding this renewal.';
  const detail = oc.map((c) => '<b>' + c.item + '</b> is owed by ' + c.owing.toLowerCase() + ' and has been open ' + c.age + ' days' + (c.attempts ? ' across ' + c.attempts + ' attempt' + (c.attempts > 1 ? 's' : '') : ' with no attempt yet') + '.').join(' ');
  const close = 'The agent recommends: ' + (r.rec ? r.rec.action.toLowerCase() + (p ? ' — ' + p.next.toLowerCase() : '') : 'monitor') + '.';
  return [lead, detail, close];
}

function consolidationGroups() {
  const by = {};
  MDx.drafts.filter((d) => d.state === 'ready' || d.state === 'held').forEach((d) => {
    const r = MDx.byId[d.renewal]; if (!r || !r.advisor) return;
    const a = advOf(r); if (!a) return;
    if (d.to !== a.email && (d.cc || '').indexOf(a.email) < 0) return;
    (by[a.id] = by[a.id] || []).push(d);
  });
  return Object.keys(by).filter((k) => by[k].length > 1).map((k) => ({ advisor: MDx.advisors.find((a) => a.id === k), drafts: by[k] }));
}

Object.assign(window, { consolidationGroups, AG: { consolidationGroups, help, RUN_STEPS, LEDGER, runCounts, decisions, prepared, monitoring, activity, assess, openC, activeR, prim, needsA, advOf, acct } });
