#!/usr/bin/env python3
"""Generate MkDocs documentation pages from transpiled examples.

This script reads the artifacts generated by examples/generate_artifacts.py
and creates beautiful markdown documentation pages with syntax highlighting
for each query category (fraud, credit, features).
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any


def load_metadata(artifact_dir: Path) -> dict[str, Any]:
    """Load metadata.json from an artifact directory."""
    metadata_path = artifact_dir / "metadata.json"
    if not metadata_path.exists():
        return {}
    return json.loads(metadata_path.read_text(encoding="utf-8"))


def load_file_safe(file_path: Path) -> str:
    """Safely load a file, returning empty string if not found."""
    if not file_path.exists():
        return ""
    return file_path.read_text(encoding="utf-8")


def escape_html(text: str) -> str:
    """Escape HTML special characters."""
    return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")


def generate_query_section(
    query_id: str,
    artifact_dir: Path,
    index: int,
) -> str:
    """Generate markdown section for a single query."""
    metadata = load_metadata(artifact_dir)

    cypher_query = load_file_safe(artifact_dir / "query.cypher")
    sql_query = load_file_safe(artifact_dir / "databricks.sql")
    logical_plan = load_file_safe(artifact_dir / "logical_plan.txt")
    error = load_file_safe(artifact_dir / "error.txt")

    description = metadata.get("description", "Query")
    application = metadata.get("application")
    notes = metadata.get("notes")
    success = metadata.get("success", False)

    # Build markdown section - clean format
    lines = [
        f"## {index}. {description}",
        "",
    ]

    # Add application if available
    if application:
        lines.append(f"**Application**: {application}")
        lines.append("")

    # Add notes if available - collapsible
    if notes:
        lines.append("??? note \"Notes\"")
        lines.append("")
        for note_line in notes.strip().split("\n"):
            lines.append(f"    {note_line}")
        lines.append("")

    # OpenCypher query - collapsible but OPEN by default
    lines.append("???+ note \"OpenCypher Query\"")
    lines.append("    ```cypher")
    for cypher_line in cypher_query.strip().split("\n"):
        lines.append(f"    {cypher_line}")
    lines.append("    ```")
    lines.append("")

    # Generated SQL (if successful) - collapsible CLOSED
    if success and sql_query:
        lines.append("??? example \"Generated SQL\"")
        lines.append("    ```sql")
        for sql_line in sql_query.strip().split("\n"):
            lines.append(f"    {sql_line}")
        lines.append("    ```")
        lines.append("")

    # Logical Plan (if successful) - collapsible CLOSED
    if success and logical_plan:
        lines.append("??? info \"Logical Plan\"")
        lines.append("    ```")
        for plan_line in logical_plan.strip().split("\n"):
            lines.append(f"    {plan_line}")
        lines.append("    ```")
        lines.append("")

    # Error (if failed)
    if not success and error:
        lines.append("??? danger \"Error Details\"")
        lines.append("    ```")
        lines.append(f"    {error.strip()}")
        lines.append("    ```")
        lines.append("")

    lines.append("---")
    lines.append("")

    return "\n".join(lines)


def generate_category_page(
    category_name: str,
    artifacts_dir: Path,
    output_path: Path,
) -> None:
    """Generate a documentation page for a query category."""
    category_title = category_name.replace("_", " ").title()

    # Header
    lines = [
        f"# {category_title}",
        "",
        f"This page contains transpiled examples for **{category_title.lower()}** queries.",
        "",
        '!!! warning "Disclaimer"',
        "    These examples were generated by Claude, and I believe Claude was",
        "    overconfident about the usefulness of these queries. Therefore,",
        "    these examples require further curation and validation,",
        "    including the transpilation results. if you spot any issues,",
        "    please open an issue or contribute at",
        "    [gsql2rsql/issues](https://github.com/devmessias/gsql2rsql/issues)",
        "",
        "Each example shows the original OpenCypher query and its corresponding",
        "Databricks SQL translation.",
        "",
        "---",
        "",
    ]

    # Find all query artifacts for this category
    category_artifacts = sorted(artifacts_dir.glob("*"))

    if not category_artifacts:
        lines.append(f"!!! warning \"No examples found for {category_name}\"")
        lines.append("")
    else:
        # Generate section for each query
        for idx, artifact_dir in enumerate(category_artifacts, 1):
            if not artifact_dir.is_dir():
                continue

            query_id = artifact_dir.name
            section = generate_query_section(query_id, artifact_dir, idx)
            lines.append(section)

    # Write to file
    output_path.write_text("\n".join(lines), encoding="utf-8")
    print(f"  ✓ Generated: {output_path.relative_to(output_path.parent.parent)}")


def generate_index_page(
    artifacts_base: Path,
    output_path: Path,
    categories: list[str],
) -> None:
    """Generate the examples index page."""
    lines = [
        "# Query Examples",
        "",
        "Welcome to the gsql2rsql query examples gallery!",
        "",
        "This section demonstrates the transpiler's capabilities across different domains.",
        "Each example shows the original OpenCypher query alongside the generated Databricks SQL.",
        "",
        "## Available Categories",
        "",
    ]

    # Stats for each category
    for category in categories:
        category_dir = artifacts_base / category
        if not category_dir.exists():
            continue

        # Count successes and failures
        total = 0
        success = 0

        for artifact_dir in category_dir.glob("*"):
            if not artifact_dir.is_dir():
                continue
            metadata = load_metadata(artifact_dir)
            total += 1
            if metadata.get("success", False):
                success += 1

        category_title = category.replace("_queries", "").replace("_", " ").title()
        category_link = category.replace("_queries", "")

        lines.append(f"### [{category_title}]({category_link}.md)")
        lines.append("")
        lines.append(f"- **Total Queries**: {total}")
        lines.append(f"- **Successful**: {success}")
        lines.append(f"- **Failed**: {total - success}")
        lines.append("")

    lines.extend(
        [
            "## About These Examples",
            "",
            "All queries are sourced from real-world use cases in:",
            "",
            "- **Fraud Detection**: Graph-based fraud ring detection, anomaly identification",
            "- **Credit Analysis**: Relationship-based credit risk assessment",
            "- **Simple examples**: Simple examples",
            "",
            '!!! tip "Try These Yourself"',
            "    You can run any of these queries through the transpiler using:",
            "    ```bash",
            '    gsql2rsql translate --schema examples/fraud_queries.yaml "<your-query>"',
            "    ```",
            "",
        ]
    )

    output_path.write_text("\n".join(lines), encoding="utf-8")
    print(f"  ✓ Generated: {output_path.relative_to(output_path.parent.parent)}")


def _generate_quickstart_sql(cypher_query: str, strategy: str) -> str | None:
    """Transpile a query with procedural BFS for the quickstart section.

    Returns None if transpilation fails (e.g., unsupported feature).
    """
    try:
        from gsql2rsql.common.schema import EdgeSchema, EntityProperty, NodeSchema
        from gsql2rsql.parser.opencypher_parser import OpenCypherParser
        from gsql2rsql.planner.logical_plan import LogicalPlan
        from gsql2rsql.planner.pass_manager import optimize_plan
        from gsql2rsql.renderer.schema_provider import (
            SimpleSQLSchemaProvider,
            SQLTableDescriptor,
        )
        from gsql2rsql.renderer.sql_renderer import SQLRenderer

        schema = SimpleSQLSchemaProvider()
        schema.add_node(
            NodeSchema(
                name="Person",
                properties=[
                    EntityProperty("node_id", str),
                    EntityProperty("name", str),
                    EntityProperty("age", int),
                ],
                node_id_property=EntityProperty("node_id", str),
            ),
            SQLTableDescriptor(
                table_name="nodes",
                node_id_columns=["node_id"],
                filter="node_type = 'Person'",
            ),
        )
        schema.add_edge(
            EdgeSchema(
                name="KNOWS",
                source_node_id="Person",
                sink_node_id="Person",
                source_id_property=EntityProperty("src", str),
                sink_id_property=EntityProperty("dst", str),
                properties=[
                    EntityProperty("src", str),
                    EntityProperty("dst", str),
                    EntityProperty("amount", int),
                ],
            ),
            SQLTableDescriptor(
                entity_id="Person@KNOWS@Person",
                table_name="edges",
                node_id_columns=["src", "dst"],
                filter="relationship_type = 'KNOWS'",
            ),
        )

        # Enable no-label nodes and untyped edges for queries like:
        #   MATCH (root {node_id: ...}) MATCH p = (root)-[*1..4]-()
        schema.enable_no_label_support(
            table_name="nodes",
            node_id_columns=["node_id"],
            properties=[
                EntityProperty("node_id", str),
                EntityProperty("name", str),
                EntityProperty("age", int),
            ],
        )
        schema.enable_untyped_edge_support(
            table_name="edges",
            source_id_column="src",
            sink_id_column="dst",
            properties=[
                EntityProperty("src", str),
                EntityProperty("dst", str),
                EntityProperty("amount", int),
            ],
        )

        parser = OpenCypherParser()
        renderer = SQLRenderer(
            db_schema_provider=schema,
            vlp_rendering_mode="procedural",
            materialization_strategy=strategy,
        )
        ast = parser.parse(cypher_query)
        plan = LogicalPlan.process_query_tree(ast, schema)
        optimize_plan(plan)
        plan.resolve(original_query=cypher_query)
        return renderer.render_plan(plan)
    except Exception as e:
        print(f"    WARNING: quickstart SQL generation failed ({strategy}): {e}")
        return None


_QUICKSTART_CYPHER = """\
MATCH (root {node_id: 'Alice'})
MATCH p = (root)-[*1..4]-()
UNWIND relationships(p) AS r
RETURN r"""


def _build_quickstart_section() -> list[str]:
    """Build the Quick Start section with GraphContext example and live SQL."""
    sql_databricks = _generate_quickstart_sql(_QUICKSTART_CYPHER, "temp_tables")
    sql_pyspark = _generate_quickstart_sql(_QUICKSTART_CYPHER, "numbered_views")

    lines = [
        "## Quick Start with GraphContext",
        "",
        "The simplest way to use procedural BFS is via `GraphContext`:",
        "",
        '!!! warning "PySpark: enable SQL scripting"',
        "    Procedural BFS generates `BEGIN...END` blocks with `DECLARE`, "
        "`WHILE`, etc. PySpark requires SQL scripting to be enabled:",
        "",
        "    ```python",
        '    spark.conf.set("spark.sql.scripting.enabled", "true")',
        "    ```",
        "",
        "    Databricks has SQL scripting enabled by default.",
        "",
        "```python",
        "from gsql2rsql import GraphContext",
        "",
        "graph = GraphContext(",
        '    spark=spark,',
        '    nodes_table="catalog.schema.nodes",',
        '    edges_table="catalog.schema.edges",',
        ")",
        "",
        "# Procedural BFS for Databricks (default)",
        'sql = graph.transpile(',
        '    """',
        "    MATCH (root {node_id: 'Alice'})",
        "    MATCH p = (root)-[*1..4]-()",
        "    UNWIND relationships(p) AS r",
        "    RETURN r",
        '    """,',
        '    vlp_rendering_mode="procedural",',
        '    materialization_strategy="temp_tables",',
        ")",
        "",
        "# Procedural BFS for PySpark 4.2+",
        'sql_pyspark = graph.transpile(',
        '    """',
        "    MATCH (root {node_id: 'Alice'})",
        "    MATCH p = (root)-[*1..4]-()",
        "    UNWIND relationships(p) AS r",
        "    RETURN r",
        '    """,',
        '    vlp_rendering_mode="procedural",',
        '    materialization_strategy="numbered_views",',
        ")",
        "```",
        "",
        '!!! info "Edge collection support"',
        "    Procedural BFS supports `UNWIND relationships(path) AS r` to "
        "access edge properties. Each BFS result row is one edge, so "
        "`UNWIND` produces one row per traversed edge. "
        "`nodes(path)` is **not** supported (requires full path "
        "reconstruction). Use `vlp_rendering_mode='cte'` if you need it.",
        "",
    ]

    # Add generated SQL examples
    if sql_databricks:
        lines.append('??? example "Generated SQL — Databricks (temp_tables)"')
        lines.append("    ```sql")
        for sql_line in sql_databricks.strip().split("\n"):
            lines.append(f"    {sql_line}")
        lines.append("    ```")
        lines.append("")

    if sql_pyspark:
        lines.append(
            '??? example "Generated SQL — PySpark 4.2 (numbered_views)"',
        )
        lines.append("    ```sql")
        for sql_line in sql_pyspark.strip().split("\n"):
            lines.append(f"    {sql_line}")
        lines.append("    ```")
        lines.append("")

    lines.append("---")
    lines.append("")

    return lines


def generate_procedural_bfs_page(
    artifacts_base: Path,
    categories: list[str],
    output_path: Path,
) -> None:
    """Generate a documentation page for procedural BFS examples.

    Collects all VLP queries across all categories that have procedural
    BFS SQL output (both Databricks temp_tables and PySpark numbered_views).
    """
    lines = [
        "# Procedural BFS Examples",
        "",
        "This page shows queries with **variable-length paths** (VLP) transpiled",
        "using the **procedural BFS** rendering mode.",
        "",
        "Procedural BFS uses SQL scripting (`BEGIN...END`, `WHILE`) instead of",
        "`WITH RECURSIVE` CTEs. This enables a **global visited set** that",
        "prevents re-visiting nodes (shortest-path semantics).",
        "",
        "Two materialization strategies are shown:",
        "",
        "- **Databricks** (`temp_tables`): Uses `CREATE TEMPORARY TABLE` + "
        "`INSERT INTO`. Fixed table names, O(1) visited reads per level.",
        "- **PySpark 4.2** (`numbered_views`): Uses `EXECUTE IMMEDIATE` + "
        "numbered views. Dynamic names, UNION chain for visited.",
        "",
        "---",
        "",
    ]

    # Add Quick Start section with GraphContext example
    lines.extend(_build_quickstart_section())

    query_idx = 0

    for category in sorted(categories):
        category_dir = artifacts_base / category

        for artifact_dir in sorted(category_dir.glob("*")):
            if not artifact_dir.is_dir():
                continue

            metadata = load_metadata(artifact_dir)
            has_dbr = metadata.get("has_procedural_databricks", False)
            has_pyspark = metadata.get("has_procedural_pyspark", False)

            if not has_dbr and not has_pyspark:
                continue

            query_idx += 1
            cypher = load_file_safe(artifact_dir / "query.cypher")
            proc_dbr = load_file_safe(
                artifact_dir / "procedural_databricks.sql",
            )
            proc_pyspark = load_file_safe(
                artifact_dir / "procedural_pyspark.sql",
            )
            description = metadata.get("description", "Query")
            category_label = category.replace("_queries", "").title()

            lines.append(
                f"## {query_idx}. {description}"
            )
            lines.append("")
            lines.append(f"**Source**: {category_label}")
            lines.append("")

            # Cypher query
            lines.append('???+ note "OpenCypher Query"')
            lines.append("    ```cypher")
            for cypher_line in cypher.strip().split("\n"):
                lines.append(f"    {cypher_line}")
            lines.append("    ```")
            lines.append("")

            # Databricks (temp_tables)
            if proc_dbr:
                lines.append(
                    '??? example "Procedural BFS — Databricks '
                    '(temp_tables)"',
                )
                lines.append("    ```sql")
                for sql_line in proc_dbr.strip().split("\n"):
                    lines.append(f"    {sql_line}")
                lines.append("    ```")
                lines.append("")

            # PySpark (numbered_views)
            if proc_pyspark:
                lines.append(
                    '??? example "Procedural BFS — PySpark 4.2 '
                    '(numbered_views)"',
                )
                lines.append("    ```sql")
                for sql_line in proc_pyspark.strip().split("\n"):
                    lines.append(f"    {sql_line}")
                lines.append("    ```")
                lines.append("")

            lines.append("---")
            lines.append("")

    if query_idx == 0:
        lines.append(
            "!!! warning \"No procedural BFS examples found\""
        )
        lines.append(
            "    Run `python examples/generate_artifacts.py` first."
        )
        lines.append("")

    output_path.write_text("\n".join(lines), encoding="utf-8")
    print(
        f"  ✓ Generated: "
        f"{output_path.relative_to(output_path.parent.parent)}"
    )


def main() -> int:
    """Main entry point."""
    # Paths
    project_root = Path(__file__).parent.parent
    artifacts_base = project_root / "examples" / "out"
    docs_examples = project_root / "docs" / "examples"

    # Create docs/examples directory
    docs_examples.mkdir(parents=True, exist_ok=True)

    print("Generating example documentation pages...")
    print()

    # Check if artifacts exist
    if not artifacts_base.exists():
        print(f"ERROR: Artifacts directory not found: {artifacts_base}")
        print("Please run: python examples/generate_artifacts.py")
        return 1

    # Find categories
    categories = [d.name for d in artifacts_base.iterdir() if d.is_dir()]
    categories = [c for c in categories if not c.startswith(".")]

    if not categories:
        print(f"ERROR: No categories found in {artifacts_base}")
        return 1

    print(f"Found {len(categories)} categories: {', '.join(categories)}")
    print()

    # Generate index page
    index_path = docs_examples / "index.md"
    generate_index_page(artifacts_base, index_path, categories)

    # Generate page for each category
    for category in categories:
        category_dir = artifacts_base / category
        category_file = category.replace("_queries", "")
        output_path = docs_examples / f"{category_file}.md"

        print(f"Processing category: {category}")
        generate_category_page(category, category_dir, output_path)
        print()

    # Generate procedural BFS page
    print("Processing: procedural_bfs")
    procedural_path = docs_examples / "procedural_bfs.md"
    generate_procedural_bfs_page(
        artifacts_base, categories, procedural_path,
    )
    print()

    print("✓ Example documentation generated successfully!")
    return 0


if __name__ == "__main__":
    exit(main())
