"""
Knowledge Graph - Concept hierarchy traversal and field relationships.

Uses recursive CTEs for ancestor/descendant queries when backed by
a database, with in-memory fallback for testing.
"""

from __future__ import annotations

from typing import Any

from pycharter.wiki.shared.errors import GraphError


class KnowledgeGraph:
    """
    Traverses the concept hierarchy and field relationships.

    Args:
        session: SQLAlchemy session (optional for in-memory mode)
    """

    def __init__(self, session=None):
        self._session = session

    def _require_session(self):
        if self._session is None:
            raise GraphError("No database session configured")

    def get_ancestors(self, concept_id: str) -> list[dict[str, Any]]:
        """
        Get all ancestor concepts (parent, grandparent, etc.)
        using a recursive CTE.

        Args:
            concept_id: UUID of the starting concept

        Returns:
            List of ancestor concept dicts, from immediate parent to root
        """
        self._require_session()

        from sqlalchemy import text

        query = text("""
            WITH RECURSIVE ancestors AS (
                SELECT id, name, description, parent_id, tier, 1 as depth
                FROM pycharter.concepts
                WHERE id = (
                    SELECT parent_id FROM pycharter.concepts WHERE id = :concept_id
                )
                UNION ALL
                SELECT c.id, c.name, c.description, c.parent_id, c.tier, a.depth + 1
                FROM pycharter.concepts c
                JOIN ancestors a ON c.id = a.parent_id
            )
            SELECT id, name, description, parent_id, tier, depth
            FROM ancestors
            ORDER BY depth
        """)

        result = self._session.execute(query, {"concept_id": concept_id})
        return [
            {
                "id": str(row.id),
                "name": row.name,
                "description": row.description,
                "parent_id": str(row.parent_id) if row.parent_id else None,
                "tier": row.tier,
                "depth": row.depth,
            }
            for row in result
        ]

    def get_descendants(self, concept_id: str) -> list[dict[str, Any]]:
        """
        Get all descendant concepts (children, grandchildren, etc.)
        using a recursive CTE.

        Args:
            concept_id: UUID of the starting concept

        Returns:
            List of descendant concept dicts
        """
        self._require_session()

        from sqlalchemy import text

        query = text("""
            WITH RECURSIVE descendants AS (
                SELECT id, name, description, parent_id, tier, 1 as depth
                FROM pycharter.concepts
                WHERE parent_id = :concept_id
                UNION ALL
                SELECT c.id, c.name, c.description, c.parent_id, c.tier, d.depth + 1
                FROM pycharter.concepts c
                JOIN descendants d ON c.parent_id = d.id
            )
            SELECT id, name, description, parent_id, tier, depth
            FROM descendants
            ORDER BY depth, name
        """)

        result = self._session.execute(query, {"concept_id": concept_id})
        return [
            {
                "id": str(row.id),
                "name": row.name,
                "description": row.description,
                "parent_id": str(row.parent_id) if row.parent_id else None,
                "tier": row.tier,
                "depth": row.depth,
            }
            for row in result
        ]

    def get_concept_tree(self) -> dict[str, Any]:
        """
        Get the full concept hierarchy as a tree.

        Returns:
            Tree dict with 'roots' containing nested concept nodes
        """
        self._require_session()

        from pycharter.db.models.wiki.concept import ConceptModel

        concepts = self._session.query(ConceptModel).all()

        # Build lookup and children map
        lookup = {}
        children_map: dict[str | None, list[str]] = {}
        for c in concepts:
            cid = str(c.id)
            lookup[cid] = {
                "id": cid,
                "name": c.name,
                "description": c.description,
                "tier": c.tier,
                "node_kind": c.node_kind,
                "children": [],
            }
            pid = str(c.parent_id) if c.parent_id else None
            if pid not in children_map:
                children_map[pid] = []
            children_map[pid].append(cid)

        def build_tree(cid: str) -> dict[str, Any]:
            node = lookup[cid]
            if cid in children_map:
                node["children"] = [build_tree(child) for child in children_map[cid]]
            return node

        roots = children_map.get(None, [])
        return {"roots": [build_tree(r) for r in roots]}

    def find_related_fields(self, concept_id: str) -> list[dict[str, Any]]:
        """
        Get all fields associated with a concept.

        Args:
            concept_id: UUID of the concept

        Returns:
            List of field dicts
        """
        self._require_session()

        from sqlalchemy import text

        query = text("""
            SELECT f.id, f.contract_id, f.name, f.data_type, f.business_definition
            FROM pycharter.fields f
            JOIN pycharter.field_concepts fc ON f.id = fc.field_id
            WHERE fc.concept_id = :concept_id
        """)

        result = self._session.execute(query, {"concept_id": concept_id})
        return [
            {
                "id": str(row.id),
                "contract_id": row.contract_id,
                "name": row.name,
                "data_type": row.data_type,
                "business_definition": row.business_definition,
            }
            for row in result
        ]

    def get_fields_for_concept(self, concept_name: str) -> list[dict[str, Any]]:
        """
        Get all fields associated with a concept by name.

        Args:
            concept_name: Name of the concept

        Returns:
            List of field dicts
        """
        self._require_session()

        from pycharter.db.models.wiki.concept import ConceptModel

        concept = (
            self._session.query(ConceptModel)
            .filter(ConceptModel.name == concept_name)
            .first()
        )
        if concept is None:
            return []
        return self.find_related_fields(str(concept.id))

    def get_concept_relationships(
        self,
        concept_id: str,
        relationship_type: str | None = None,
        direction: str = "outgoing",
    ) -> list[dict[str, Any]]:
        """
        Get concept-level relationships, optionally filtered by type and direction.

        Args:
            concept_id: UUID of the concept.
            relationship_type: Filter by this type (or None for all).
            direction: ``"outgoing"``, ``"incoming"``, or ``"both"``.

        Returns:
            List of relationship dicts with source/target names and type.
        """
        self._require_session()

        from sqlalchemy import text

        from pycharter.wiki.ontology.models import resolve_inverse

        results: list[dict[str, Any]] = []

        if direction in ("outgoing", "both"):
            sql = """
                SELECT cr.id, cr.relationship_type,
                       s.name as source_name, t.name as target_name,
                       cr.source_concept_id, cr.target_concept_id
                FROM pycharter.concept_relationships cr
                JOIN pycharter.concepts s ON s.id = cr.source_concept_id
                JOIN pycharter.concepts t ON t.id = cr.target_concept_id
                WHERE cr.source_concept_id = :concept_id
            """
            if relationship_type:
                sql += " AND cr.relationship_type = :rel_type"
            rows = self._session.execute(
                text(sql),
                {"concept_id": concept_id, "rel_type": relationship_type},
            )
            for row in rows:
                results.append(
                    {
                        "id": str(row.id),
                        "source_name": row.source_name,
                        "target_name": row.target_name,
                        "relationship_type": row.relationship_type,
                        "direction": "outgoing",
                    }
                )

        if direction in ("incoming", "both"):
            sql = """
                SELECT cr.id, cr.relationship_type,
                       s.name as source_name, t.name as target_name,
                       cr.source_concept_id, cr.target_concept_id
                FROM pycharter.concept_relationships cr
                JOIN pycharter.concepts s ON s.id = cr.source_concept_id
                JOIN pycharter.concepts t ON t.id = cr.target_concept_id
                WHERE cr.target_concept_id = :concept_id
            """
            if relationship_type:
                sql += " AND cr.relationship_type = :rel_type"
            rows = self._session.execute(
                text(sql),
                {"concept_id": concept_id, "rel_type": relationship_type},
            )
            for row in rows:
                results.append(
                    {
                        "id": str(row.id),
                        "source_name": row.source_name,
                        "target_name": row.target_name,
                        "relationship_type": resolve_inverse(row.relationship_type),
                        "direction": "incoming",
                    }
                )

        return results
