"""RBAC compliance report generator.

Generates human-readable compliance reports from VerificationReport instances.
Cites NIST SP 800-162, Saltzer & Schroeder (1975), and ISO 27001 in the
methodology section.
"""

from dazzle.rbac.verifier import CellResult, VerificationReport

# Decision glyphs used in the access matrix table.
_CELL_GLYPH: dict[CellResult, str] = {
    CellResult.PASS: "PASS",
    CellResult.VIOLATION: "**VIOLATION**",
    CellResult.WARNING: "_WARNING_",
}


def _render_summary(report: VerificationReport) -> list[str]:
    lines = [
        "## Summary",
        "",
        f"- **Application**: {report.app_name}",
        f"- **Timestamp**: {report.timestamp}",
        f"- **Dazzle version**: {report.dazzle_version}",
    ]
    if report.matrix is not None:
        lines += [
            f"- **Roles tested**: {len(report.matrix.roles)}",
            f"- **Entities**: {len(report.matrix.entities)}",
            f"- **Operations**: {len(report.matrix.operations)}",
        ]
    lines += [
        f"- **Total cells**: {report.total}",
        f"- **Passed**: {report.passed}",
        f"- **Violations**: {report.violated}",
        f"- **Warnings**: {report.warnings}",
        "",
    ]
    return lines


def _render_matrix_table(report: VerificationReport) -> list[str]:
    if not report.cells:
        return ["## Access Matrix", "", "*(no cells — run `dazzle rbac verify` first)*", ""]

    # Gather unique roles, entities, operations from cells.
    roles: list[str] = []
    entities: list[str] = []
    operations: list[str] = []
    seen_roles: set[str] = set()
    seen_entities: set[str] = set()
    seen_operations: set[str] = set()

    # Preserve insertion order.
    if report.matrix is not None:
        roles = list(report.matrix.roles)
        entities = list(report.matrix.entities)
        operations = list(report.matrix.operations)
    else:
        for cell in report.cells:
            if cell.role not in seen_roles:
                roles.append(cell.role)
                seen_roles.add(cell.role)
            if cell.entity not in seen_entities:
                entities.append(cell.entity)
                seen_entities.add(cell.entity)
            if cell.operation not in seen_operations:
                operations.append(cell.operation)
                seen_operations.add(cell.operation)

    # Build lookup: (role, entity, operation) → CellResult
    lookup: dict[tuple[str, str, str], CellResult] = {
        (c.role, c.entity, c.operation): c.result for c in report.cells
    }

    lines = ["## Access Matrix", ""]

    for entity in entities:
        lines.append(f"### {entity}")
        lines.append("")

        header = "| operation | " + " | ".join(roles) + " |"
        separator = "| --- | " + " | ".join(["---"] * len(roles)) + " |"
        lines.append(header)
        lines.append(separator)

        for op in operations:
            row_parts = [op]
            for role in roles:
                key = (role, entity, op)
                result = lookup.get(key)
                if result is None:
                    row_parts.append("-")
                else:
                    row_parts.append(_CELL_GLYPH[result])
            lines.append("| " + " | ".join(row_parts) + " |")

        lines.append("")

    return lines


def _render_violations(report: VerificationReport) -> list[str]:
    violations = [c for c in report.cells if c.result == CellResult.VIOLATION]
    warnings = [c for c in report.cells if c.result == CellResult.WARNING]

    if not violations and not warnings:
        return ["## Findings", "", "No violations or warnings detected.", ""]

    lines = ["## Findings", ""]

    if violations:
        lines += [f"### Violations ({len(violations)})", ""]
        for v in violations:
            lines.append(
                f"- **{v.role}** / **{v.entity}** / `{v.operation}`: "
                f"expected `{v.expected}`, got HTTP {v.observed_status}. "
                f"{v.detail}"
            )
        lines.append("")

    if warnings:
        lines += [f"### Warnings ({len(warnings)})", ""]
        for w in warnings:
            lines.append(
                f"- **{w.role}** / **{w.entity}** / `{w.operation}`: "
                f"expected `{w.expected}`, got HTTP {w.observed_status}. "
                f"{w.detail}"
            )
        lines.append("")

    return lines


def _render_methodology() -> list[str]:
    return [
        "## Methodology",
        "",
        "This report was generated by the Dazzle RBAC Verification Framework.",
        "",
        "### Standards and References",
        "",
        "- **NIST SP 800-162** — *Guide to Attribute Based Access Control (ABAC) Definition "
        "and Considerations* (2014).  Provides the conceptual basis for policy-based access "
        "control and the separation of policy enforcement from policy decision.",
        "",
        "- **Saltzer & Schroeder (1975)** — *The Protection of Information in Computer Systems.*  "
        "Defines the principle of least privilege and complete mediation, which underpin the "
        "DENY-by-default Cedar semantics used in this framework.",
        "",
        "- **ISO/IEC 27001:2022** — Annex A, Control 5.15 (Access Control).  Requires that "
        "access to information and associated assets is controlled and that access rules are "
        "established, documented, and reviewed.",
        "",
        "### Verification Approach",
        "",
        "1. **Layer 1 (Static)** — `dazzle rbac matrix` resolves the access matrix from DSL "
        "without starting a server.  Cedar semantics: FORBID overrides PERMIT; no matching "
        "rule implies DENY.",
        "",
        "2. **Layer 2 (Dynamic)** — `dazzle rbac verify` probes live API endpoints with "
        "synthetic credentials for each role, compares HTTP responses against the static "
        "matrix, and records every decision via the audit trail.",
        "",
        "3. **Layer 3 (Audit)** — `AccessDecisionRecord` events are emitted for every "
        "permission evaluation, enabling post-hoc forensic review.",
        "",
    ]


def generate_report(report: VerificationReport, format: str = "markdown") -> str:
    """Generate a compliance report from *report*.

    Currently only ``"markdown"`` format is supported.  Passing any other
    value also produces Markdown (format is reserved for future JSON/HTML
    output).
    """
    lines: list[str] = [
        "# RBAC Compliance Report",
        "",
    ]
    lines.extend(_render_summary(report))
    lines.extend(_render_matrix_table(report))
    lines.extend(_render_violations(report))
    lines.extend(_render_methodology())

    return "\n".join(lines)
