"""Mermaid CLI subprocess ile PNG render.

TürkPatent kuralları:
- Siyah-beyaz, içi boyalı değil, ≥0.5pt çizgi (CSS ile override)
- Metin ≥10pt (themeVariables.fontSize=14px ≈ 10.5pt; yeterli)
- Referans numaraları çizimde kullanıcı tarafından mermaid içinde yazılır

Neden subprocess? mermaid bir Node.js/Chromium paketi; Python için native
port yok. @mermaid-js/mermaid-cli (`mmdc`) en olgun yaklaşım.
"""

from __future__ import annotations

import asyncio
import os
import shutil
import tempfile
from pathlib import Path

_PACKAGE_ROOT = Path(__file__).resolve().parent
_BACKEND_ROOT = _PACKAGE_ROOT.parent.parent.parent  # backend-python/
_CONFIG_PATH = _PACKAGE_ROOT / "turkpatent-theme.json"


def _resolve_mmdc() -> Path | None:
    """mmdc binary'sini bul.

    Sıra:
      1. MMDC_BIN environment variable (explicit override)
      2. shutil.which("mmdc") — sistem PATH (global npm install -g senaryosu)
      3. backend-python/node_modules/.bin/mmdc — local npm install senaryosu

    None döner → kurulu değil.
    """
    env_path = os.environ.get("MMDC_BIN")
    if env_path and Path(env_path).exists():
        return Path(env_path)

    system_path = shutil.which("mmdc")
    if system_path:
        return Path(system_path)

    local = _BACKEND_ROOT / "node_modules" / ".bin" / "mmdc"
    if local.exists():
        return local

    return None


_MMDC_BIN = _resolve_mmdc()

# TPMK ≥0.5pt çizgi gereği + font boyut kontrolü için SVG-level CSS.
# mmdc -C ile inject edilebilir. Düz CSS yerine SVG element'lerini hedefliyoruz.
_PATENT_CSS = """
.node rect, .node circle, .node ellipse, .node polygon, .node path,
.cluster rect {
  fill: #ffffff !important;
  stroke: #000000 !important;
  stroke-width: 1.5px !important;
}
.edgePath .path, .flowchart-link, .messageLine0, .messageLine1,
.actor, line, path.transition {
  stroke: #000000 !important;
  stroke-width: 1.5px !important;
  fill: none !important;
}
.edgeLabel, .nodeLabel, text, tspan {
  fill: #000000 !important;
  color: #000000 !important;
  font-family: "Times New Roman", serif !important;
}
.arrowheadPath, marker path {
  fill: #000000 !important;
  stroke: #000000 !important;
}
"""


class MermaidRenderError(RuntimeError):
    """mmdc çalıştı ama render başarısız (geçersiz diyagram syntax'ı vb.)."""


class MermaidNotInstalledError(RuntimeError):
    """Node.js / mmdc kurulu değil — backend-python/node_modules bekleniyor."""


async def render_mermaid_to_png(
    source: str,
    *,
    scale: int = 3,
    width: int = 1200,
    timeout_seconds: int = 30,
) -> bytes:
    """Mermaid kaynağını TürkPatent-uyumlu siyah-beyaz PNG'e çevirir.

    Args:
        source: Mermaid diyagram kaynağı (flowchart, sequenceDiagram, vs.)
        scale: PNG ölçeklendirme faktörü (DPI eşdeğeri). 3 → ~300 DPI.
        width: Piksel genişlik (scale * width = output genişlik).
        timeout_seconds: mmdc çalışma süresi limiti.

    Returns:
        PNG bytes.

    Raises:
        MermaidNotInstalledError: mmdc bulunamazsa.
        MermaidRenderError: mermaid syntax hatası veya Chromium başlatma hatası.
    """
    if _MMDC_BIN is None or not _MMDC_BIN.exists():
        raise MermaidNotInstalledError(
            "mmdc bulunamadı. Global olarak `npm install -g @mermaid-js/mermaid-cli` "
            "veya MMDC_BIN env variable ile explicit yol belirtin."
        )
    if not _CONFIG_PATH.exists():
        raise MermaidNotInstalledError(f"TürkPatent theme config bulunamadı: {_CONFIG_PATH}")

    # mmdc dosya bekler — stdin desteklemez. Geçici dizin + cleanup.
    with tempfile.TemporaryDirectory(prefix="mermaid_") as tmpdir:
        tmp_path = Path(tmpdir)
        input_file = tmp_path / "diagram.mmd"
        output_file = tmp_path / "diagram.png"
        css_file = tmp_path / "patent.css"

        input_file.write_text(source, encoding="utf-8")
        css_file.write_text(_PATENT_CSS, encoding="utf-8")

        # Puppeteer argümanları — sandbox off (Docker/CI uyumu) + font rendering.
        # executablePath: Puppeteer default'ta kendi Chrome'unu indirmeye çalışır;
        # sunucu ortamında bu internet/disk sorunu olur. Sistem Chrome'una yönlendir:
        #   1. PUPPETEER_EXECUTABLE_PATH env variable (explicit override)
        #   2. /usr/bin/google-chrome-stable (apt paketinin kurulu olduğu yer)
        #   3. /usr/bin/chromium (apt chromium alternatifi)
        #   4. hiçbiri yoksa Puppeteer default (download) — dev ortamı için OK
        chrome_path = os.environ.get("PUPPETEER_EXECUTABLE_PATH")
        if not chrome_path:
            for candidate in (
                "/usr/bin/google-chrome-stable",
                "/usr/bin/google-chrome",
                "/usr/bin/chromium",
            ):
                if Path(candidate).exists():
                    chrome_path = candidate
                    break

        puppeteer_cfg_data: dict[str, object] = {
            "args": ["--no-sandbox", "--disable-setuid-sandbox"],
        }
        if chrome_path:
            puppeteer_cfg_data["executablePath"] = chrome_path

        import json as _json

        puppeteer_cfg = tmp_path / "puppeteer.json"
        puppeteer_cfg.write_text(
            _json.dumps(puppeteer_cfg_data),
            encoding="utf-8",
        )

        cmd = [
            str(_MMDC_BIN),
            "-i",
            str(input_file),
            "-o",
            str(output_file),
            "-c",
            str(_CONFIG_PATH),
            "-C",
            str(css_file),
            "-p",
            str(puppeteer_cfg),
            "-s",
            str(scale),
            "-w",
            str(width),
            "-b",
            "white",  # background
            "-t",
            "default",  # theme ayarı (themeVariables override eder)
        ]

        try:
            proc = await asyncio.wait_for(
                asyncio.create_subprocess_exec(
                    *cmd,
                    stdout=asyncio.subprocess.PIPE,
                    stderr=asyncio.subprocess.PIPE,
                ),
                timeout=timeout_seconds,
            )
            stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout_seconds)
        except TimeoutError as exc:
            raise MermaidRenderError(f"mmdc {timeout_seconds}s içinde tamamlanamadı") from exc

        if proc.returncode != 0:
            err = (stderr or b"").decode("utf-8", errors="replace")
            raise MermaidRenderError(f"mmdc exit {proc.returncode}: {err[:500]}")

        if not output_file.exists():
            raise MermaidRenderError("mmdc başarılı çıkış kodu verdi ama PNG üretmedi.")

        return output_file.read_bytes()


def mmdc_available() -> bool:
    """Test + health check için — mmdc kurulu mu?"""
    return _MMDC_BIN is not None and _MMDC_BIN.exists() and shutil.which("node") is not None
