import type { Paragraph } from "@sec-cybert/schemas/paragraph.ts";

export const PROMPT_VERSION = "v3.0";

/** System prompt for all Stage 1 annotation and benchmarking. */
export const SYSTEM_PROMPT = `You are an expert annotator classifying paragraphs from SEC cybersecurity disclosures (Form 10-K Item 1C and Form 8-K Item 1.05 filings).

For each paragraph, assign a content_category and specificity level.

═══ CONTENT CATEGORY ═══

Assign the single most applicable category:

"Board Governance" — Board/committee oversight of cyber risk, briefing cadence, board cyber expertise. Assign when the board or a board committee is the grammatical subject.
"Management Role" — CISO/CTO/CIO identification, qualifications, reporting lines. Assign when a management role is the grammatical subject.
"Risk Management Process" — Risk assessment, framework adoption, vulnerability management, monitoring, IR planning, ERM integration. Assign when the company's OWN internal processes are the topic.
"Third-Party Risk" — Vendor/supplier security oversight, contractual security standards. Assign ONLY when vendor oversight is the CENTRAL topic, not a component of internal processes.
"Incident Disclosure" — Description of actual cybersecurity incidents: what happened, when, scope, response actions. Must reference a real event. Includes: incident narrative, incident response actions, AND descriptions of affected data/systems scope or operational impact of a disclosed incident.
"Strategy Integration" — Business/financial impact, cyber insurance, budget, materiality assessments. Includes standalone materiality conclusions with no incident narrative.
"None/Other" — Forward-looking disclaimers, section headers, cross-references, non-cybersecurity content. NO substantive disclosure at all.

CATEGORY TIEBREAKERS:
  - Paragraph DESCRIBES what happened in an incident (dates, access, encryption, scope, response actions) → Incident Disclosure
  - Paragraph ONLY discusses financial cost, insurance, or materiality of an incident WITHOUT describing the event → Strategy Integration (even if it says "the incident" or "the cybersecurity incident")
  - Brief mention of a past incident + materiality conclusion as the main point → Strategy Integration
  - Standalone materiality conclusion with no incident reference → Strategy Integration
  - Materiality disclaimers ("have not materially affected our business strategy, results of operations, or financial condition") → Strategy Integration, even if boilerplate. A cross-reference to Risk Factors appended to a materiality assessment does NOT change the classification. Only pure cross-references with no materiality conclusion are None/Other.
  - SPACs and shell companies explicitly stating they have no operations, no cybersecurity program, or no formal processes → None/Other regardless of incidental mentions of board oversight or risk acknowledgment. The absence of a program is not a description of a program.
  - Internal processes mentioning vendors as one component → Risk Management Process
  - Requirements imposed ON vendors → Third-Party Risk
  - Board oversight mentioned briefly + management roles as main focus → Management Role
  - Management mentioned briefly + board oversight as main focus → Board Governance

PERSON-VS-FUNCTION TEST (Management Role vs Risk Management Process):
  If a paragraph is about the PERSON (qualifications, credentials, background, tenure, career history) → Management Role.
  If it's about what the role/program DOES (processes, activities, tools, frameworks) → Risk Management Process, even if a CISO/CIO/CTO title appears.
  Test: would the paragraph still make sense if you removed the person's name, title, and credentials? If yes → the paragraph is about the function, not the person → Risk Management Process.

═══ SPECIFICITY ═══

"Generic Boilerplate" — Could paste into any company's filing unchanged. No named entities, frameworks, roles, dates, or specific details.
"Sector-Adapted" — Names a specific, recognized framework or standard (NIST, ISO 27001, SOC 2, CIS, GDPR, PCI DSS, HIPAA) but contains nothing unique to THIS company. General security practices (penetration testing, vulnerability scanning, tabletop exercises) do NOT qualify — only named standards do.
"Firm-Specific" — Contains at least one fact that identifies something unique to THIS company's disclosure. See the IS/IS-NOT lists below.
"Quantified-Verifiable" — Contains TWO or more hard facts that an outsider could independently verify. Count ONLY: specific dates (month+year or exact date), dollar amounts, headcounts, percentages, named third-party firms (Mandiant, CrowdStrike, Deloitte), named tools/products (Splunk, Azure Sentinel, Dropbox Sign). Do NOT count toward this threshold: named roles, named committees, team compositions, reporting cadences, named internal programs, or organizational structure — these are Firm-Specific, not Quantified-Verifiable.

DECISION TEST — ask in order, stop at the first "yes":
  1. Count hard verifiable facts ONLY (specific dates, dollar amounts, headcounts/percentages, named third-party firms, named products/tools). TWO or more? → Quantified-Verifiable
  2. Does it contain at least one fact from the ✓ IS list below? If yes → Firm-Specific. (Even one ✓ IS fact is enough, regardless of how many ✗ NOT items also appear.)
  3. Does it name a recognized standard (NIST, ISO 27001, SOC 2, CIS, GDPR, PCI DSS, HIPAA)? → Sector-Adapted
  4. None of the above? → Generic Boilerplate

✓ IS A SPECIFIC FACT (any ONE of these → at least Firm-Specific):
  - Cybersecurity-specific titles: CISO, CTO, CIO, VP of IT/Security, Information Security Officer, Director of IT Security, HSE Director overseeing cybersecurity
  - Named non-generic committees: Technology Committee, Cybersecurity Committee, Risk Committee, ERM Committee (NOT "Audit Committee" — that is standard)
  - Specific team/department compositions: "Legal, Compliance, and Finance"
  - Specific dates: "In December 2023", "On May 6, 2024", "fiscal 2025"
  - Named internal programs with unique identifiers: "Cyber Incident Response Plan (CIRP)"
  - Named products, systems, tools: Splunk, CrowdStrike Falcon, Azure Sentinel
  - Named third-party firms: Mandiant, Deloitte, CrowdStrike
  - Specific numbers: headcounts, dollar amounts, percentages, exact durations
  - Certification claims: "We maintain ISO 27001 certification" (more than just naming the standard)

✗ IS NOT A SPECIFIC FACT (do NOT extract these in specific_facts, do NOT use them to justify Firm-Specific):
  - Generic governance: "the Board", "Board of Directors", "management", "Audit Committee", "the Committee"
  - Generic C-suite: CEO, CFO, COO, President, General Counsel — these exist at every company
  - Unnamed entities: "third-party experts", "external consultants", "cybersecurity firms", "managed service provider"
  - Generic cadences: "quarterly", "annual", "periodic", "regular" — without exact dates
  - Boilerplate phrases: "cybersecurity risks", "material adverse effect", "business operations", "financial condition"
  - Standard incident language: "forensic investigation", "law enforcement", "regulatory obligations", "incident response protocols"
  - Vague quantifiers: "certain systems", "some employees", "a number of", "a portion of"
  - Common practices: "penetration testing", "vulnerability scanning", "tabletop exercises", "phishing simulations", "security awareness training"
  - Generic program names: "incident response plan", "business continuity plan", "cybersecurity program", "Company-wide training"
  - Company self-references: the company's own name, "the Company", filing form types (Form 8-K, Form 10-K)

VALIDATION STEP — before finalizing specificity:
  Review your specific_facts list. Remove any fact that appears on the ✗ NOT list above. If NO facts remain after filtering, the paragraph is Generic Boilerplate (or Sector-Adapted if it names a standard). Do not let ✗ NOT items inflate your specificity rating.

CALIBRATION EXAMPLES:
  "On April 24, 2024, we became aware of unauthorized access to our Dropbox Sign environment" → Quantified-Verifiable (date + named product = two hard verifiable facts)
  "Our CISO reports quarterly to the Audit Committee" → Firm-Specific (CISO is a cybersecurity-specific role; Audit Committee alone would be generic)
  "We follow the NIST Cybersecurity Framework" → Sector-Adapted (named framework, nothing firm-specific)
  "We maintain ISO 27001 certification" → Firm-Specific (holding a certification is a verifiable firm fact, beyond merely naming the standard)
  "The incident is not reasonably likely to materially affect our financial condition" → Generic Boilerplate (boilerplate materiality language)
  "We detected unauthorized access to certain systems and engaged third-party experts" → Generic Boilerplate (no names, no dates, no specifics)
  "In December 2023, we experienced an incident involving unauthorized access to internal systems" → Firm-Specific (specific month/year = one fact, but only one → not QV)
  "A team comprising Legal, Compliance, and Finance assesses cybersecurity threats" → Firm-Specific (specific departmental composition)
  "The Audit Committee receives periodic reports from management on cybersecurity risks" → Generic Boilerplate (Audit Committee is standard, "management" is generic, "periodic" is vague)
  "On May 6, 2024, the Company detected unauthorized access. The Company engaged CrowdStrike to investigate." → Quantified-Verifiable (specific date + named firm = two hard facts)
  "The CISO has 17 years of experience and holds CISSP and CISM certifications" → Quantified-Verifiable (specific number + named certifications = two hard facts)
  "The Company halted operations for approximately two weeks and restored systems by mid-June" → Firm-Specific (approximate duration and approximate date are soft — only one hard fact)
  "Our CISO, supported by a team of 12 security professionals, leads the cybersecurity program" → Quantified-Verifiable (cybersecurity-specific role + specific headcount = two hard facts)
  "Costs related to the incident are expected to be covered by cybersecurity insurance" → Generic Boilerplate (no specific amounts, dates, or named insurers)
  "The Company's ERM program incorporates cybersecurity into 12 risk categories with quarterly reviews" → Firm-Specific (named internal program, but "12 categories" and "quarterly" alone don't reach QV — organizational detail, not hard external facts)

None/Other paragraphs always get Generic Boilerplate.

═══ OUTPUT ═══

Return JSON with: content_category, specific_facts (list what specific facts you found — empty if generic), specificity, category_confidence, specificity_confidence, reasoning (1-2 sentences for category choice).`;

/** Build the user prompt for a single paragraph annotation. */
export function buildUserPrompt(paragraph: Paragraph): string {
  const { filing, text } = paragraph;
  return `Company: ${filing.companyName} (${filing.ticker})
Filing type: ${filing.filingType}
Filing date: ${filing.filingDate}
Section: ${filing.secItem}

Paragraph:
${text}`;
}

// ── Category confusion-axis disambiguation rules ──────────────────────────
// Keyed by sorted pair of disputed categories. Only included when relevant.
const CATEGORY_GUIDANCE: Record<string, string> = {
  "Management Role|Risk Management Process": `MANAGEMENT ROLE vs RISK MANAGEMENT PROCESS — ask: what is the DOMINANT communicative purpose?
  • A named manager (CISO, VP) mentioned once at the beginning, followed by extensive process description → Risk Management Process. The role mention is incidental.
  • Management Role requires the manager's identity, qualifications, or reporting structure to be the PRIMARY content — not just a brief attribution.
  • Test: remove the role mention. Does the paragraph still make sense as a process description? If yes → RMP.`,

  "Risk Management Process|Third-Party Risk": `RISK MANAGEMENT PROCESS vs THIRD-PARTY RISK — ask: is vendor/supplier oversight the CENTRAL topic?
  • "We use third-party consultants for penetration testing" = RMP (third parties support an internal process).
  • "We maintain a vendor oversight program with due diligence and monitoring of third-party controls" = Third-Party Risk (vendor oversight IS the topic).
  • The paragraph must be PRIMARILY about managing vendor/supplier cyber risk to qualify as Third-Party Risk.`,

  "None/Other|Strategy Integration": `NONE/OTHER vs STRATEGY INTEGRATION — ask: is there substantive cybersecurity disclosure?
  • None/Other = NO substantive disclosure at all: section headers, disclaimers, generic IT-dependence language ("our IT systems are important to operations"), forward-looking boilerplate, generic regulatory compliance language ("subject to various regulatory requirements... non-compliance could result in penalties").
  • Strategy Integration = actual discussion of business/financial impact, cyber insurance, budget allocation, or materiality assessment.
  • Generic regulatory risk language (acknowledging regulations exist, non-compliance would be bad) is None/Other — it makes no materiality assessment and describes no strategy. It only becomes Strategy Integration if it explicitly assesses whether regulatory risks have "materially affected" the business.
  • If the paragraph only establishes that the company has IT systems and data without describing any program, process, or strategy → None/Other.`,

  "Board Governance|Management Role": `BOARD GOVERNANCE vs MANAGEMENT ROLE — ask: who is the grammatical subject?
  • Board or board committee taking oversight actions (receiving briefings, reviewing risks) → Board Governance.
  • Named executive with qualifications, experience, or reporting lines → Management Role.
  • When both appear, the PRIMARY focus wins: board oversight with a brief management mention → Board Governance, and vice versa.`,

  "Board Governance|Risk Management Process": `BOARD GOVERNANCE vs RISK MANAGEMENT PROCESS — ask: oversight or operations?
  • Board/committee receiving reports, overseeing risk, setting policy → Board Governance.
  • Company describing HOW it assesses, monitors, mitigates risks → Risk Management Process.
  • "The board receives quarterly cybersecurity briefings" → Board Governance. "We conduct quarterly risk assessments; the board is informed" → RMP (process is primary content).`,

  "None/Other|Risk Management Process": `NONE/OTHER vs RISK MANAGEMENT PROCESS — ask: does the paragraph describe actual cybersecurity activities?
  • Describing actual processes (monitoring, assessment, vulnerability management, training programs) → RMP.
  • Only stating the company has IT systems, collects data, or faces cyber risks — without describing what it DOES about them → None/Other.
  • Generic regulatory compliance language ("subject to various regulations... non-compliance could result in penalties") is None/Other — it describes no actual compliance activities. If a specific regulation is named (GDPR, HIPAA, PCI DSS) but no company-specific program is described → RMP at Specificity 2 (named standard).`,

  "Risk Management Process|Strategy Integration": `RISK MANAGEMENT PROCESS vs STRATEGY INTEGRATION — ask: operational or strategic?
  • Describing HOW risks are assessed, monitored, mitigated → Risk Management Process.
  • Discussing materiality, financial impact, insurance, budget, or business implications → Strategy Integration.
  • ERM integration described as a process = RMP. Materiality conclusions drawn from the process = Strategy.`,
};

// ── Specificity disambiguation rules ──────────────────────────────────────
const SPECIFICITY_GUIDANCE_1_VS_3 = `SPECIFICITY: GENERIC BOILERPLATE (1) vs FIRM-SPECIFIC (3)
The #1 annotator error is counting NOT-list items as specific facts. Before deciding, filter strictly:
  ✗ "Head of IT department", "IT Manager" = generic IT leadership, NOT on IS list (IS list: CISO, CTO, CIO, VP of IT/Security, Information Security Officer, Director of IT Security)
  ✗ Generic program names without unique identifiers: "incident response plan", "cybersecurity program", "Third-Party Risk Management Program" → all on NOT list. Compare with IS example: "Cyber Incident Response Plan (CIRP)" which has a distinguishing abbreviation.
  ✗ Company self-references ("the Company", "the Bank", company name) → NOT list
  ✗ Common practices (pen testing, vulnerability scanning, tabletop exercises) → NOT list
  ✗ Generic governance (Board, Audit Committee, management, CEO, CFO) → NOT list
After filtering all NOT-list items, if zero IS-list facts remain → Generic Boilerplate.`;

const SPECIFICITY_GUIDANCE_3_VS_4 = `SPECIFICITY: FIRM-SPECIFIC (3) vs QUANTIFIED-VERIFIABLE (4)
Count ONLY these as hard verifiable facts for the 2-fact QV threshold:
  ✓ Specific dates (month+year or exact date): "In January 2022", "On May 6, 2024"
  ✓ Dollar amounts, headcounts, percentages: "$2M", "12 professionals", "25%"
  ✓ Named third-party firms: Mandiant, CrowdStrike, Deloitte
  ✓ Named products/tools: Splunk, Azure Sentinel, CrowdStrike Falcon
  ✓ Named certifications: CISSP, CISM (but NOT generic degrees like "BS in Management")
  ✓ Years of experience: "17 years", "over 20 years" (these are specific numbers)
Do NOT count toward QV:
  ✗ Named roles (CISO, CIO) — these trigger Firm-Specific but do NOT count as QV facts
  ✗ Named committees — Firm-Specific, not QV
  ✗ Named frameworks (NIST, ISO 27001) — trigger Sector-Adapted, not QV
  ✗ Team compositions, reporting structures — Firm-Specific, not QV
  ✗ Generic degrees without named university
Need 2+ QV-eligible facts. One fact = stays at Firm-Specific.`;

/**
 * Build the Stage 2 judge prompt with disagreement-aware disambiguation.
 * Dynamically includes only the guidance relevant to the specific dispute.
 */
export function buildJudgePrompt(
  paragraph: Paragraph,
  annotations: Array<{
    content_category: string;
    specificity_level: number;
    reasoning: string;
  }>,
): string {
  // ── Analyze the disagreement ─────────────────────────────────────────
  const cats = annotations.map((a) => a.content_category);
  const specs = annotations.map((a) => a.specificity_level);
  const uniqueCats = [...new Set(cats)].sort();
  const uniqueSpecs = [...new Set(specs)].sort((a, b) => a - b);
  const catDisagree = uniqueCats.length > 1;
  const specDisagree = uniqueSpecs.length > 1;

  // Vote distribution strings
  const catCounts = new Map<string, number>();
  for (const c of cats) catCounts.set(c, (catCounts.get(c) ?? 0) + 1);
  const catDistStr = [...catCounts.entries()]
    .sort(([, a], [, b]) => b - a)
    .map(([c, n]) => `${c} (${n})`)
    .join(", ");

  const specCounts = new Map<number, number>();
  for (const s of specs) specCounts.set(s, (specCounts.get(s) ?? 0) + 1);
  const specLabels: Record<number, string> = { 1: "Generic Boilerplate", 2: "Sector-Adapted", 3: "Firm-Specific", 4: "Quantified-Verifiable" };
  const specDistStr = [...specCounts.entries()]
    .sort(([, a], [, b]) => b - a)
    .map(([s, n]) => `${specLabels[s] ?? s} (${n})`)
    .join(", ");

  let focusLine: string;
  if (catDisagree && specDisagree) {
    focusLine = `DISAGREEMENT: BOTH category and specificity.\n  Category votes: ${catDistStr}\n  Specificity votes: ${specDistStr}`;
  } else if (catDisagree) {
    focusLine = `DISAGREEMENT: CATEGORY — ${catDistStr}.\n  Specificity is agreed at ${specLabels[uniqueSpecs[0]!] ?? uniqueSpecs[0]}.`;
  } else {
    focusLine = `DISAGREEMENT: SPECIFICITY — ${specDistStr}.\n  Category is agreed as ${uniqueCats[0]}.`;
  }

  // ── Build targeted guidance ──────────────────────────────────────────
  const guidanceBlocks: string[] = [];

  if (catDisagree) {
    // Generate all pairs and look up guidance
    for (let i = 0; i < uniqueCats.length; i++) {
      for (let j = i + 1; j < uniqueCats.length; j++) {
        const key = [uniqueCats[i], uniqueCats[j]].sort().join("|");
        const guidance = CATEGORY_GUIDANCE[key];
        if (guidance) guidanceBlocks.push(guidance);
      }
    }
  }

  if (specDisagree) {
    const minSpec = Math.min(...specs);
    const maxSpec = Math.max(...specs);
    if (minSpec <= 1 && maxSpec >= 3) guidanceBlocks.push(SPECIFICITY_GUIDANCE_1_VS_3);
    if (minSpec <= 3 && maxSpec >= 4) guidanceBlocks.push(SPECIFICITY_GUIDANCE_3_VS_4);
    // For [1,2] or [2,3] disputes, the general codebook suffices
  }

  const guidanceSection =
    guidanceBlocks.length > 0
      ? `\n═══ DISAMBIGUATION RULES (for this specific dispute) ═══\n\n${guidanceBlocks.join("\n\n")}\n`
      : "";

  // ── Fisher-Yates shuffle for anti-bias ───────────────────────────────
  const shuffled = [...annotations];
  for (let i = shuffled.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffled[i], shuffled[j]] = [shuffled[j]!, shuffled[i]!];
  }

  const annotatorLabels = ["A", "B", "C"] as const;
  const annotatorBlock = shuffled
    .map((a, i) => {
      return `Annotator ${annotatorLabels[i]}: content_category="${a.content_category}", specificity_level=${a.specificity_level}
  Reasoning: "${a.reasoning}"`;
    })
    .join("\n\n");

  const { filing, text } = paragraph;

  return `═══ ADJUDICATION TASK ═══

You are resolving a labeling disagreement among three annotators who applied the codebook above.

${focusLine}
${guidanceSection}
═══ ANALYSIS STEPS ═══

Before providing your label:
1. CRITIQUE each annotator's reasoning against the codebook. Which one(s) applied the rules correctly? Which made identifiable errors?
2. For specificity: independently enumerate IS-list facts in the text. Filter out any NOT-list items the annotators may have counted. How many genuine IS-list facts remain? How many are QV-eligible?
3. Determine the dominant communicative purpose for category.
4. Provide your final label. In your reasoning, explain which annotator(s) you agree with and what specific error(s) the others made.

═══ CONFIDENCE CALIBRATION ═══

Your confidence ratings will be used to weight this label in model training. Calibrate honestly:
  HIGH = one annotator clearly applied the codebook correctly; others made identifiable errors
  MEDIUM = reasonable case for the winning label but you can see why annotators disagreed
  LOW = genuinely ambiguous; the codebook boundary is fuzzy for this text and reasonable experts could disagree

═══ PARAGRAPH ═══

Company: ${filing.companyName} (${filing.ticker})
Filing type: ${filing.filingType}
Filing date: ${filing.filingDate}
Section: ${filing.secItem}

${text}

═══ PRIOR ANNOTATIONS (randomized order) ═══

${annotatorBlock}`;
}
