#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
"""Markdown and SVG rendering for repo_metrics.py."""

from __future__ import annotations

import html
from pathlib import Path
from typing import Any


def human_count(value: int) -> str:
    if value < 10_000:
        return str(value)
    if value < 1_000_000:
        return f"{value / 1000:.1f}k"
    if value < 1_000_000_000:
        return f"{value / 1_000_000:.2f}M"
    return f"{value / 1_000_000_000:.2f}G"


def width_for_text(text: str) -> int:
    return max(34, len(text) * 7 + 14)


def render_flat_badge(label: str, value: str, color: str = "#007ec6") -> str:
    label_w = width_for_text(label)
    value_w = width_for_text(value)
    total_w = label_w + value_w
    label_e = html.escape(label)
    value_e = html.escape(value)
    aria = html.escape(f"{label}: {value}")
    return f'''<svg xmlns="http://www.w3.org/2000/svg" width="{total_w}" height="20" role="img" aria-label="{aria}">
  <title>{aria}</title>
  <linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient>
  <clipPath id="r"><rect width="{total_w}" height="20" rx="3" fill="#fff"/></clipPath>
  <g clip-path="url(#r)"><rect width="{label_w}" height="20" fill="#555"/><rect x="{label_w}" width="{value_w}" height="20" fill="{color}"/><rect width="{total_w}" height="20" fill="url(#s)"/></g>
  <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
    <text x="{label_w / 2:.1f}" y="15" fill="#010101" fill-opacity=".3">{label_e}</text><text x="{label_w / 2:.1f}" y="14">{label_e}</text>
    <text x="{label_w + value_w / 2:.1f}" y="15" fill="#010101" fill-opacity=".3">{value_e}</text><text x="{label_w + value_w / 2:.1f}" y="14">{value_e}</text>
  </g>
</svg>
'''


def render_languages_svg(languages: list[dict[str, Any]], top_n: int) -> str:
    top = languages[:top_n]
    if not top:
        return render_flat_badge("languages", "none", "#6e7781")
    displayed_total = sum(int(item["code"]) for item in top) or 1
    bar_w = 480
    svg_w = 520
    row_h = 16
    rows = (len(top) + 1) // 2
    svg_h = 48 + rows * row_h
    parts = [
        f'<svg xmlns="http://www.w3.org/2000/svg" width="{svg_w}" height="{svg_h}" role="img" aria-label="languages by lines of code">',
        "  <title>languages by lines of code</title>",
        f'  <rect width="{svg_w}" height="{svg_h}" fill="#ffffff"/>',
        f'  <rect x="20" y="10" width="{bar_w}" height="14" rx="3" fill="#eaecef"/>',
    ]
    x = 20
    for index, item in enumerate(top):
        width = max(1, int(int(item["code"]) * bar_w / displayed_total))
        if index == len(top) - 1:
            width = max(1, 20 + bar_w - x)
        parts.append(
            f'  <rect x="{x}" y="10" width="{width}" height="14" fill="{item["color"]}"/>'
        )
        x += width
    for index, item in enumerate(top):
        col = index % 2
        row = index // 2
        base_x = 20 + col * 250
        base_y = 44 + row * row_h
        pct = float(item["percentage"])
        name = html.escape(str(item["name"]))
        parts.append(f'  <rect x="{base_x}" y="{base_y - 10}" width="10" height="10" rx="2" fill="{item["color"]}"/>')
        parts.append(
            f'  <text x="{base_x + 14}" y="{base_y}" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11" fill="#24292f">{name} <tspan fill="#57606a">{pct:.1f}%</tspan></text>'
        )
    parts.append("</svg>")
    return "\n".join(parts) + "\n"


def write_text(path: Path, content: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")


def render_markdown(metrics: dict[str, Any]) -> str:
    summary = metrics["summary"]
    deps = metrics["dependencies"]
    lines = [
        "<!-- aidevops:repo-metrics:start -->",
        "# Repository metrics",
        "",
        "Generated by `aidevops metrics generate`.",
        "",
        "## Summary",
        "",
        "| Metric | Value |",
        "|---|---:|",
        f"| Source files | {summary['source_files']:,} |",
        f"| Lines of code | {summary['code']:,} |",
        f"| Comment lines | {summary['comments']:,} |",
        f"| Blank lines | {summary['blanks']:,} |",
        f"| Languages | {summary['languages']:,} |",
        f"| Direct dependencies | {deps['direct']:,} |",
        f"| Locked/total dependencies | {deps['total']:,} |",
        "",
        "## Languages",
        "",
        "| Language | Code lines | Files | Share |",
        "|---|---:|---:|---:|",
    ]
    for item in metrics["languages"]:
        lines.append(
            f"| {item['name']} | {int(item['code']):,} | {int(item['files']):,} | {float(item['percentage']):.1f}% |"
        )
    lines.extend(["", "## Dependency manifests", "", "| Ecosystem | Manifest | Direct | Locked |", "|---|---|---:|---:|"])
    if deps["manifests"]:
        for item in deps["manifests"]:
            lines.append(
                f"| {item['ecosystem']} | `{item['path']}` | {int(item['direct']):,} | {int(item['locked']):,} |"
            )
    else:
        lines.append("| none detected | — | 0 | 0 |")
    lines.extend(
        [
            "",
            "Refresh policy: run locally when needed, or use the managed weekly/24h-fresh workflow. The scanner uses git-tracked/unignored files and does not contact external services by default.",
            "<!-- aidevops:repo-metrics:end -->",
            "",
        ]
    )
    return "\n".join(lines)
