"""SVG chart generation for human reports."""

from __future__ import annotations

from math import cos, pi, sin
from pathlib import Path
from typing import Sequence

from .catalog import HUMAN_PROFILES, PROFILE_BY_ID
from .models import MatrixCell, PlatformHumanReport


def svg_escape(value: object) -> str:
    return (
        str(value)
        .replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )


def color_for_score(score: int) -> str:
    if score >= 85:
        return "#1f9d72"
    if score >= 70:
        return "#43b881"
    if score >= 55:
        return "#e0a72e"
    if score >= 35:
        return "#d66b37"
    return "#bf3f4a"


def text(x: int | float, y: int | float, value: str, size: int = 12, anchor: str = "start", weight: str = "400") -> str:
    return (
        f'<text x="{x}" y="{y}" font-family="Arial, sans-serif" font-size="{size}" '
        f'font-weight="{weight}" text-anchor="{anchor}" fill="#18202a">{svg_escape(value)}</text>'
    )


def rect(x: int | float, y: int | float, width: int | float, height: int | float, fill: str, stroke: str = "none") -> str:
    return f'<rect x="{x}" y="{y}" width="{width}" height="{height}" rx="4" fill="{fill}" stroke="{stroke}"/>'


def write_svg(path: Path, width: int, height: int, body: str) -> Path:
    path.parent.mkdir(parents=True, exist_ok=True)
    svg = (
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" '
        f'viewBox="0 0 {width} {height}" role="img">'
        '<rect width="100%" height="100%" fill="#f8fafc"/>'
        + body
        + "</svg>"
    )
    path.write_text(svg, encoding="utf-8")
    return path


def matrix_heatmap_svg(path: Path, cells: Sequence[MatrixCell]) -> Path:
    platform_ids = sorted({cell.platform_id for cell in cells})
    profiles = HUMAN_PROFILES
    cell_w = 74
    cell_h = 32
    left = 190
    top = 130
    width = left + cell_w * len(profiles) + 40
    height = top + cell_h * len(platform_ids) + 70
    by_pair = {(cell.platform_id, cell.profile_id): cell for cell in cells}
    parts: list[str] = []
    parts.append(text(30, 36, "Matriz plataforma x perfil humano", 20, weight="700"))
    parts.append(text(30, 58, "Score 0-100 calculado por evidencias locais, categorias e lacunas.", 12))
    for index, profile in enumerate(profiles):
        x = left + index * cell_w + cell_w / 2
        label = profile.profile_id.replace("_", " ")
        parts.append(
            f'<text x="{x}" y="{top - 16}" font-family="Arial, sans-serif" font-size="10" '
            'text-anchor="end" transform="rotate(-45 '
            f'{x},{top - 16})" fill="#334155">{svg_escape(label)}</text>'
        )
    for row_index, platform_id in enumerate(platform_ids):
        y = top + row_index * cell_h
        parts.append(text(30, y + 21, platform_id, 12, weight="700"))
        for col_index, profile in enumerate(profiles):
            x = left + col_index * cell_w
            cell = by_pair.get((platform_id, profile.profile_id))
            score = cell.score if cell else 0
            parts.append(rect(x, y, cell_w - 4, cell_h - 4, color_for_score(score), "#ffffff"))
            parts.append(text(x + (cell_w - 4) / 2, y + 19, str(score), 11, anchor="middle", weight="700"))
    parts.append(text(30, height - 28, "Verde indica maior prontidao humana; vermelho indica lacuna prioritaria.", 11))
    return write_svg(path, width, height, "".join(parts))


def platform_bar_svg(path: Path, reports: Sequence[PlatformHumanReport]) -> Path:
    ordered = sorted(reports, key=lambda report: report.average_score, reverse=True)
    width = 980
    row_h = 38
    top = 72
    left = 245
    height = top + row_h * len(ordered) + 45
    parts: list[str] = []
    parts.append(text(30, 36, "Maturidade humana por plataforma", 20, weight="700"))
    for index, report in enumerate(ordered):
        y = top + index * row_h
        parts.append(text(30, y + 20, report.platform.platform_id, 12, weight="700"))
        parts.append(rect(left, y, 600, 22, "#e2e8f0"))
        bar_w = max(2, report.average_score * 6)
        parts.append(rect(left, y, bar_w, 22, color_for_score(report.average_score)))
        parts.append(text(left + bar_w + 10, y + 16, str(report.average_score), 12, weight="700"))
    return write_svg(path, width, height, "".join(parts))


def profile_radar_svg(path: Path, report: PlatformHumanReport) -> Path:
    cells = sorted(report.cells, key=lambda cell: cell.profile_id)
    if not cells:
        return write_svg(path, 640, 480, text(30, 30, "Sem dados"))
    width = 760
    height = 680
    cx = width / 2
    cy = height / 2 + 20
    radius = 230
    parts: list[str] = []
    parts.append(text(30, 36, f"Radar humano: {report.platform.title}", 20, weight="700"))
    for ring in range(1, 6):
        r = radius * ring / 5
        points = polygon_points(len(cells), cx, cy, r)
        parts.append(f'<polygon points="{points}" fill="none" stroke="#cbd5e1" stroke-width="1"/>')
    data_points = []
    for index, cell in enumerate(cells):
        angle = -pi / 2 + 2 * pi * index / len(cells)
        score_radius = radius * cell.score / 100
        data_points.append((cx + cos(angle) * score_radius, cy + sin(angle) * score_radius))
        label_radius = radius + 58
        profile = PROFILE_BY_ID[cell.profile_id]
        lx = cx + cos(angle) * label_radius
        ly = cy + sin(angle) * label_radius
        parts.append(text(round(lx, 2), round(ly, 2), profile.name[:22], 10, anchor="middle"))
    parts.append(
        '<polygon points="'
        + " ".join(f"{round(x, 2)},{round(y, 2)}" for x, y in data_points)
        + '" fill="#2f80ed55" stroke="#2f80ed" stroke-width="2"/>'
    )
    for x, y in data_points:
        parts.append(f'<circle cx="{round(x,2)}" cy="{round(y,2)}" r="4" fill="#2f80ed"/>')
    return write_svg(path, width, height, "".join(parts))


def polygon_points(count: int, cx: float, cy: float, radius: float) -> str:
    points = []
    for index in range(count):
        angle = -pi / 2 + 2 * pi * index / count
        points.append(f"{round(cx + cos(angle) * radius, 2)},{round(cy + sin(angle) * radius, 2)}")
    return " ".join(points)

