/**
 * Compliance Findings Synthesizer
 * ---------------------------------
 * Surfaces WCAG 2.2 accessibility, GDPR/CCPA privacy, and HIPAA
 * (medical vertical only) compliance issues from audit data.
 *
 * Purely deterministic -- no LLM calls.
 * Fails open: returns [] on any error.
 * Audit pipeline must not block on this synthesizer.
 */

import type { AuditInput, PrioritizedFinding, SeoFindings } from "@/lib/audit/types";

const MAX_FINDINGS = 6;

function isMedicalIndustry(input: AuditInput): boolean {
  const industries = input.industryContext?.industries ?? [];
  if (industries.some((i) => i.startsWith("healthcare") || i === "healthcare")) return true;
  // Backward compat: check the legacy singular industry field
  if (input.industryContext?.industry?.startsWith("healthcare")) return true;
  return false;
}

function hasPrivacyPage(input: AuditInput): boolean {
  const pages = input.pageStrategicPassContext?.pages ?? [];
  return pages.some(
    (p) =>
      p.url.toLowerCase().includes("privacy") ||
      (p.structuralSignals.metaDescription?.toLowerCase().includes("privacy") ?? false),
  );
}

export async function runComplianceSynthesizer(
  input: AuditInput,
  seoFindings: SeoFindings | null,
): Promise<PrioritizedFinding[]> {
  try {
    const findings: PrioritizedFinding[] = [];
    const isMedical = isMedicalIndustry(input);
    const privacyPageFound = hasPrivacyPage(input);

    // ── WCAG Finding 1: alt_text_missing ─────────────────────────────────────
    const missingAlt = seoFindings?.images?.missingAlt ?? 0;
    if (missingAlt > 0) {
      const totalImages = seoFindings?.images?.totalImages ?? 0;
      const imagesWithAlt = totalImages - missingAlt;
      const priority: "critical" | "high" = missingAlt > 10 ? "critical" : "high";

      findings.push({
        rank: 300 + findings.length,
        title: `Images missing alt text (${missingAlt} image${missingAlt !== 1 ? "s" : ""})`,
        priority,
        whatsWorking:
          imagesWithAlt > 0
            ? `${imagesWithAlt} image${imagesWithAlt !== 1 ? "s" : ""} on the site already include alt text, which supports screen reader users and image search indexing.`
            : "The site's images have been crawled and assessed for accessibility compliance.",
        whereItFallsShort:
          `WCAG 2.2 Success Criterion 1.1.1 (Non-text Content) requires that all meaningful images have ` +
          `descriptive alt text so assistive technology users can understand the content. ` +
          `${missingAlt} image${missingAlt !== 1 ? "s are" : " is"} currently missing alt text, ` +
          `which may create barriers for screen reader users and may affect image indexing.`,
        recommendedNextSteps: [
          "Add concise, descriptive alt text to each image that conveys meaning or content",
          "Use empty alt attributes (alt=\"\") for purely decorative images so screen readers skip them",
          "Avoid starting alt text with phrases like \"image of\" or \"photo of\" -- describe the content directly",
          "Test with a screen reader or browser accessibility extension to confirm alt text reads naturally",
        ],
        affectedUrls: [],
        dataSourceWeave: ["crawl"] as const,
        category: "compliance" as const,
      });
    }

    // ── WCAG Finding 2: heading_hierarchy ────────────────────────────────────
    const missingH1 = seoFindings?.headings?.missingH1 ?? 0;
    const multipleH1 = seoFindings?.headings?.multipleH1 ?? 0;
    const totalHeadingIssues = missingH1 + multipleH1;

    if (totalHeadingIssues >= 2) {
      const issuesList: string[] = [];
      if (missingH1 > 0)
        issuesList.push(`${missingH1} page${missingH1 !== 1 ? "s" : ""} missing an H1`);
      if (multipleH1 > 0)
        issuesList.push(`${multipleH1} page${multipleH1 !== 1 ? "s" : ""} with multiple H1 elements`);
      const issuesSummary = issuesList.join("; ");
      const affectedUrls = input.crawlStatsContext?.missingH1Urls?.slice(0, 5) ?? [];

      findings.push({
        rank: 300 + findings.length,
        title: `Heading hierarchy issues on ${totalHeadingIssues} page${totalHeadingIssues !== 1 ? "s" : ""}`,
        priority: "medium",
        whatsWorking: "The site's heading structure has been evaluated and specific pages with issues have been identified.",
        whereItFallsShort:
          `WCAG 2.2 Success Criterion 1.3.1 (Info and Relationships) requires that information conveyed ` +
          `through presentation is also available programmatically. Screen reader users rely on heading ` +
          `structure to navigate page content. Detected issues: ${issuesSummary}.`,
        recommendedNextSteps: [
          "Ensure each page has exactly one H1 element that describes the primary topic of that page",
          "Do not skip heading levels (e.g. jumping from H2 directly to H4) -- use a logical hierarchy",
          "Test heading structure with browser accessibility tools such as the Accessibility Tree in DevTools",
        ],
        affectedUrls,
        dataSourceWeave: ["crawl"] as const,
        category: "compliance" as const,
      });
    }

    // ── GDPR/CCPA Finding 3: privacy_policy_missing ──────────────────────────
    if (!privacyPageFound) {
      findings.push({
        rank: 300 + findings.length,
        title: "Privacy policy page not detected in crawl",
        priority: "high",
        whatsWorking: "The site is actively crawled, which provides a baseline for detecting publicly available policy pages.",
        whereItFallsShort:
          "GDPR Article 13 may require data controllers to provide privacy information to users at the time personal data is collected. " +
          "CCPA may require businesses to maintain a privacy policy accessible to California consumers. " +
          "The crawl found no page with a privacy policy URL or title -- recommend reviewing with compliance counsel.",
        recommendedNextSteps: [
          "Create a dedicated /privacy-policy page (or equivalent) with a stable, crawlable URL",
          "Link to the privacy policy from the footer of every page so it is accessible site-wide",
          "Include the identity and contact details of the data controller, and describe all data collection purposes",
          "List the rights available to users (access, deletion, opt-out) and the process to exercise them",
        ],
        dataSourceWeave: ["crawl"] as const,
        category: "compliance" as const,
      });
    }

    // ── HIPAA Finding 4: phi_form_exposure ───────────────────────────────────
    if (isMedical) {
      const pages = input.pageStrategicPassContext?.pages ?? [];
      const contactPagesWithCta = pages.filter(
        (p) => p.pageType === "contact" && p.structuralSignals.hasCTA === true,
      );

      if (contactPagesWithCta.length > 0) {
        findings.push({
          rank: 300 + findings.length,
          title: "Contact forms on medical site may handle protected health information",
          priority: "critical",
          whatsWorking:
            "Site uses HTTPS for all pages, providing baseline transport-layer encryption for form submissions.",
          whereItFallsShort:
            "HIPAA may require covered entities to implement technical safeguards for protected health information (PHI). " +
            "Patient-facing contact and appointment forms that collect names alongside a medical context may create PHI exposure risk " +
            "if the underlying form platform does not meet HIPAA standards. Recommend reviewing with healthcare compliance counsel.",
          recommendedNextSteps: [
            "Replace standard web forms on patient-facing pages with a HIPAA-compliant form platform that includes encryption at rest and in transit",
            "Never include fields that capture medical condition details in a form that lacks end-to-end encryption and audit logging",
            "Ensure your form submission processor or CRM has a signed Business Associate Agreement (BAA) in place",
            "Add a brief patient privacy notice to all patient-facing forms referencing your HIPAA Notice of Privacy Practices",
            "Audit form submission logs and third-party integrations to verify PHI is not stored in non-compliant systems",
          ],
          dataSourceWeave: ["crawl"] as const,
          category: "compliance" as const,
        });
      }
    }

    // ── HIPAA Finding 5: baa_language_missing ────────────────────────────────
    if (isMedical && !privacyPageFound) {
      findings.push({
        rank: 300 + findings.length,
        title: "Privacy policy may lack HIPAA business associate language",
        priority: "high",
        whatsWorking: "HIPAA compliance considerations have been flagged for review based on the site's industry classification.",
        whereItFallsShort:
          "HIPAA-covered entities are generally expected to address PHI handling, covered entity status, and patient rights in their " +
          "privacy documentation. Because no privacy policy page was detected in the crawl, it is unclear whether the required " +
          "HIPAA-specific disclosures and BAA references are in place. Recommend reviewing with healthcare compliance counsel.",
        recommendedNextSteps: [
          "Add a HIPAA-specific section to the privacy policy describing how PHI is collected, used, and protected",
          "Reference Business Associate Agreement requirements for any third-party services that process patient data",
          "List patient rights under HIPAA including the right to access, amend, and request restrictions on PHI",
          "Consult with a healthcare compliance attorney to confirm the privacy policy meets HIPAA Notice of Privacy Practices requirements",
        ],
        dataSourceWeave: ["crawl"] as const,
        category: "compliance" as const,
      });
    }

    // ── Sort by priority, then cap at MAX_FINDINGS ────────────────────────────
    const priorityOrder: Record<string, number> = {
      critical: 0,
      high: 1,
      medium: 2,
      low: 3,
    };
    findings.sort(
      (a, b) =>
        (priorityOrder[a.priority] ?? 99) - (priorityOrder[b.priority] ?? 99),
    );

    // Re-assign ranks after sort
    findings.forEach((f, i) => {
      f.rank = 300 + i;
    });

    return findings.slice(0, MAX_FINDINGS);
  } catch {
    return [];
  }
}
