"""
GRAPH_REPORT.md generator — automated analysis of the project graph.

Produces a Markdown report with:
  - Project overview & statistics
  - God nodes (highest-degree concepts)
  - Surprising connections (cross-cutting relationships)
  - Complexity hotspots
  - Suggested questions the graph can answer
  - Rationale summaries (WHY/TODO/HACK/NOTE comments)
"""

from __future__ import annotations

import logging
import os
import time
from collections import Counter
from typing import Any

from graphify.core.graph import GraphStore
from graphify.core.schema import EdgeType, NodeType
from graphify.search.query_engine import QueryEngine

logger = logging.getLogger(__name__)


class ReportGenerator:
    """Generates GRAPH_REPORT.md from a populated graph store."""

    def __init__(self, store: GraphStore) -> None:
        self._store = store
        self._query = QueryEngine(store)

    def generate(
        self,
        project_id: str,
        output_dir: str = "",
    ) -> str:
        """Build the full report and optionally write to file. Returns Markdown."""
        start = time.time()
        sections = [
            self._header(project_id),
            self._overview(project_id),
            self._god_nodes(project_id),
            self._surprising_connections(project_id),
            self._hotspots(project_id),
            self._rationale_summary(project_id),
            self._suggested_questions(project_id),
            self._footer(start),
        ]
        report = "\n\n".join(s for s in sections if s)

        if output_dir:
            path = os.path.join(output_dir, "GRAPH_REPORT.md")
            with open(path, "w", encoding="utf-8") as fh:
                fh.write(report)
            logger.info("Report written to %s", path)

        return report

    # ------------------------------------------------------------------
    # Sections
    # ------------------------------------------------------------------

    def _header(self, project_id: str) -> str:
        meta = self._store.get_project_meta(project_id)
        name = meta.name if meta else project_id
        return (
            f"# 📊 Graph Report: {name}\n\n"
            f"*Auto-generated by Graphify. "
            f"Read this before grepping raw files — it gives you the structural "
            f"map of the project.*"
        )

    def _overview(self, project_id: str) -> str:
        stats = self._store.stats(project_id)
        meta = self._store.get_project_meta(project_id)
        lines = ["## Overview\n"]

        if meta:
            lines.append(f"- **Project:** {meta.name}")
            lines.append(f"- **Path:** `{meta.root_path}`")
            lines.append(f"- **Files:** {meta.total_files}")
            lines.append(f"- **Lines of code:** {meta.total_lines:,}")
            lines.append(f"- **Classes:** {meta.total_classes}")
            lines.append(f"- **Functions:** {meta.total_functions}")
            lines.append(f"- **Tests:** {meta.total_tests}")
            if meta.frameworks:
                lines.append(f"- **Frameworks:** {', '.join(meta.frameworks)}")

        lines.append(f"- **Graph nodes:** {stats['nodes']}")
        lines.append(f"- **Graph edges:** {stats['edges']}")

        if meta and meta.languages:
            lines.append("\n### Language Breakdown\n")
            lines.append("| Language | Files |")
            lines.append("|----------|-------|")
            for lang, count in sorted(
                meta.languages.items(),
                key=lambda x: x[1],
                reverse=True,
            ):
                lines.append(f"| {lang} | {count} |")

        return "\n".join(lines)

    def _god_nodes(self, project_id: str) -> str:
        """Highest-degree nodes — what everything connects through."""
        gods = self._store.god_nodes(project_id, top_n=15)
        if not gods:
            return ""

        lines = [
            "## 🏛️ God Nodes\n",
            "*Highest-degree concepts — everything connects through these.*\n",
            "| Rank | Node | Type | Degree |",
            "|------|------|------|--------|",
        ]
        for i, g in enumerate(gods, 1):
            node = g["node"]
            # Skip directory/project nodes — they're structural, not conceptual
            if node.node_type in (NodeType.PROJECT, NodeType.DIRECTORY):
                continue
            name = node.name[:50]
            lines.append(f"| {i} | `{name}` | {node.node_type.value} | {g['degree']} |")

        return "\n".join(lines)

    def _surprising_connections(self, project_id: str) -> str:
        """Cross-cutting edges that bridge different parts of the codebase."""
        edges = self._store.get_edges(project_id=project_id)
        if not edges:
            return ""

        # Score surprising connections: cross-directory edges weighted higher
        scored: list[dict[str, Any]] = []
        seen = set()

        for edge in edges:
            if edge.edge_type in (EdgeType.CONTAINS, EdgeType.CONFIGURED_BY):
                continue
            pair_key = f"{edge.source_id}:{edge.target_id}"
            if pair_key in seen:
                continue
            seen.add(pair_key)

            src = self._store.get_node(edge.source_id)
            tgt = self._store.get_node(edge.target_id)
            if not src or not tgt:
                continue

            # Score: cross-file edges are more interesting
            score = 1.0
            if src.file_path != tgt.file_path:
                score += 2.0
            if edge.edge_type == EdgeType.CALLS:
                score += 1.5
            if edge.edge_type == EdgeType.INHERITS:
                score += 1.0
            if edge.provenance == "INFERRED":
                score += 0.5

            scored.append(
                {
                    "source": src,
                    "target": tgt,
                    "edge": edge,
                    "score": score,
                }
            )

        scored.sort(key=lambda x: x["score"], reverse=True)
        top = scored[:15]
        if not top:
            return ""

        lines = [
            "## 🔗 Surprising Connections\n",
            "*Cross-cutting relationships between different parts of the codebase.*\n",
            "| Source | → | Target | Relationship | Why |",
            "|--------|---|--------|-------------|-----|",
        ]
        for item in top:
            src_name = item["source"].name[:30]
            tgt_name = item["target"].name[:30]
            rel = item["edge"].edge_type.value
            why = ""
            if item["source"].file_path != item["target"].file_path:
                why = f"Cross-file ({item['source'].file_path[:25]} → {item['target'].file_path[:25]})"
            lines.append(f"| `{src_name}` | → | `{tgt_name}` | {rel} | {why} |")

        return "\n".join(lines)

    def _hotspots(self, project_id: str) -> str:
        spots = self._query.complexity_hotspots(project_id, top_n=10)
        if not spots:
            return ""

        lines = [
            "## 🔥 Complexity Hotspots\n",
            "*Files with the highest structural complexity.*\n",
            "| File | Lines | Classes | Functions | Score |",
            "|------|-------|---------|-----------|-------|",
        ]
        for s in spots:
            lines.append(
                f"| `{s['file'][:50]}` | {s['lines']} | {s['classes']} "
                f"| {s['functions']} | {s['score']} |"
            )

        return "\n".join(lines)

    def _rationale_summary(self, project_id: str) -> str:
        """Summarise WHY/TODO/HACK/NOTE comments."""
        rationale_nodes = self._store.get_nodes(
            project_id=project_id,
            node_type=NodeType.RATIONALE,
            limit=500,
        )
        if not rationale_nodes:
            return ""

        tag_counts: Counter = Counter()
        for node in rationale_nodes:
            tag = node.metadata.get("tag", "NOTE")
            tag_counts[tag] += 1

        lines = [
            "## 📝 Rationale & Design Notes\n",
            f"*{len(rationale_nodes)} rationale comments extracted across the codebase.*\n",
        ]

        for tag, count in tag_counts.most_common():
            lines.append(f"- **{tag}**: {count} occurrences")
            tag_nodes = [n for n in rationale_nodes if n.metadata.get("tag") == tag]
            for node in tag_nodes[:5]:
                text = node.metadata.get("full_text", node.name)[:80]
                lines.append(f"  - `{node.file_path}:{node.line_start}` — {text}")

        return "\n".join(lines)

    def _suggested_questions(self, project_id: str) -> str:
        """Generate questions the graph is uniquely positioned to answer."""
        meta = self._store.get_project_meta(project_id)
        stats = self._store.stats(project_id)

        questions = []

        if stats.get("node_types", {}).get("CLASS", 0) > 3:
            questions.append("What is the class inheritance hierarchy?")
        if stats.get("node_types", {}).get("TEST", 0) > 0:
            questions.append("Which modules have the most test coverage?")
        if stats.get("edge_types", {}).get("CALLS", 0) > 0:
            questions.append("What are the most-called functions?")
        if stats.get("node_types", {}).get("IMPORT", 0) > 5:
            questions.append("Which external dependencies are most used?")
        if stats.get("node_types", {}).get("RATIONALE", 0) > 0:
            questions.append("What design decisions are documented in comments?")
        if meta and len(meta.languages) > 1:
            questions.append("How do different language modules interact?")

        questions.append("What are the god nodes (high-degree concepts)?")
        questions.append("Which files are the complexity hotspots?")

        if not questions:
            return ""

        lines = [
            "## 💡 Suggested Questions\n",
            "*Questions this graph is uniquely positioned to answer:*\n",
        ]
        for i, q in enumerate(questions[:8], 1):
            lines.append(f"{i}. {q}")

        return "\n".join(lines)

    @staticmethod
    def _footer(start_time: float) -> str:
        elapsed = time.time() - start_time
        return (
            "---\n\n"
            f"*Report generated in {elapsed:.2f}s by "
            f"[Graphify](https://github.com/hoangsonww/AI-Agents-Orchestrator)*"
        )
