import json
from pathlib import Path
from typing import Any

from app.services.graph.neo4j_client import Neo4jClient

from common_logging import get_logger

logger = get_logger(__name__)



class GraphExportService:

    def __init__(self):
        self.neo4j_client = Neo4jClient()

    def export_tenant_graph(self, tenant_id: int, output_dir: Path) -> dict[str, Any]:
        logger.bind(export_id=tenant_id).info("graph export started")
        output_dir.mkdir(parents=True, exist_ok=True)
        nodes = self._export_nodes(tenant_id)
        nodes_file = output_dir / "nodes.json"
        with open(nodes_file, "w", encoding="utf-8") as f:
            json.dump(nodes, f, indent=2, ensure_ascii=False)
        relationships = self._export_relationships(tenant_id)
        rels_file = output_dir / "relationships.json"
        with open(rels_file, "w", encoding="utf-8") as f:
            json.dump(relationships, f, indent=2, ensure_ascii=False)
        cypher_file = self._generate_cypher_script(tenant_id, output_dir, nodes, relationships)
        logger.bind(export_id=tenant_id).info(
            "graph export completed: {node_count} nodes, {rel_count} relationships",
            node_count=len(nodes),
            rel_count=len(relationships),
        )
        return {
            "nodes_file": str(nodes_file),
            "relationships_file": str(rels_file),
            "cypher_file": str(cypher_file),
            "stats": {"nodes": len(nodes), "relationships": len(relationships)},
        }

    def _export_nodes(self, tenant_id: int) -> list[dict[str, Any]]:
        query = "\n        MATCH (n)\n        WHERE n.tenant_id = $tenant_id\n        RETURN n, labels(n) as labels\n        "
        with self.neo4j_client.get_session() as session:
            result = session.run(query, tenant_id=tenant_id)
            nodes = []
            for record in result:
                node = dict(record["n"])
                node["_labels"] = record["labels"]
                nodes.append(node)
            return nodes

    def _export_relationships(self, tenant_id: int) -> list[dict[str, Any]]:
        query = "\n        MATCH (a)-[r]->(b)\n        WHERE a.tenant_id = $tenant_id AND b.tenant_id = $tenant_id\n        RETURN a.id as source_id, b.id as target_id, type(r) as type, properties(r) as props\n        "
        with self.neo4j_client.get_session() as session:
            result = session.run(query, tenant_id=tenant_id)
            return [dict(record) for record in result]

    def _generate_cypher_script(
        self, tenant_id: int, output_dir: Path, nodes: list[dict], relationships: list[dict]
    ) -> Path:
        cypher_file = output_dir / "import.cypher"
        with open(cypher_file, "w", encoding="utf-8") as f:
            for node in nodes:
                labels = ":".join(node.pop("_labels", []))
                props = json.dumps(node, ensure_ascii=False)
                f.write(f"CREATE (:{labels} {props});\n")
            for rel in relationships:
                f.write(
                    f"MATCH (a {{id: '{rel['source_id']}'}}), (b {{id: '{rel['target_id']}'}}) CREATE (a)-[:{rel['type']} {json.dumps(rel['props'], ensure_ascii=False)}]->(b);\n"
                )
        return cypher_file
