from __future__ import annotations

import argparse
from collections import Counter
import json
from pathlib import Path
from typing import List

from .graph import build_graph
from .ingestion import ingest_source
from .parsing import parse_document
from .recipes import execute_profile, execute_recipe
from .storage import load_document, load_graph, make_document_id, save_document, save_graph


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="neuraparse", description="Agentic document-to-dataset pipeline.")
    parser.add_argument(
        "--output-dir",
        default="data",
        help="Base directory where neuraparse stores intermediate artifacts (default: data)",
    )

    subparsers = parser.add_subparsers(dest="command", required=True)

    ingest = subparsers.add_parser("ingest", help="Ingest a document from a path or URL.")
    ingest.add_argument("source", help="Path or URL of the document to ingest.")

    build_graph = subparsers.add_parser("build-graph", help="Build a DocumentGraph for a previously ingested document.")
    build_graph.add_argument("document_id", help="Identifier of the ingested document.")

    run_recipe = subparsers.add_parser("run-recipe", help="Run a dataset recipe on a document graph.")
    run_recipe.add_argument("document_id", help="Identifier of the ingested document.")
    run_recipe.add_argument("--recipe", required=True, help="Path to the recipe configuration file (YAML/JSON).")

    subparsers.add_parser(
        "list-documents",
        help="List ingested documents in the output directory.",
    )

    subparsers.add_parser(
        "list-graphs",
        help="List stored document graphs in the output directory.",
    )

    show_summary = subparsers.add_parser(
        "show-graph-summary",
        help="Show a summary for a stored document graph.",
    )
    show_summary.add_argument("document_id", help="Identifier of the ingested document.")
    run_profile = subparsers.add_parser(
        "run-profile",
        help="Run a named profile (set of recipes) on a document graph.",
    )
    run_profile.add_argument("document_id", help="Identifier of the ingested document.")
    run_profile.add_argument("--profile", required=True, help="Name of the profile to run (e.g. 'graphrag').")
    run_profile.add_argument(
        "--profiles-config",
        help=(
            "Optional path to a profiles configuration file (JSON/YAML) that "
            "defines or overrides available profiles."
        ),
    )


    return parser


def main(argv: List[str] | None = None) -> None:
    """Entry point for the neuraparse CLI.

    For now this only parses arguments and prints stubs; the actual
    orchestration will be wired in as the core subsystems are implemented.
    """

    parser = _build_parser()
    args = parser.parse_args(argv)

    output_dir = args.output_dir

    if args.command == "ingest":
        raw = ingest_source(args.source)
        document_id = make_document_id(raw)
        from .parsing import parse_document  # local import to avoid cycles

        tree = parse_document(raw)
        path = save_document(output_dir, document_id, raw, tree)
        print(
            f"Ingested document {raw.id!r} -> document_id={document_id} "
            f"saved at {path} (nodes={len(tree.root.children)})"
        )
    elif args.command == "build-graph":
        raw, tree = load_document(output_dir, args.document_id)
        graph = build_graph(tree)
        path = save_graph(output_dir, args.document_id, graph)
        print(
            f"Built graph for document_id={args.document_id} "
            f"-> {len(graph.nodes)} nodes, {len(graph.edges)} edges saved at {path}"
        )
    elif args.command == "run-recipe":
        graph = load_graph(output_dir, args.document_id)
        dataset_path = execute_recipe(args.recipe, graph, output_dir, args.document_id)
        print(f"Recipe executed. Dataset written to {dataset_path}")
    elif args.command == "list-documents":
        docs_dir = Path(output_dir) / "documents"
        if not docs_dir.exists():
            print(f"No documents found under {docs_dir}")
            return
        files = sorted(docs_dir.glob("*.json"))
        if not files:
            print(f"No documents found under {docs_dir}")
            return
        print(f"Found {len(files)} documents under {docs_dir}:")
        for path in files:
            try:
                data = json.loads(path.read_text(encoding="utf-8"))
                document_id = data.get("document_id", path.stem)
                raw = data.get("raw", {})
                raw_id = raw.get("id", "?")
                source_type = raw.get("source_type", "?")
                print(
                    f"- document_id={document_id} raw_id={raw_id!r} "
                    f"source_type={source_type}"
                )
            except Exception:
                print(f"- {path.name} (failed to parse)")
    elif args.command == "list-graphs":
        graphs_dir = Path(output_dir) / "graphs"
        if not graphs_dir.exists():
            print(f"No graphs found under {graphs_dir}")
            return
        files = sorted(graphs_dir.glob("*.json"))
        if not files:
            print(f"No graphs found under {graphs_dir}")
            return
        print(f"Found {len(files)} graphs under {graphs_dir}:")
        for path in files:
            try:
                data = json.loads(path.read_text(encoding="utf-8"))
                document_id = data.get("document_id", path.stem)
                graph_data = data.get("graph", {})
                nodes = graph_data.get("nodes", [])
                edges = graph_data.get("edges", [])
                print(f"- document_id={document_id} nodes={len(nodes)} edges={len(edges)}")
            except Exception:
                print(f"- {path.name} (failed to parse)")
    elif args.command == "show-graph-summary":
        graph = load_graph(output_dir, args.document_id)
        total_nodes = len(graph.nodes)
        total_edges = len(graph.edges)
        node_types = Counter(n.node_type.value for n in graph.nodes)
        edge_types = Counter(e.edge_type for e in graph.edges)

        print(f"Graph summary for document_id={args.document_id}")
        print(f"- nodes={total_nodes}, edges={total_edges}")
        if node_types:
            print("- node types:")
            for t, c in sorted(node_types.items()):
                print(f"  {t}: {c}")
        if edge_types:
            print("- edge types:")
            for t, c in sorted(edge_types.items()):
                print(f"  {t}: {c}")
    elif args.command == "run-profile":
        graph = load_graph(output_dir, args.document_id)
        outputs = execute_profile(
            args.profile,
            graph,
            output_dir,
            args.document_id,
            profiles_config=args.profiles_config,
        )
        print(f"Profile {args.profile!r} executed. Datasets:")
        for path in outputs:
            print(f"- {path}")
    else:
        parser.error(f"Unknown command: {args.command}")

