"""Compliance Certificate Generator — Formal HTML compliance certificates.

Generates formal, corporate-style HTML compliance certificates including:
- Scan date and certificate issuance date
- Overall compliance score
- Frameworks compliant with
- Auditor name and organization
- Validity period
- Digital signature placeholder (SHA-256 hash of content)
- Corporate styling with border and seal

Zero external dependencies — pure Python stdlib.
"""

from __future__ import annotations

import hashlib
import html
import json
import time
from dataclasses import dataclass, field
from typing import Any


# ---------- Data Models ----------

@dataclass
class FrameworkCompliance:
    """Compliance status for a single framework."""

    name: str
    score: float  # 0-100
    status: str = "compliant"  # compliant, partial, non_compliant
    controls_passed: int = 0
    controls_total: int = 0

    def __post_init__(self) -> None:
        if not self.status:
            if self.score >= 80:
                self.status = "compliant"
            elif self.score >= 50:
                self.status = "partial"
            else:
                self.status = "non_compliant"

    def to_dict(self) -> dict[str, Any]:
        return {
            "name": self.name,
            "score": round(self.score, 1),
            "status": self.status,
            "controls_passed": self.controls_passed,
            "controls_total": self.controls_total,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> FrameworkCompliance:
        return cls(
            name=data.get("name", ""),
            score=data.get("score", 0.0),
            status=data.get("status", ""),
            controls_passed=data.get("controls_passed", 0),
            controls_total=data.get("controls_total", 0),
        )


@dataclass
class ScanResultSummary:
    """Summary of scan results for the certificate."""

    scan_date: str = ""  # ISO 8601
    total_findings: int = 0
    critical: int = 0
    high: int = 0
    medium: int = 0
    low: int = 0
    score: float = 0.0  # overall score 0-100
    target: str = ""  # what was scanned

    def __post_init__(self) -> None:
        if not self.scan_date:
            self.scan_date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

    def to_dict(self) -> dict[str, Any]:
        return {
            "scan_date": self.scan_date,
            "total_findings": self.total_findings,
            "critical": self.critical,
            "high": self.high,
            "medium": self.medium,
            "low": self.low,
            "score": round(self.score, 1),
            "target": self.target,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> ScanResultSummary:
        return cls(
            scan_date=data.get("scan_date", ""),
            total_findings=data.get("total_findings", 0),
            critical=data.get("critical", 0),
            high=data.get("high", 0),
            medium=data.get("medium", 0),
            low=data.get("low", 0),
            score=data.get("score", 0.0),
            target=data.get("target", ""),
        )


@dataclass
class AuditorInfo:
    """Information about the auditor."""

    name: str = "MCP Security Audit"
    organization: str = "mcpaudit"
    title: str = "Automated Security Auditor"
    contact: str = ""

    def to_dict(self) -> dict[str, Any]:
        return {
            "name": self.name,
            "organization": self.organization,
            "title": self.title,
            "contact": self.contact,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> AuditorInfo:
        return cls(
            name=data.get("name", "MCP Security Audit"),
            organization=data.get("organization", "mcpaudit"),
            title=data.get("title", "Automated Security Auditor"),
            contact=data.get("contact", ""),
        )


@dataclass
class CertificateOptions:
    """Options for certificate generation."""

    validity_days: int = 90
    certificate_id: str = ""
    include_seal: bool = True
    include_signature: bool = True
    custom_note: str = ""

    def to_dict(self) -> dict[str, Any]:
        return {
            "validity_days": self.validity_days,
            "certificate_id": self.certificate_id,
            "include_seal": self.include_seal,
            "include_signature": self.include_signature,
            "custom_note": self.custom_note,
        }


@dataclass
class CertificateResult:
    """Result of certificate generation."""

    html: str
    certificate_id: str
    issue_date: str
    expiry_date: str
    content_hash: str
    overall_status: str  # compliant, partial, non_compliant

    def to_dict(self) -> dict[str, Any]:
        return {
            "certificate_id": self.certificate_id,
            "issue_date": self.issue_date,
            "expiry_date": self.expiry_date,
            "content_hash": self.content_hash,
            "overall_status": self.overall_status,
        }


# ---------- Helpers ----------

def _generate_cert_id(scan_date: str, target: str) -> str:
    """Generate a unique certificate ID."""
    seed = f"{scan_date}:{target}:{time.time()}"
    h = hashlib.sha256(seed.encode("utf-8")).hexdigest()[:16].upper()
    return f"CERT-{h[:4]}-{h[4:8]}-{h[8:12]}-{h[12:16]}"


def _compute_expiry(issue_date: str, validity_days: int) -> str:
    """Compute expiry date from issue date + validity days.

    Uses simple arithmetic to avoid datetime dependency in format.
    """
    # Parse YYYY-MM-DD from issue_date
    date_part = issue_date[:10]
    parts = date_part.split("-")
    if len(parts) != 3:
        return issue_date

    year, month, day = int(parts[0]), int(parts[1]), int(parts[2])

    # Add days
    days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        days_in_month[2] = 29

    remaining = validity_days
    while remaining > 0:
        days_left_in_month = days_in_month[month] - day
        if remaining <= days_left_in_month:
            day += remaining
            remaining = 0
        else:
            remaining -= (days_left_in_month + 1)
            day = 1
            month += 1
            if month > 12:
                month = 1
                year += 1
                if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
                    days_in_month[2] = 29
                else:
                    days_in_month[2] = 28

    return f"{year:04d}-{month:02d}-{day:02d}T00:00:00Z"


def _compute_content_hash(content: str) -> str:
    """Compute SHA-256 hash of content for digital signature placeholder."""
    return hashlib.sha256(content.encode("utf-8")).hexdigest()


def _overall_status(frameworks: list[FrameworkCompliance]) -> str:
    """Determine overall compliance status."""
    if not frameworks:
        return "non_compliant"

    statuses = [f.status for f in frameworks]
    if all(s == "compliant" for s in statuses):
        return "compliant"
    if any(s == "non_compliant" for s in statuses):
        return "non_compliant"
    return "partial"


def _status_color(status: str) -> str:
    """Get color for a compliance status."""
    colors = {
        "compliant": "#22c55e",
        "partial": "#f59e0b",
        "non_compliant": "#ef4444",
    }
    return colors.get(status, "#6b7280")


def _status_label(status: str) -> str:
    """Human-readable status label."""
    labels = {
        "compliant": "COMPLIANT",
        "partial": "PARTIALLY COMPLIANT",
        "non_compliant": "NON-COMPLIANT",
    }
    return labels.get(status, status.upper())


def _score_grade(score: float) -> str:
    """Convert score to letter grade."""
    if score >= 90:
        return "A"
    if score >= 80:
        return "B"
    if score >= 70:
        return "C"
    if score >= 60:
        return "D"
    return "F"


# ---------- HTML Generation ----------

def _generate_certificate_html(
    scan_result: ScanResultSummary,
    frameworks: list[FrameworkCompliance],
    auditor: AuditorInfo,
    options: CertificateOptions,
    cert_id: str,
    issue_date: str,
    expiry_date: str,
    content_hash: str,
    overall_status: str,
) -> str:
    """Generate the full HTML certificate."""
    target = html.escape(scan_result.target or "MCP Server")
    auditor_name = html.escape(auditor.name)
    auditor_org = html.escape(auditor.organization)
    auditor_title = html.escape(auditor.title)

    # Framework rows
    fw_rows: list[str] = []
    for fw in frameworks:
        color = _status_color(fw.status)
        label = _status_label(fw.status)
        controls = f"{fw.controls_passed}/{fw.controls_total}" if fw.controls_total > 0 else "N/A"
        fw_rows.append(f"""
            <tr>
                <td style="font-weight:600">{html.escape(fw.name)}</td>
                <td style="text-align:center">{fw.score:.1f}%</td>
                <td style="text-align:center">{controls}</td>
                <td style="text-align:center;color:{color};font-weight:700">{label}</td>
            </tr>""")

    # Seal
    seal_html = ""
    if options.include_seal:
        seal_color = _status_color(overall_status)
        seal_html = f"""
        <div class="seal">
            <div class="seal-inner" style="border-color:{seal_color}">
                <div class="seal-text" style="color:{seal_color}">{_status_label(overall_status)}</div>
                <div class="seal-grade">{_score_grade(scan_result.score)}</div>
            </div>
        </div>"""

    # Signature
    sig_html = ""
    if options.include_signature:
        sig_html = f"""
        <div class="signature-block">
            <div class="signature-line"></div>
            <div class="signature-name">{auditor_name}</div>
            <div class="signature-title">{auditor_title}, {auditor_org}</div>
            <div class="signature-hash">Digital Signature: SHA-256:{content_hash[:32]}...</div>
        </div>"""

    # Custom note
    note_html = ""
    if options.custom_note:
        note_html = f'<div class="custom-note"><p>{html.escape(options.custom_note)}</p></div>'

    # Overall status display
    status_color = _status_color(overall_status)
    status_label = _status_label(overall_status)

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Compliance Certificate — {cert_id}</title>
    <style>
        @page {{ margin: 2cm; }}
        * {{ margin: 0; padding: 0; box-sizing: border-box; }}
        body {{ font-family: 'Georgia', 'Times New Roman', serif; background: #f8f9fa; color: #1a1a1a; padding: 0; }}
        .certificate {{ max-width: 800px; margin: 2em auto; background: white; border: 3px double #1a365d; padding: 3em; position: relative; box-shadow: 0 4px 24px rgba(0,0,0,0.1); }}
        .certificate::before {{ content: ''; position: absolute; top: 8px; left: 8px; right: 8px; bottom: 8px; border: 1px solid #cbd5e1; pointer-events: none; }}
        .header {{ text-align: center; margin-bottom: 2em; }}
        .header h1 {{ font-size: 2em; color: #1a365d; letter-spacing: 3px; text-transform: uppercase; margin-bottom: 0.3em; }}
        .header .subtitle {{ color: #64748b; font-size: 1em; font-style: italic; }}
        .cert-id {{ text-align: center; color: #94a3b8; font-size: 0.85em; margin-bottom: 1.5em; font-family: 'Courier New', monospace; }}
        .status-banner {{ text-align: center; padding: 1em; margin: 1.5em 0; border-radius: 8px; }}
        .status-text {{ font-size: 1.5em; font-weight: 700; letter-spacing: 2px; }}
        .section {{ margin: 1.5em 0; }}
        .section h2 {{ font-size: 1.1em; color: #1a365d; border-bottom: 1px solid #e2e8f0; padding-bottom: 0.3em; margin-bottom: 0.8em; }}
        .detail-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 0.8em 2em; }}
        .detail-item {{ }}
        .detail-label {{ font-size: 0.8em; color: #64748b; text-transform: uppercase; letter-spacing: 0.5px; }}
        .detail-value {{ font-size: 1em; font-weight: 600; color: #1e293b; }}
        table {{ width: 100%; border-collapse: collapse; margin: 0.5em 0; }}
        th {{ background: #f1f5f9; padding: 8px 12px; text-align: left; font-size: 0.85em; color: #475569; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 2px solid #e2e8f0; }}
        td {{ padding: 8px 12px; border-bottom: 1px solid #f1f5f9; font-size: 0.9em; }}
        .seal {{ position: absolute; top: 2em; right: 2em; }}
        .seal-inner {{ width: 100px; height: 100px; border-radius: 50%; border: 3px solid; display: flex; flex-direction: column; align-items: center; justify-content: center; }}
        .seal-text {{ font-size: 0.55em; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; text-align: center; }}
        .seal-grade {{ font-size: 2em; font-weight: 700; }}
        .signature-block {{ margin-top: 2.5em; text-align: right; }}
        .signature-line {{ border-top: 1px solid #1a1a1a; width: 250px; margin-left: auto; margin-bottom: 0.3em; }}
        .signature-name {{ font-weight: 600; font-size: 1em; }}
        .signature-title {{ color: #64748b; font-size: 0.85em; }}
        .signature-hash {{ color: #94a3b8; font-size: 0.7em; font-family: 'Courier New', monospace; margin-top: 0.5em; }}
        .footer {{ text-align: center; margin-top: 2em; padding-top: 1em; border-top: 1px solid #e2e8f0; color: #94a3b8; font-size: 0.75em; }}
        .custom-note {{ background: #f8fafc; border-left: 3px solid #3b82f6; padding: 0.8em 1em; margin: 1em 0; font-style: italic; color: #475569; }}
        .findings-summary {{ display: flex; gap: 1em; justify-content: center; margin: 0.5em 0; }}
        .finding-badge {{ padding: 4px 12px; border-radius: 4px; font-size: 0.85em; font-weight: 600; color: white; }}
    </style>
</head>
<body>
    <div class="certificate">
        {seal_html}

        <div class="header">
            <h1>Compliance Certificate</h1>
            <div class="subtitle">MCP Security Audit Assessment</div>
        </div>

        <div class="cert-id">Certificate ID: {cert_id}</div>

        <div class="status-banner" style="background:{status_color}15;border:1px solid {status_color}">
            <div class="status-text" style="color:{status_color}">{status_label}</div>
        </div>

        <div class="section">
            <h2>Assessment Details</h2>
            <div class="detail-grid">
                <div class="detail-item">
                    <div class="detail-label">Target</div>
                    <div class="detail-value">{target}</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Overall Score</div>
                    <div class="detail-value">{scan_result.score:.1f}% (Grade: {_score_grade(scan_result.score)})</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Scan Date</div>
                    <div class="detail-value">{scan_result.scan_date[:10]}</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Certificate Issued</div>
                    <div class="detail-value">{issue_date[:10]}</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Valid Until</div>
                    <div class="detail-value">{expiry_date[:10]}</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Total Findings</div>
                    <div class="detail-value">{scan_result.total_findings}</div>
                </div>
            </div>
        </div>

        <div class="section">
            <h2>Findings Summary</h2>
            <div class="findings-summary">
                <span class="finding-badge" style="background:#dc2626">Critical: {scan_result.critical}</span>
                <span class="finding-badge" style="background:#f97316">High: {scan_result.high}</span>
                <span class="finding-badge" style="background:#eab308;color:#1a1a1a">Medium: {scan_result.medium}</span>
                <span class="finding-badge" style="background:#22c55e">Low: {scan_result.low}</span>
            </div>
        </div>

        <div class="section">
            <h2>Framework Compliance</h2>
            <table>
                <thead>
                    <tr><th>Framework</th><th style="text-align:center">Score</th><th style="text-align:center">Controls</th><th style="text-align:center">Status</th></tr>
                </thead>
                <tbody>{''.join(fw_rows)}</tbody>
            </table>
        </div>

        <div class="section">
            <h2>Auditor</h2>
            <div class="detail-grid">
                <div class="detail-item">
                    <div class="detail-label">Auditor Name</div>
                    <div class="detail-value">{auditor_name}</div>
                </div>
                <div class="detail-item">
                    <div class="detail-label">Organization</div>
                    <div class="detail-value">{auditor_org}</div>
                </div>
            </div>
        </div>

        {note_html}
        {sig_html}

        <div class="footer">
            <p>This certificate was generated automatically by mcpaudit.</p>
            <p>Validity period: {options.validity_days} days from issuance.</p>
            <p>Content hash: SHA-256:{content_hash}</p>
        </div>
    </div>
</body>
</html>"""


# ---------- Public API ----------

def generate_certificate(
    scan_result: dict[str, Any] | ScanResultSummary,
    frameworks: list[dict[str, Any] | FrameworkCompliance] | None = None,
    auditor: dict[str, Any] | AuditorInfo | None = None,
    options: dict[str, Any] | CertificateOptions | None = None,
) -> str:
    """Generate a formal compliance certificate as HTML.

    Args:
        scan_result: Scan result summary (dict or ScanResultSummary).
        frameworks: List of framework compliance data.
        auditor: Auditor information.
        options: Certificate generation options.

    Returns:
        Complete HTML string of the certificate.
    """
    # Normalize scan_result
    sr: ScanResultSummary
    if isinstance(scan_result, ScanResultSummary):
        sr = scan_result
    elif isinstance(scan_result, dict):
        sr = ScanResultSummary.from_dict(scan_result)
    else:
        sr = ScanResultSummary()

    # Normalize frameworks
    fws: list[FrameworkCompliance] = []
    if frameworks:
        for fw in frameworks:
            if isinstance(fw, FrameworkCompliance):
                fws.append(fw)
            elif isinstance(fw, dict):
                fws.append(FrameworkCompliance.from_dict(fw))

    # Normalize auditor
    aud: AuditorInfo
    if auditor is None:
        aud = AuditorInfo()
    elif isinstance(auditor, AuditorInfo):
        aud = auditor
    elif isinstance(auditor, dict):
        aud = AuditorInfo.from_dict(auditor)
    else:
        aud = AuditorInfo()

    # Normalize options
    opts: CertificateOptions
    if options is None:
        opts = CertificateOptions()
    elif isinstance(options, CertificateOptions):
        opts = options
    elif isinstance(options, dict):
        opts = CertificateOptions(
            validity_days=options.get("validity_days", 90),
            certificate_id=options.get("certificate_id", ""),
            include_seal=options.get("include_seal", True),
            include_signature=options.get("include_signature", True),
            custom_note=options.get("custom_note", ""),
        )
    else:
        opts = CertificateOptions()

    # Generate metadata
    issue_date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    cert_id = opts.certificate_id or _generate_cert_id(sr.scan_date, sr.target)
    expiry_date = _compute_expiry(issue_date, opts.validity_days)
    overall_status = _overall_status(fws)

    # Pre-compute hash of the certificate data
    hash_input = json.dumps({
        "cert_id": cert_id,
        "scan": sr.to_dict(),
        "frameworks": [f.to_dict() for f in fws],
        "auditor": aud.to_dict(),
        "issue_date": issue_date,
        "expiry_date": expiry_date,
    }, sort_keys=True)
    content_hash = _compute_content_hash(hash_input)

    html_out = _generate_certificate_html(
        scan_result=sr,
        frameworks=fws,
        auditor=aud,
        options=opts,
        cert_id=cert_id,
        issue_date=issue_date,
        expiry_date=expiry_date,
        content_hash=content_hash,
        overall_status=overall_status,
    )

    return html_out


def generate_certificate_with_metadata(
    scan_result: dict[str, Any] | ScanResultSummary,
    frameworks: list[dict[str, Any] | FrameworkCompliance] | None = None,
    auditor: dict[str, Any] | AuditorInfo | None = None,
    options: dict[str, Any] | CertificateOptions | None = None,
) -> CertificateResult:
    """Generate certificate and return metadata alongside HTML.

    Args:
        scan_result: Scan result summary.
        frameworks: Framework compliance data.
        auditor: Auditor information.
        options: Certificate options.

    Returns:
        CertificateResult with HTML and metadata.
    """
    # Normalize all inputs same as generate_certificate
    sr: ScanResultSummary
    if isinstance(scan_result, ScanResultSummary):
        sr = scan_result
    elif isinstance(scan_result, dict):
        sr = ScanResultSummary.from_dict(scan_result)
    else:
        sr = ScanResultSummary()

    fws: list[FrameworkCompliance] = []
    if frameworks:
        for fw in frameworks:
            if isinstance(fw, FrameworkCompliance):
                fws.append(fw)
            elif isinstance(fw, dict):
                fws.append(FrameworkCompliance.from_dict(fw))

    aud: AuditorInfo
    if auditor is None:
        aud = AuditorInfo()
    elif isinstance(auditor, AuditorInfo):
        aud = auditor
    elif isinstance(auditor, dict):
        aud = AuditorInfo.from_dict(auditor)
    else:
        aud = AuditorInfo()

    opts: CertificateOptions
    if options is None:
        opts = CertificateOptions()
    elif isinstance(options, CertificateOptions):
        opts = options
    elif isinstance(options, dict):
        opts = CertificateOptions(
            validity_days=options.get("validity_days", 90),
            certificate_id=options.get("certificate_id", ""),
            include_seal=options.get("include_seal", True),
            include_signature=options.get("include_signature", True),
            custom_note=options.get("custom_note", ""),
        )
    else:
        opts = CertificateOptions()

    issue_date = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
    cert_id = opts.certificate_id or _generate_cert_id(sr.scan_date, sr.target)
    expiry_date = _compute_expiry(issue_date, opts.validity_days)
    overall_status = _overall_status(fws)

    hash_input = json.dumps({
        "cert_id": cert_id,
        "scan": sr.to_dict(),
        "frameworks": [f.to_dict() for f in fws],
        "auditor": aud.to_dict(),
        "issue_date": issue_date,
        "expiry_date": expiry_date,
    }, sort_keys=True)
    content_hash = _compute_content_hash(hash_input)

    html_out = _generate_certificate_html(
        scan_result=sr,
        frameworks=fws,
        auditor=aud,
        options=opts,
        cert_id=cert_id,
        issue_date=issue_date,
        expiry_date=expiry_date,
        content_hash=content_hash,
        overall_status=overall_status,
    )

    return CertificateResult(
        html=html_out,
        certificate_id=cert_id,
        issue_date=issue_date,
        expiry_date=expiry_date,
        content_hash=content_hash,
        overall_status=overall_status,
    )


def format_certificate_json(result: CertificateResult) -> str:
    """Format certificate metadata as JSON."""
    return json.dumps(result.to_dict(), indent=2, ensure_ascii=False)
