"""
JSON Renderer — produces a structured JSON file + interactive React viewer.

Outputs:
  hld_data.json  — complete HLD data consumable by external tools.
  viewer.html    — self-contained React viewer with embedded data.
  assets/        — JS/CSS bundles for the viewer.
"""

from __future__ import annotations

import json
import logging
import shutil
from datetime import datetime, timezone
from pathlib import Path

from hld_generator import __version__
from hld_generator.graph import CodeGraph
from hld_generator.html_renderer import build_package_diagram
from hld_generator.llm import LLMAnalysis

logger = logging.getLogger(__name__)

# Resolve the bundled viewer directory (built from frontend/).
_VIEWER_DIR = Path(__file__).resolve().parent / "viewer"


class JSONRenderer:
    """Generate a structured JSON file + interactive React viewer."""

    VERSION = "1.0"

    def __init__(self, output_dir: Path) -> None:
        self.output_dir = output_dir
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def render(self, analysis: LLMAnalysis, code_graph: CodeGraph) -> list[Path]:
        """Serialize HLD data to JSON and generate the interactive viewer."""
        data = self._serialize(analysis, code_graph)
        json_text = json.dumps(data, indent=2, ensure_ascii=False)

        # 1. Write raw JSON
        json_path = self.output_dir / "hld_data.json"
        json_path.write_text(json_text, encoding="utf-8")
        logger.info("Wrote JSON data: %s", json_path)

        output_files = [json_path]

        # 2. Generate interactive viewer (if bundled viewer assets exist)
        viewer_files = self._generate_viewer(json_text)
        output_files.extend(viewer_files)

        return output_files

    def _generate_viewer(self, json_text: str) -> list[Path]:
        """Copy the bundled React viewer and embed the JSON data."""
        viewer_index = _VIEWER_DIR / "index.html"
        viewer_assets = _VIEWER_DIR / "assets"

        if not viewer_index.exists():
            logger.debug("Bundled viewer not found at %s — skipping", _VIEWER_DIR)
            return []

        output_files: list[Path] = []

        # Read the viewer HTML and inject JSON data before </body>.
        # Escape "</" to "<\/" to prevent premature script tag closure
        # if JSON values contain "</script>".  JSON parsers treat
        # "<\/" identically to "</" so the data round-trips safely.
        html = viewer_index.read_text(encoding="utf-8")
        safe_json = json_text.replace("</", "<\\/")
        data_script = (
            '<script id="hld-data" type="application/json">'
            + safe_json
            + "</script>"
        )
        html = html.replace("</body>", f"  {data_script}\n  </body>")

        viewer_path = self.output_dir / "viewer.html"
        viewer_path.write_text(html, encoding="utf-8")
        output_files.append(viewer_path)
        logger.info("Wrote interactive viewer: %s", viewer_path)

        # Copy assets/ directory
        if viewer_assets.is_dir():
            dest_assets = self.output_dir / "assets"
            if dest_assets.exists():
                shutil.rmtree(dest_assets)
            shutil.copytree(viewer_assets, dest_assets)
            output_files.append(dest_assets)

        # Copy favicon
        favicon = _VIEWER_DIR / "favicon.svg"
        if favicon.exists():
            dest_favicon = self.output_dir / "favicon.svg"
            shutil.copy2(favicon, dest_favicon)

        return output_files

    def _serialize(self, analysis: LLMAnalysis, cg: CodeGraph) -> dict:
        return {
            "version": self.VERSION,
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "generator_version": __version__,
            "project_name": analysis.project_name or "Codebase Analysis",
            "summary": analysis.summary,
            "architecture_pattern": analysis.architecture_pattern,
            "components": analysis.components,
            "data_flow": analysis.data_flow,
            "external_dependencies": analysis.external_dependencies,
            "tech_stack": analysis.tech_stack,
            "mermaid_diagram": analysis.mermaid_diagram or "",
            "mermaid_package_diagram": build_package_diagram(cg),
            "observations": analysis.observations,
            "error": analysis.error or None,
            "graph": self._serialize_graph(cg),
        }

    @staticmethod
    def _serialize_graph(cg: CodeGraph) -> dict:
        modules = {}
        for mid, mod in cg.modules.items():
            modules[mid] = {
                "id": mod.id,
                "language": mod.language,
                "package": mod.package,
                "classes": mod.classes,
                "functions": mod.functions,
                "endpoints": mod.endpoints,
                "imports_raw": mod.imports_raw,
                "line_count": mod.line_count,
                "summary": mod.summary,
            }

        edges = [
            {
                "source": src,
                "target": dst,
                "label": data.get("label", "imports"),
            }
            for src, dst, data in cg.graph.edges(data=True)
        ]

        return {
            "modules": modules,
            "packages": {k: list(v) for k, v in cg.packages.items()},
            "languages": dict(cg.languages),
            "total_lines": cg.total_lines,
            "entry_points": cg.entry_points,
            "hub_modules": cg.hub_modules,
            "leaf_modules": cg.leaf_modules,
            "edges": edges,
        }
