"""
Concept Manager -- CRUD operations for individual concepts.

Works with the wiki concepts table via SQLAlchemy session.
"""

from __future__ import annotations

from typing import Any

from pycharter.wiki.ontology.models import ConceptDefinition
from pycharter.wiki.rdf.context import concept_uri
from pycharter.wiki.shared.errors import OntologyError


class ConceptManager:
    """
    Manages individual concepts in the knowledge graph.

    Args:
        session: SQLAlchemy session.
    """

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

    def _model_class(self) -> type:
        from pycharter.db.models.wiki.concept import ConceptModel

        return ConceptModel

    def get(self, name: str) -> dict[str, Any] | None:
        """Get a concept by name. Returns dict or None."""
        Model = self._model_class()
        row = self._session.query(Model).filter(Model.name == name).first()
        if row is None:
            return None
        return self._row_to_dict(row)

    def list_all(self) -> list[dict[str, Any]]:
        """List all concepts."""
        Model = self._model_class()
        rows = self._session.query(Model).order_by(Model.name).all()
        return [self._row_to_dict(r) for r in rows]

    def create(
        self,
        concept: ConceptDefinition,
        *,
        author: str = "api",
    ) -> dict[str, Any]:
        """
        Create a new concept.

        Raises OntologyError if a concept with the same name already exists.
        """
        Model = self._model_class()
        existing = self._session.query(Model).filter(Model.name == concept.id).first()
        if existing:
            raise OntologyError(f"Concept already exists: {concept.id}")

        parent_id = None
        if concept.broader:
            parent = (
                self._session.query(Model).filter(Model.name == concept.broader).first()
            )
            if parent:
                parent_id = parent.id

        row = Model(
            name=concept.id,
            description=concept.definition,
            parent_id=parent_id,
            uri=concept_uri(concept.id),
            notation=concept.notation,
            deprecated=concept.deprecated,
            node_kind=concept.node_kind,
            tier="core" if not concept.broader else "domain",
            created_by=author,
        )
        self._session.add(row)
        self._session.flush()
        return self._row_to_dict(row)

    def update(
        self,
        name: str,
        updates: dict[str, Any],
        *,
        author: str = "api",
    ) -> dict[str, Any]:
        """
        Update an existing concept by name.

        Accepted update keys: description, notation, deprecated, broader, tier, node_kind.
        """
        Model = self._model_class()
        row = self._session.query(Model).filter(Model.name == name).first()
        if row is None:
            raise OntologyError(f"Concept not found: {name}")

        allowed = {"description", "notation", "deprecated", "tier", "node_kind"}
        for key, value in updates.items():
            if key in allowed:
                setattr(row, key, value)

        if "broader" in updates:
            broader_name = updates["broader"]
            if broader_name:
                parent = (
                    self._session.query(Model)
                    .filter(Model.name == broader_name)
                    .first()
                )
                row.parent_id = parent.id if parent else None
            else:
                row.parent_id = None

        row.updated_by = author
        self._session.flush()
        return self._row_to_dict(row)

    def delete(self, name: str) -> bool:
        """Delete a concept by name. Returns True if deleted."""
        Model = self._model_class()
        row = self._session.query(Model).filter(Model.name == name).first()
        if row is None:
            return False
        self._session.delete(row)
        self._session.flush()
        return True

    @staticmethod
    def _row_to_dict(row: Any) -> dict[str, Any]:
        return {
            "id": str(row.id),
            "name": row.name,
            "description": row.description,
            "parent_id": str(row.parent_id) if row.parent_id else None,
            "uri": row.uri,
            "notation": row.notation,
            "deprecated": row.deprecated,
            "tier": row.tier,
            "node_kind": row.node_kind,
        }
