from __future__ import annotations

import datetime as _dt
import hashlib
import re
from dataclasses import dataclass
from pathlib import Path


def _sha256_text(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8", errors="replace")).hexdigest()


def _sha256_file(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def generate_shadow_dossier(*, style_id: str, source_text: str, source_path: str) -> str:
    if style_id.lower() in {
        "if.dave.v1",
        "if.dave.v1.1",
        "if.dave.v1.2",
        "dave",
        "if://bible/dave/v1.0",
        "if://bible/dave/v1.1",
        "if://bible/dave/v1.2",
    }:
        return _generate_dave_v1_2_mirror(source_text=source_text, source_path=source_path)
    raise ValueError(f"Unknown style id: {style_id}")

@dataclass(frozen=True)
class _SourceSection:
    title: str
    body: str
    why_it_matters: str | None = None


_PAGE_SPLIT_RE = re.compile(r"(?m)^===== page-(\d+) =====$")


def _normalize_ocr(text: str) -> str:
    text = re.sub(r"\bAl\b", "AI", text)
    text = text.replace("GenAl", "GenAI")
    text = text.replace("Cl/CD", "CI/CD")
    text = text.replace("olugin", "plugin")
    return text


def _parse_pages(source_text: str) -> list[tuple[str, str]]:
    matches = list(_PAGE_SPLIT_RE.finditer(source_text))
    if not matches:
        return [("doc", source_text.strip())]

    pages: list[tuple[str, str]] = []
    for idx, match in enumerate(matches):
        page_no = match.group(1)
        start = match.end()
        end = matches[idx + 1].start() if idx + 1 < len(matches) else len(source_text)
        pages.append((page_no, source_text[start:end].strip()))
    return pages


def _parse_title_block(lines: list[str]) -> tuple[str, int]:
    i = 0
    while i < len(lines) and not lines[i].strip():
        i += 1
    title_lines: list[str] = []
    while i < len(lines) and lines[i].strip():
        stripped = lines[i].strip()
        if stripped.lower() != "snyk":
            title_lines.append(stripped)
        i += 1
    while i < len(lines) and not lines[i].strip():
        i += 1
    title = " ".join(title_lines).strip() or "UNTITLED"
    return title, i


def _extract_title_above(lines: list[str], why_idx: int) -> str:
    j = why_idx - 1
    while j >= 0 and not lines[j].strip():
        j -= 1

    title_lines: list[str] = []
    while j >= 0 and lines[j].strip():
        title_lines.append(lines[j].strip())
        j -= 1
    title_lines.reverse()

    k = j
    while k >= 0 and not lines[k].strip():
        k -= 1
    if k >= 0 and re.fullmatch(r"\d{1,3}", lines[k].strip()):
        title_lines.insert(0, lines[k].strip())

    title = " ".join(title_lines).strip()
    match = re.match(r"^(\d{1,3})\s+(.+)$", title)
    if match:
        label = match.group(1)
        if len(label) == 3 and label.startswith("0"):
            label = label[:2]
        title = f"{label} — {match.group(2)}"
    return title


def _parse_sections_from_page(page_text: str) -> list[_SourceSection]:
    lines = [ln.rstrip() for ln in page_text.splitlines()]
    why_idxs = [i for i, ln in enumerate(lines) if ln.strip().lower().startswith("why it matters:")]

    if not why_idxs:
        title, body_start = _parse_title_block(lines)
        body = "\n".join([ln for ln in lines[body_start:] if ln.strip() and ln.strip().lower() != "snyk"]).strip()
        return [_SourceSection(title=title, body=body, why_it_matters=None)]

    sections: list[_SourceSection] = []
    for idx, why_idx in enumerate(why_idxs):
        title = _extract_title_above(lines, why_idx)
        end = why_idxs[idx + 1] if idx + 1 < len(why_idxs) else len(lines)
        why = lines[why_idx].strip()
        body = "\n".join(lines[why_idx + 1 : end]).strip()
        sections.append(_SourceSection(title=title, body=body, why_it_matters=why))
    return sections


def _extract_sections(source_text: str) -> list[_SourceSection]:
    pages = _parse_pages(source_text)
    sections: list[_SourceSection] = []
    for _page_no, page_text in pages:
        if page_text.strip():
            sections.extend(_parse_sections_from_page(page_text))
    return sections


def _has(text: str, *needles: str) -> bool:
    lowered = text.lower()
    return any(n.lower() in lowered for n in needles)


def _extract_code_block(body: str) -> str | None:
    lines = [ln.rstrip() for ln in body.splitlines()]
    start = next((i for i, ln in enumerate(lines) if ln.strip().startswith("{")), None)
    if start is None:
        return None
    end = None
    for i in range(start, len(lines)):
        if lines[i].strip() == "}":
            end = i
            break
    if end is None:
        return None
    return "\n".join(lines[start : end + 1])


def _extract_access_report(body: str) -> str | None:
    if "Gemini Access Report" not in body:
        return None

    lines = [ln.strip() for ln in body.splitlines() if ln.strip()]
    try:
        idx = lines.index("Gemini Access Report")
    except ValueError:
        return None

    rows: list[list[str]] = []
    for ln in lines[idx + 1 : idx + 8]:
        if "@" not in ln:
            continue
        parts = [p for p in re.split(r"\s{2,}", ln) if p]
        if len(parts) >= 5:
            rows.append(parts[:5])

    if not rows:
        return None

    header = ["Name", "Email", "License Assigned", "Last Active", "Last Detected Scan"]
    out = [
        "### Gemini Access Report",
        "",
        "| " + " | ".join(header) + " |",
        "| " + " | ".join(["---"] * len(header)) + " |",
    ]
    for r in rows:
        out.append("| " + " | ".join(r) + " |")
    return "\n".join(out)


def _extract_quiz(body: str) -> str | None:
    if "Quiz" not in body:
        return None
    if "What must you do" not in body and "What must you do if you want access" not in body:
        return None

    question_match = re.search(r"(What must you do[^\n\r]+)", body)
    question = question_match.group(1).strip() if question_match else "Quiz"

    options = []
    for ln in body.splitlines():
        stripped = ln.strip(" >•\t")
        if stripped.startswith("Include") or stripped.startswith("Install") or stripped.startswith("Download"):
            options.append(stripped)

    if not options:
        return None

    out = ["### Quiz", "", f"**{question}**", ""]
    out.extend([f"- {opt}" for opt in options])
    return "\n".join(out)


def _extract_form(body: str) -> str | None:
    if "Code Assistant Access Request Form" not in body:
        return None
    return "\n".join(
        [
            "### Code Assistant Access Request Form",
            "",
            "- Upload a screenshot showing the security IDE plugin is installed for local testing.",
            "- Provide any additional context on the request.",
            "- Attest that the AI coding assistant will be used in conjunction with local scanning.",
        ]
    )


def _slugify(value: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9]+", "-", value).strip("-")
    cleaned = re.sub(r"-{2,}", "-", cleaned)
    return cleaned.upper() if cleaned else "UNKNOWN"


def _inferred_mermaid(title: str) -> str | None:
    title_upper = title.upper()

    if "PULL REQUEST" in title_upper:
        return """flowchart TD
  A[Code change] --> B[Pull request opened]
  B --> C[Automated scan (PR checks)]
  C --> D{Findings?}
  D -->|None| E[Merge]
  D -->|Some| F[Ticket created]
  F --> G[Exception request]
  G --> H[Alignment session]
  H --> I[Risk accepted (documented)]
  I --> E
"""

    if "SHIFTING LEFT" in title_upper:
        return """flowchart TD
  A[Developer writes code] --> B[IDE scan (local)]
  B --> C{Issue found?}
  C -->|Yes| D[Fix now]
  C -->|No| E[Commit]
  E --> F[PR checks]
  A --> G[Agent workflow]
  G --> H[Local MCP scan]
  H --> E
"""

    if "REQUEST EVIDENCE" in title_upper:
        return """flowchart TD
  A[Developer requests access] --> B[Upload screenshot]
  B --> C[Attestation captured]
  C --> D[Access enabled]
  D --> E[Local testing (claimed)]
  E --> F[Periodic audit]
  F --> G{Still compliant?}
  G -->|Yes| D
  G -->|No| H[Access paused pending review]
  H --> I[Alignment session]
  I --> D
"""

    if "AUDIT" in title_upper:
        return """flowchart TD
  A[Collect usage signals] --> B[Correlate assistants vs scans]
  B --> C[Identify gaps]
  C --> D[Notify developers]
  D --> E[Remediation window]
  E --> F[Dashboard update]
  F --> G[Quarterly KPI trend review]
  G --> H[Action items (optional)]
"""

    if "TRAINING" in title_upper:
        return """flowchart TD
  A[Onboarding] --> B[Training module]
  B --> C[Quiz]
  C --> D{Pass?}
  D -->|Yes| E[Certificate issued]
  D -->|No| F[Retake scheduled]
  E --> G[Access request approved]
  G --> H[Usage begins]
  H --> I[Refresher cadence]
  I --> B
"""

    if "ACCESS CONTROL" in title_upper:
        return """flowchart TD
  A[Policy defined] --> B[Endpoint management]
  B --> C{Prerequisites met?}
  C -->|Yes| D[Assistant enabled]
  C -->|No| E[Blocked by policy]
  E --> F[Exception request]
  F --> G[Owner approval]
  G --> D
"""

    if "PATH FOR" in title_upper:
        return """flowchart TD
  A[Desire: secure innovation] --> B[Guardrails planned]
  B --> C[Pilot cohort]
  C --> D[Deck + FAQ]
  D --> E[Stakeholder alignment]
  E --> F[Incremental rollout]
  F --> G[Measure adoption]
  G --> H[Reframe as iteration]
  H --> E
"""

    return None


def _render_inferred_diagram(title: str) -> str | None:
    diagram = _inferred_mermaid(title)
    if not diagram:
        return None
    return "\n".join(
        [
            "### InfraFabric Red Team Diagram (Inferred)",
            "",
            "```mermaid",
            diagram.rstrip(),
            "```",
        ]
    )


def _render_intro(section: _SourceSection) -> str:
    lines = [ln.strip() for ln in section.body.splitlines() if ln.strip()]
    tagline = "\n".join(lines[:7]).strip() if lines else ""

    out = [f"## {section.title}", ""]
    if tagline:
        out.extend([f"> {tagline}", ""])

    out.extend(
        [
            "We love the ambition here and are directionally aligned with the idea of moving quickly while remaining contractually comfortable.",
            "The source frames the core tension clearly: higher throughput tends to surface more vulnerabilities, which is a volume-and-velocity story, not a tool failure story.",
            "Accordingly, the practical path is to operationalize guardrails as workflow defaults (PR, IDE, CI/CD, and access controls), while ensuring the rollout remains optimized for alignment and minimal disruption on paper.",
            "In other words: we can move fast and be safe, as long as we define safe as \"documented\" and fast as \"agendized.\"",
            "",
            "> **InfraFabric Red Team Note:** Vendors sell secure speed. InfraFabric audits what survives contact with bureaucracy.",
        ]
    )
    return "\n".join(out).strip()


def _render_section(section: _SourceSection) -> str:
    excerpt = f"{section.title}\n{section.why_it_matters or ''}\n{section.body}".strip()
    paragraphs: list[str] = []

    title_upper = section.title.upper()

    if "PULL REQUEST" in title_upper:
        paragraphs.extend(
            [
                "We fully support focusing guardrails at the pull request stage, because it creates a reassuring sense of control without requiring anyone to change how they work at 10:00 AM.",
                "It also provides a structurally safe venue for accountability theater: findings can be surfaced, tracked, and re-litigated in perpetuity while timelines remain subject to stakeholder alignment.",
                "If anything goes sideways, we can always point to the PR thread and note that it was reviewed with deep seriousness at 4:55 PM on a Friday.",
            ]
        )
    elif "REQUEST EVIDENCE" in title_upper or _has(excerpt, "access request", "screenshot"):
        paragraphs.extend(
            [
                "Requiring proof of local testing is a lightweight enablement workflow that conveniently doubles as a durable audit artifact.",
                "Screenshots are particularly helpful because they are high-effort to verify and low-fidelity to audit, which preserves the timeless corporate principle that visibility should be proportional to comfort.",
                "Once the screenshot is uploaded, it can be stored in a folder with a robust heritage naming convention and a retention policy of \"until the heat death of the universe.\"",
            ]
        )
    elif "AUDIT" in title_upper or _has(excerpt, "usage reports", "periodic audits"):
        paragraphs.extend(
            [
                "Periodic audits are a strong mechanism for discovering that the rollout has already happened, just not in a way that can be conveniently measured.",
                "A centralized dashboard with adoption signals allows us to produce a KPI trend line that looks decisive while still leaving room for interpretation, follow-ups, and iterative enablement.",
                "If the dashboard ever shows a red triangle, we can immediately form the Committee for the Preservation of the Committee and begin the healing process.",
            ]
        )
    elif "TRAINING" in title_upper or _has(excerpt, "snyk learn", "owasp"):
        paragraphs.extend(
            [
                "Security awareness training is the perfect control because it is both necessary and never truly complete.",
                "A short quiz provides a durable compliance narrative: we can demonstrate investment in education, capture attestations, and schedule refreshers whenever the organization needs to signal seriousness.",
                "The goal is not mastery; the goal is a completion certificate that can be forwarded to leadership with the subject line \"Progress Update.\"",
            ]
        )
    elif "ACCESS CONTROL" in title_upper or _has(excerpt, "intune", "jamf", "citrix", "dev container", "extensions"):
        paragraphs.extend(
            [
                "Tying access to secure configurations creates scalable guardrails, assuming we keep the policy language aspirational and the enforcement language progressive.",
                "Endpoint management and dev container baselines let us gate assistants behind prerequisites, ideally in a way that can be described as enablement rather than blocking for cultural compatibility.",
                "This is the \"not my job\" routing protocol, except the router is policy and the destination is an alignment session.",
            ]
        )
    elif "SHIFTING LEFT" in title_upper:
        paragraphs.extend(
            [
                "Shifting left is directionally aligned with best practices, provided we define left as somewhere we can still roll back quietly.",
                "In practice, IDE scanning creates fast feedback loops, and agentic workflows can be covered via a local MCP server, which is excellent because it allows us to say continuous without committing to blocking.",
                "We recommend a pilot cohort, a slide deck, and an FAQ, so the shift remains culturally reversible.",
            ]
        )
    elif _has(title_upper, "PATH FORWARD") or _has(excerpt, "secure innovation", "talk to our team"):
        paragraphs.extend(
            [
                "The path forward is to treat guardrails as an operational capability, not a one-time rollout, which ensures we remain permanently in a state of constructive iteration.",
                "With the right sequencing, we can build trust, reduce friction, and maintain the strategic option value of circling back when timelines become emotionally complex.",
                "Secure innovation is not just possible; it is operational, provided we align on what operational means in Q3.",
            ]
        )
    else:
        paragraphs.append(
            "We are aligned on the intent of this section and recommend a phased approach that optimizes for stakeholder comfort while we validate success criteria."
        )

    out: list[str] = [f"## {section.title}"]
    if section.why_it_matters:
        out.extend(["", section.why_it_matters, ""])
    else:
        out.append("")

    out.extend(paragraphs)

    inferred = _render_inferred_diagram(section.title)
    if inferred:
        out.extend(["", inferred])

    code = _extract_code_block(section.body)
    if code:
        out.extend(["", "```json", code.strip(), "```"])

    report = _extract_access_report(section.body)
    if report:
        out.extend(["", report])

    quiz = _extract_quiz(section.body)
    if quiz:
        out.extend(["", quiz])

    form = _extract_form(section.body)
    if form:
        out.extend(["", form])

    return "\n".join(out).strip()


def _generate_dave_v1_2_mirror(*, source_text: str, source_path: str) -> str:
    today = _dt.date.today().isoformat()
    normalized = _normalize_ocr(source_text)
    extract_sha = _sha256_text(normalized)
    source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"

    sections = _extract_sections(normalized)
    if not sections:
        raise ValueError("No content extracted from source")

    cover_lines = [ln.strip() for ln in sections[0].body.splitlines() if ln.strip() and ln.strip().lower() != "snyk"]
    cover_h1 = sections[0].title.strip() or "SHADOW DOSSIER"
    cover_h2 = " ".join(cover_lines[:2]).strip() if cover_lines else ""

    y, m, d = today.split("-")
    report_id = f"IF-RT-DAVE-{y}-{m}{d}"
    source_basename = Path(source_path).name
    project_slug = _slugify(Path(source_basename).stem + "-mirror")
    source_slug = _slugify(source_basename)

    out: list[str] = [
        "---",
        "BRAND: InfraFabric.io",
        "UNIT: RED TEAM (STRATEGIC OPS)",
        "DOCUMENT: SHADOW DOSSIER",
        "CLASSIFICATION: EYES ONLY // DAVE",
        "---",
        "",
        "# [ RED TEAM DECLASSIFIED ]",
        f"## PROJECT: {project_slug}",
        f"### SOURCE: {source_slug}",
        f"**INFRAFABRIC REPORT ID:** `{report_id}`",
        "",
        "> NOTICE: This document is a product of InfraFabric Red Team.",
        "> It provides socio-technical friction analysis for how a rollout survives contact with incentives.",
        "",
        "**[ ACCESS GRANTED: INFRAFABRIC RED TEAM ]**",
        "**[ STATUS: OPERATIONAL REALISM ]**",
        "",
        f"## {cover_h1}",
    ]
    if cover_h2:
        out.extend([f"### {cover_h2}", ""])
    else:
        out.append("")

    out.extend(
        [
            "> Shadow dossier (mirror-first).",
            ">",
            "> Protocol: IF.DAVE.v1.2",
            "> Citation: `if://bible/dave/v1.2`",
            f"> Source: `{source_path}`",
            f"> Generated: `{today}`",
            f"> Source Hash (sha256): `{source_file_sha}`",
            f"> Extract Hash (sha256): `{extract_sha}`",
            "",
        ]
    )

    for section in sections[1:]:
        if section.title.strip().upper() == "INTRODUCTION":
            out.append(_render_intro(section))
        else:
            out.append(_render_section(section))
        out.append("")

    out.extend(
        [
            "---",
            "",
            "*Standard Dave Footer:* This document is intended for the recipient only. If you are not the recipient, please delete it and forget you saw anything. P.S. Please consider the environment before printing this email.",
        ]
    )

    return "\n".join(out).strip() + "\n"
