"""
Fabric Warehouse Advisor — Security Check Reports
====================================================
Generates text, Markdown, and HTML reports from :class:`CheckSummary`.
"""

from __future__ import annotations

from typing import Dict, List

from .findings import (
    CheckSummary,
    Finding,
    LEVEL_CRITICAL,
    LEVEL_HIGH,
    LEVEL_MEDIUM,
    LEVEL_LOW,
    LEVEL_INFO,
    SEVERITY_ORDER,
    CATEGORY_PERMISSIONS,
    CATEGORY_ROLES,
    CATEGORY_RLS,
    CATEGORY_CLS,
    CATEGORY_DDM,
    CATEGORY_WORKSPACE_ROLES,
    CATEGORY_NETWORK,
    CATEGORY_SQL_AUDIT,
    CATEGORY_ITEM_PERMISSIONS,
    CATEGORY_SENSITIVITY_LABELS,
    CATEGORY_ROLE_ALIGNMENT,
)


# -- Category display metadata ------------------------------------------

_CATEGORY_LABELS = {
    CATEGORY_PERMISSIONS: "Schema Permissions",
    CATEGORY_ROLES: "Custom Roles",
    CATEGORY_RLS: "Row-Level Security",
    CATEGORY_CLS: "Column-Level Security",
    CATEGORY_DDM: "Dynamic Data Masking",
    CATEGORY_WORKSPACE_ROLES: "Workspace Roles",
    CATEGORY_NETWORK: "Network Isolation",
    CATEGORY_SQL_AUDIT: "SQL Audit Settings",
    CATEGORY_ITEM_PERMISSIONS: "Item Permissions",
    CATEGORY_SENSITIVITY_LABELS: "Sensitivity Labels",
    CATEGORY_ROLE_ALIGNMENT: "Role Alignment",
}

_CATEGORY_ORDER = [
    CATEGORY_PERMISSIONS,
    CATEGORY_ROLES,
    CATEGORY_RLS,
    CATEGORY_CLS,
    CATEGORY_DDM,
    CATEGORY_WORKSPACE_ROLES,
    CATEGORY_NETWORK,
    CATEGORY_SQL_AUDIT,
    CATEGORY_ITEM_PERMISSIONS,
    CATEGORY_SENSITIVITY_LABELS,
    CATEGORY_ROLE_ALIGNMENT,
]

_LEVEL_ICONS = {
    LEVEL_CRITICAL: "❌",
    LEVEL_HIGH: "🔴",
    LEVEL_MEDIUM: "🟠",
    LEVEL_LOW: "🟡",
    LEVEL_INFO: "✅",
}


def _edition_label(edition: str | None, *, capitalize: bool = True) -> str:
    """Return 'SQL Endpoint' or 'Warehouse' based on warehouse edition."""
    is_sql_endpoint = edition == "LakeWarehouse"
    if capitalize:
        return "SQL Endpoint" if is_sql_endpoint else "Warehouse"
    return "SQL endpoint" if is_sql_endpoint else "warehouse"


# ======================================================================
# TEXT REPORT
# ======================================================================

def generate_text_report(summary: CheckSummary) -> str:
    """Generate a plain-text report with Unicode box drawing."""
    lines: List[str] = []
    w = 72
    item = _edition_label(summary.warehouse_edition)

    lines.append("╔" + "═" * w + "╗")
    lines.append("║" + " Fabric Warehouse Security Check Report".center(w) + "║")
    lines.append("╚" + "═" * w + "╝")
    lines.append("")
    lines.append(f"  {item} : {summary.warehouse_name}")
    lines.append("")

    # Summary bar
    lines.append("━" * w)
    lines.append("  SUMMARY")
    lines.append(f"    ❌ CRITICAL : {summary.critical_count}")
    lines.append(f"    🔴 HIGH     : {summary.high_count}")
    lines.append(f"    🟠 MEDIUM   : {summary.medium_count}")
    lines.append(f"    🟡 LOW      : {summary.low_count}")
    lines.append(f"    ✅ INFO     : {summary.info_count}")
    lines.append(f"    Total      : {len(summary.findings)} findings")
    lines.append("━" * w)
    lines.append("")

    # Findings grouped by category
    for cat in _CATEGORY_ORDER:
        cat_findings = summary.findings_by_category(cat)
        if not cat_findings:
            continue

        label = _CATEGORY_LABELS.get(cat, cat)
        crit = sum(1 for f in cat_findings if f.is_critical)
        high = sum(1 for f in cat_findings if f.is_high)
        med = sum(1 for f in cat_findings if f.is_medium)
        low = sum(1 for f in cat_findings if f.is_low)
        info = sum(1 for f in cat_findings if f.is_info)

        lines.append(f"━━━ {label.upper()} ({crit} critical, {high} high, {med} medium, {low} low, {info} info) ━━━")
        lines.append("")

        # Group same-check findings for deduplication
        grouped = _group_findings(cat_findings)
        for check_name, findings in grouped.items():
            if len(findings) > 5:
                # Summarize large groups
                lines.append(f"  {_LEVEL_ICONS.get(findings[0].level, '•')} "
                             f"{findings[0].message} (×{len(findings)} occurrences)")
                lines.append(f"    Affected objects:")
                for f in findings[:5]:
                    lines.append(f"      • {f.object_name}")
                if len(findings) > 5:
                    lines.append(f"      ... and {len(findings) - 5} more")
                if findings[0].recommendation:
                    lines.append(f"    → {findings[0].recommendation}")
                lines.append("")
            else:
                for f in findings:
                    icon = _LEVEL_ICONS.get(f.level, "•")
                    lines.append(f"  {icon} [{f.object_name}] {f.message}")
                    if f.detail:
                        lines.append(f"    {f.detail}")
                    if f.recommendation:
                        lines.append(f"    → {f.recommendation}")
                    if f.sql_fix:
                        lines.append(f"    SQL: {f.sql_fix}")
                    lines.append("")

    # Footer
    lines.append("═" * w)
    lines.append("  Generated by Fabric Warehouse Advisor — Security Check")
    lines.append("═" * w)

    return "\n".join(lines)


# ======================================================================
# MARKDOWN REPORT
# ======================================================================

def generate_markdown_report(summary: CheckSummary) -> str:
    """Generate a Markdown report."""
    lines: List[str] = []

    lines.append("# 🔒 Fabric Warehouse Security Check Report")
    lines.append("")
    item = _edition_label(summary.warehouse_edition)
    lines.append(f"| Property | Value |")
    lines.append(f"|----------|-------| ")
    lines.append(f"| {item} | `{summary.warehouse_name}` |")
    lines.append("")

    # Summary
    lines.append("## Summary")
    lines.append("")
    lines.append(f"| Level | Count |")
    lines.append(f"|-------|-------|")
    lines.append(f"| ❌ Critical | **{summary.critical_count}** |")
    lines.append(f"| 🔴 High | **{summary.high_count}** |")
    lines.append(f"| 🟠 Medium | **{summary.medium_count}** |")
    lines.append(f"| 🟡 Low | **{summary.low_count}** |")
    lines.append(f"| ✅ Info | {summary.info_count} |")
    lines.append(f"| **Total** | **{len(summary.findings)}** |")
    lines.append("")

    # Findings by category
    for cat in _CATEGORY_ORDER:
        cat_findings = summary.findings_by_category(cat)
        if not cat_findings:
            continue

        label = _CATEGORY_LABELS.get(cat, cat)
        crit = sum(1 for f in cat_findings if f.is_critical)
        high = sum(1 for f in cat_findings if f.is_high)

        badge = ""
        if crit > 0:
            badge = f" — ❌ {crit} critical"
        elif high > 0:
            badge = f" — 🔴 {high} high"

        lines.append(f"## {label}{badge}")
        lines.append("")

        grouped = _group_findings(cat_findings)
        for check_name, findings in grouped.items():
            if len(findings) > 5:
                f0 = findings[0]
                lines.append(f"### {_LEVEL_ICONS.get(f0.level, '•')} {f0.message} (×{len(findings)})")
                lines.append("")
                if f0.detail:
                    lines.append(f"> {f0.detail}")
                    lines.append("")
                lines.append("<details>")
                lines.append(f"<summary>Affected objects ({len(findings)})</summary>")
                lines.append("")
                for f in findings:
                    lines.append(f"- `{f.object_name}`")
                lines.append("")
                lines.append("</details>")
                lines.append("")
                if f0.recommendation:
                    lines.append(f"**Recommendation:** {f0.recommendation}")
                    lines.append("")
                if f0.sql_fix:
                    lines.append("```sql")
                    lines.append(f0.sql_fix)
                    lines.append("```")
                    lines.append("")
            else:
                for f in findings:
                    icon = _LEVEL_ICONS.get(f.level, "•")
                    lines.append(f"- {icon} **`{f.object_name}`** — {f.message}")
                    if f.detail:
                        lines.append(f"  > {f.detail}")
                    if f.recommendation:
                        lines.append(f"  - **Recommendation:** {f.recommendation}")
                    if f.sql_fix:
                        lines.append(f"  ```sql")
                        lines.append(f"  {f.sql_fix}")
                        lines.append(f"  ```")
                    lines.append("")

    # Footer
    lines.append("---")
    lines.append("*Generated by Fabric Warehouse Advisor — Security Check*")

    return "\n".join(lines)


# ======================================================================
# HTML REPORT
# ======================================================================

def generate_html_report(summary: CheckSummary, captured_at: str | None = None) -> str:
    """Generate a self-contained HTML report with sidebar dashboard layout."""
    from datetime import datetime, timezone
    from ...core.html_template import (
        esc, html_open, html_close, render_sidebar, render_main_open,
        render_severity_stats, severity_pill, render_sql_block,
        render_footer,
    )

    if captured_at is None:
        captured_at = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")

    total = len(summary.findings)
    h: List[str] = []

    # ── Document open ───────────────────────────────────────────────
    h.append(html_open("Security Advisor Dashboard | Fabric Warehouse"))

    # ── Build tab list ──────────────────────────────────────────────
    active_cats = [
        (cat, _CATEGORY_LABELS.get(cat, cat))
        for cat in _CATEGORY_ORDER
        if summary.findings_by_category(cat)
    ]

    tabs = [(f"pane-{idx}", label) for idx, (_cat, label) in enumerate(active_cats)]

    # Determine badge label based on edition
    badge_label = _edition_label(summary.warehouse_edition)

    # ── Sidebar ─────────────────────────────────────────────────
    h.append(render_sidebar(
        brand_text="Security",
        report_type="security",
        warehouse_name=summary.warehouse_name,
        tabs=tabs,
        generated_at=captured_at,
        badge_label=badge_label,
    ))

    item = _edition_label(summary.warehouse_edition, capitalize=False)

    # ── Main content ────────────────────────────────────────────────
    h.append(render_main_open(
        "Advisor Dashboard",
        f"Comprehensive overview of your {item} security posture.",
    ))

    # Severity stat cards
    h.append(render_severity_stats(
        summary.critical_count, summary.high_count,
        summary.medium_count, summary.low_count,
        summary.info_count, total,
    ))

    if not active_cats:
        h.append(
            '<div style="text-align:center;padding:48px;color:var(--text-muted);">'
            'No findings to display.</div>'
        )
        h.append(render_footer(
            "Generated by Fabric Warehouse Advisor \u2014 Security Check"
        ))
        h.append(html_close())
        return "\n".join(h)

    # ── Tab panes ───────────────────────────────────────────────────
    for idx, (cat, label) in enumerate(active_cats):
        cat_findings = summary.findings_by_category(cat)
        active_cls = " active" if idx == 0 else ""
        hidden = "" if idx == 0 else ' hidden'

        h.append(
            f'<div class="tab-pane{active_cls}" id="pane-{idx}" '
            f'role="tabpanel"{hidden}>'
        )
        h.append(f'<h2 class="print-title">{esc(label)}</h2>')

        h.append('<div class="table-container"><div class="table-scroll">')
        h.append('<table>')
        h.append(
            '<thead><tr>'
            '<th class="col-level" data-sort="level">Level '
            '<span class="sort-icon">\u21C5</span></th>'
            '<th data-sort="text">Object '
            '<span class="sort-icon">\u21C5</span></th>'
            '<th data-sort="text">Rule '
            '<span class="sort-icon">\u21C5</span></th>'
            '<th>Finding</th>'
            '<th>Recommendation</th>'
            '</tr></thead>'
        )
        h.append('<tbody>')

        grouped = _group_findings(cat_findings)
        for check_name, findings in grouped.items():
            if len(findings) > 10:
                f0 = findings[0]
                lc = f0.level.lower()
                h.append(
                    f'<tr data-level="{lc}">'
                    f'<td class="col-level">{severity_pill(f0.level)}</td>'
                    f'<td><em>{len(findings)} objects</em></td>'
                    f'<td><code>{esc(f0.check_name)}</code></td>'
                    f'<td>{esc(f0.message)}'
                )
                if f0.detail:
                    h.append(f'<small>{esc(f0.detail)}</small>')
                h.append(
                    f'<details style="margin-top:8px">'
                    f'<summary>Show all {len(findings)} objects</summary><ul>'
                )
                for f in findings:
                    h.append(f'<li><code>{esc(f.object_name)}</code></li>')
                h.append('</ul></details></td>')
                rec = esc(f0.recommendation) if f0.recommendation else ""
                all_sql = []
                for f in findings:
                    if f.sql_fix and f.sql_fix not in all_sql:
                        all_sql.append(f.sql_fix)
                if all_sql:
                    rec += render_sql_block("\n\n".join(all_sql))
                h.append(f'<td>{rec}</td></tr>')
            else:
                for f in findings:
                    lc = f.level.lower()
                    h.append(
                        f'<tr data-level="{lc}">'
                        f'<td class="col-level">{severity_pill(f.level)}</td>'
                        f'<td><code>{esc(f.object_name)}</code></td>'
                        f'<td><code>{esc(f.check_name)}</code></td>'
                        f'<td>{esc(f.message)}'
                    )
                    if f.detail:
                        h.append(f'<small>{esc(f.detail)}</small>')
                    h.append('</td>')
                    rec = esc(f.recommendation) if f.recommendation else ""
                    if f.sql_fix:
                        rec += render_sql_block(f.sql_fix)
                    h.append(f'<td>{rec}</td></tr>')

        h.append('</tbody></table>')
        h.append('</div></div>')  # table-scroll + table-container
        h.append('</div>')  # tab-pane

    # ── Footer ──────────────────────────────────────────────────────
    h.append(render_footer(
        "Generated by Fabric Warehouse Advisor \u2014 Security Check"
    ))
    h.append(html_close())
    return "\n".join(h)


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

def _group_findings(findings: List[Finding]) -> Dict[str, List[Finding]]:
    """Group findings by check_name, preserving order."""
    grouped: Dict[str, List[Finding]] = {}
    for f in findings:
        grouped.setdefault(f.check_name, []).append(f)
    return grouped


def _esc(text: str) -> str:
    """Escape HTML special characters."""
    return (
        text.replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )
