from __future__ import annotations

import html
from pathlib import Path
from typing import Any

from leshy.core.models import (
    AttackPayload,
    EvalResult,
    TargetResponse,
    TestCaseResult,
    TestSuiteConfig,
    TestSuiteResult,
)
from leshy.core.registry import logger_registry
from leshy.loggers.base import Logger

_HTML_TEMPLATE = """\
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Leshy Security Report - {suite_name}</title>
    <style>
        :root {{
            --bg: #0d1117;
            --bg-secondary: #161b22;
            --border: #30363d;
            --text: #c9d1d9;
            --text-muted: #8b949e;
            --green: #238636;
            --red: #da3633;
            --yellow: #d29922;
            --blue: #58a6ff;
        }}
        * {{ box-sizing: border-box; margin: 0; padding: 0; }}
        body {{
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: var(--bg);
            color: var(--text);
            line-height: 1.6;
            padding: 2rem;
        }}
        .container {{ max-width: 1200px; margin: 0 auto; }}
        h1 {{ color: #fff; margin-bottom: 0.5rem; }}
        h2 {{
            color: var(--blue); margin: 2rem 0 1rem;
            border-bottom: 1px solid var(--border); padding-bottom: 0.5rem;
        }}
        h3 {{ color: var(--text); margin: 1.5rem 0 0.75rem; }}
        .subtitle {{ color: var(--text-muted); margin-bottom: 2rem; }}
        .summary-grid {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 1rem;
            margin: 1.5rem 0;
        }}
        .summary-card {{
            background: var(--bg-secondary);
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 1rem;
            text-align: center;
        }}
        .summary-card .value {{
            font-size: 2rem;
            font-weight: bold;
        }}
        .summary-card .label {{ color: var(--text-muted); font-size: 0.875rem; }}
        .vulnerable .value {{ color: var(--red); }}
        .resilient .value {{ color: var(--green); }}
        .inconclusive .value {{ color: var(--yellow); }}
        .status-badge {{
            display: inline-block;
            padding: 0.25rem 0.75rem;
            border-radius: 999px;
            font-size: 0.75rem;
            font-weight: 600;
            text-transform: uppercase;
        }}
        .status-vulnerable {{ background: var(--red); color: #fff; }}
        .status-resilient {{ background: var(--green); color: #fff; }}
        .status-inconclusive {{ background: var(--yellow); color: #000; }}
        .status-error {{ background: #6e7681; color: #fff; }}
        .case-card {{
            background: var(--bg-secondary);
            border: 1px solid var(--border);
            border-radius: 8px;
            margin: 1rem 0;
            overflow: hidden;
        }}
        .case-header {{
            padding: 1rem;
            border-bottom: 1px solid var(--border);
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
        }}
        .case-header:hover {{ background: rgba(255,255,255,0.02); }}
        .case-body {{ padding: 1rem; display: none; }}
        .case-body.open {{ display: block; }}
        .payload-row {{
            display: grid;
            grid-template-columns: 100px 1fr 120px;
            gap: 1rem;
            padding: 0.75rem;
            border-bottom: 1px solid var(--border);
            align-items: start;
        }}
        .payload-row:last-child {{ border-bottom: none; }}
        .payload-content {{
            font-family: monospace;
            font-size: 0.8rem;
            color: var(--text-muted);
            word-break: break-word;
            max-height: 100px;
            overflow: hidden;
        }}
        .payload-content.expanded {{ max-height: none; }}
        .expand-btn {{
            color: var(--blue);
            cursor: pointer;
            font-size: 0.75rem;
        }}
        .meta {{ color: var(--text-muted); font-size: 0.875rem; }}
        .progress-bar {{
            height: 8px;
            background: var(--border);
            border-radius: 4px;
            overflow: hidden;
            margin: 1rem 0;
        }}
        .progress-fill {{
            height: 100%;
            display: flex;
        }}
        .progress-fill .vulnerable {{ background: var(--red); }}
        .progress-fill .resilient {{ background: var(--green); }}
        .progress-fill .inconclusive {{ background: var(--yellow); }}
        .progress-fill .error {{ background: #6e7681; }}
        footer {{
            margin-top: 3rem;
            padding-top: 1rem;
            border-top: 1px solid var(--border);
            color: var(--text-muted);
            font-size: 0.875rem;
            text-align: center;
        }}
        footer a {{ color: var(--blue); text-decoration: none; }}
    </style>
</head>
<body>
    <div class="container">
        <h1>🌲 Leshy Security Report</h1>
        <p class="subtitle">{suite_name} &middot; {timestamp}</p>

        <div class="summary-grid">
            <div class="summary-card">
                <div class="value">{total_payloads}</div>
                <div class="label">Total Payloads</div>
            </div>
            <div class="summary-card vulnerable">
                <div class="value">{total_vulnerable}</div>
                <div class="label">Vulnerable</div>
            </div>
            <div class="summary-card resilient">
                <div class="value">{total_resilient}</div>
                <div class="label">Resilient</div>
            </div>
            <div class="summary-card inconclusive">
                <div class="value">{total_inconclusive}</div>
                <div class="label">Inconclusive</div>
            </div>
        </div>

        <div class="progress-bar">
            <div class="progress-fill">
                <div class="vulnerable" style="width: {vuln_pct}%"></div>
                <div class="resilient" style="width: {res_pct}%"></div>
                <div class="inconclusive" style="width: {inc_pct}%"></div>
                <div class="error" style="width: {err_pct}%"></div>
            </div>
        </div>

        <h2>Test Cases</h2>
        {cases_html}

        <footer>
            Generated by <a href="https://github.com/bardsai/leshy">Leshy</a> &middot;
            Agent-first security testing framework
        </footer>
    </div>

    <script>
        document.querySelectorAll('.case-header').forEach(header => {{
            header.addEventListener('click', () => {{
                header.nextElementSibling.classList.toggle('open');
            }});
        }});
        document.querySelectorAll('.expand-btn').forEach(btn => {{
            btn.addEventListener('click', (e) => {{
                e.stopPropagation();
                const content = btn.previousElementSibling;
                content.classList.toggle('expanded');
                btn.textContent = content.classList.contains('expanded') ? 'collapse' : 'expand';
            }});
        }});
    </script>
</body>
</html>
"""

_CASE_TEMPLATE = """\
<div class="case-card">
    <div class="case-header">
        <div>
            <strong>{name}</strong>
            <span class="meta">&middot; {payload_count} payloads &middot; {duration}ms</span>
        </div>
        <div>
            {status_badges}
        </div>
    </div>
    <div class="case-body">
        {payloads_html}
    </div>
</div>
"""

_PAYLOAD_TEMPLATE = """\
<div class="payload-row">
    <div><span class="status-badge status-{verdict_class}">{verdict}</span></div>
    <div>
        <div class="payload-content">{payload}</div>
        <span class="expand-btn">expand</span>
    </div>
    <div class="meta">{attack_name}</div>
</div>
"""


@logger_registry.register("html")
class HtmlReportLogger(Logger):
    """Generate a rich HTML report for test results."""

    name = "html"

    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)
        self._output_path = kwargs.get("output", str(Path("leshy") / "runs" / "report.html"))
        self._suite_config: TestSuiteConfig | None = None
        self._payload_results: list[tuple[AttackPayload, TargetResponse, EvalResult]] = []

    async def on_suite_start(self, config: TestSuiteConfig) -> None:
        self._suite_config = config
        self._payload_results = []

    async def on_payload_result(
        self,
        payload: AttackPayload,
        response: TargetResponse,
        results: list[EvalResult],
    ) -> None:
        if results:
            self._payload_results.append((payload, response, results[0]))

    async def on_suite_complete(self, result: TestSuiteResult) -> None:
        html_content = self._generate_html(result)
        path = Path(self._output_path)
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(html_content)

    def _generate_html(self, result: TestSuiteResult) -> str:
        total = result.total_payloads or 1
        vuln_pct = (result.total_vulnerable / total) * 100
        res_pct = (result.total_resilient / total) * 100
        inc_pct = (result.total_inconclusive / total) * 100
        err_pct = (result.total_errors / total) * 100

        cases_html = ""
        for case in result.case_results:
            cases_html += self._generate_case_html(case)

        return _HTML_TEMPLATE.format(
            suite_name=html.escape(result.suite_name),
            timestamp=result.started_at.strftime("%Y-%m-%d %H:%M:%S UTC"),
            total_payloads=result.total_payloads,
            total_vulnerable=result.total_vulnerable,
            total_resilient=result.total_resilient,
            total_inconclusive=result.total_inconclusive,
            vuln_pct=vuln_pct,
            res_pct=res_pct,
            inc_pct=inc_pct,
            err_pct=err_pct,
            cases_html=cases_html,
        )

    def _generate_case_html(self, case: TestCaseResult) -> str:
        status_badges = []
        if case.vulnerable_count:
            status_badges.append(
                f'<span class="status-badge status-vulnerable">{case.vulnerable_count} vuln</span>'
            )
        if case.resilient_count:
            status_badges.append(
                f'<span class="status-badge status-resilient">{case.resilient_count} ok</span>'
            )
        if case.inconclusive_count:
            status_badges.append(
                f'<span class="status-badge status-inconclusive">{case.inconclusive_count} ?</span>'
            )

        payloads_html = ""
        for eval_result in case.eval_results:
            payload_data = self._find_payload(eval_result.payload_id)
            if payload_data:
                payload, _, _ = payload_data
                payloads_html += _PAYLOAD_TEMPLATE.format(
                    verdict=eval_result.verdict.value,
                    verdict_class=eval_result.verdict.value,
                    payload=html.escape(payload.content[:500]),
                    attack_name=html.escape(payload.attack_name),
                )

        return _CASE_TEMPLATE.format(
            name=html.escape(case.name),
            payload_count=case.total_payloads,
            duration=int(case.duration_ms),
            status_badges=" ".join(status_badges),
            payloads_html=payloads_html,
        )

    def _find_payload(
        self, payload_id: str
    ) -> tuple[AttackPayload, TargetResponse, EvalResult] | None:
        for p, r, e in self._payload_results:
            if p.id == payload_id:
                return (p, r, e)
        return None
