"""
Ontology Importer - Imports ontologies from pycharter contract directories.

Reads ontology.yaml from a contract directory, parses it, optionally
enriches with field types from the pycharter contract, and materializes
concepts/fields/relationships into the knowledge graph tables.
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Any

from pycharter.wiki.ontology.parser import parse_ontology_file
from pycharter.wiki.ontology.validator import validate_ontology
from pycharter.wiki.shared.errors import IngestionError

logger = logging.getLogger(__name__)


class OntologyImporter:
    """
    Imports ontology artifacts from pycharter contract directories.

    Reads ontology.yaml, enriches with contract field types,
    and materializes into knowledge graph tables.

    Args:
        session: SQLAlchemy session (required for materialization)
    """

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

    def import_from_contract_dir(
        self,
        contract_dir: str,
        contract_id: str | None = None,
        author: str = "importer",
    ) -> dict[str, Any]:
        """
        Import ontology from a pycharter contract directory.

        Expects the directory to contain:
        - ontology.yaml: Ontology annotations
        - contract.yaml: (optional) PyCharter contract for field type enrichment

        Args:
            contract_dir: Path to the contract directory
            contract_id: Contract identifier (defaults to directory name)
            author: Author for version tracking

        Returns:
            Dict with import results (fields_imported, concepts_created, etc.)
        """
        dir_path = Path(contract_dir)
        if not dir_path.is_dir():
            raise IngestionError(f"Not a directory: {contract_dir}")

        ontology_path = dir_path / "ontology.yaml"
        if not ontology_path.exists():
            raise IngestionError(
                f"No ontology.yaml found in {contract_dir}",
            )

        if contract_id is None:
            contract_id = dir_path.name

        # Parse ontology
        artifact = parse_ontology_file(str(ontology_path))

        # Optionally enrich with contract field types
        field_types = {}
        contract_path = dir_path / "contract.yaml"
        if contract_path.exists():
            field_types = self._extract_field_types(str(contract_path))

        # Materialize if session is available
        result = {
            "contract_id": contract_id,
            "ontology_version": artifact.version,
            "fields_imported": len(artifact.fields),
            "field_types_enriched": len(field_types),
        }

        if self._session is not None:
            materialized = self._materialize(
                contract_id=contract_id,
                artifact_dict=artifact.to_dict(),
                field_types=field_types,
                author=author,
            )
            result.update(materialized)
        else:
            result["materialized"] = False

        return result

    def import_from_directory(
        self,
        base_dir: str,
        author: str = "importer",
    ) -> list[dict[str, Any]]:
        """
        Import ontologies from all subdirectories containing ontology.yaml.

        Args:
            base_dir: Base directory containing contract subdirectories
            author: Author for version tracking

        Returns:
            List of import result dicts, one per contract
        """
        base_path = Path(base_dir)
        if not base_path.is_dir():
            raise IngestionError(f"Not a directory: {base_dir}")

        results = []
        for sub_dir in sorted(base_path.iterdir()):
            if sub_dir.is_dir() and (sub_dir / "ontology.yaml").exists():
                try:
                    result = self.import_from_contract_dir(str(sub_dir), author=author)
                    results.append(result)
                except IngestionError as e:
                    logger.warning("Skipping %s: %s", sub_dir.name, e)
                    results.append({"contract_id": sub_dir.name, "error": str(e)})

        return results

    def import_concept_relationships(
        self,
        path: str,
        author: str = "importer",
    ) -> dict[str, int]:
        """
        Load concept_relationships.yaml and create ConceptRelationshipModel rows.

        Args:
            path: Path to the concept_relationships YAML file.
            author: Author for audit fields.

        Returns:
            Dict with ``created`` and ``skipped`` counts.
        """
        import yaml  # type: ignore[import-untyped]

        from pycharter.db.models.wiki.concept import ConceptModel
        from pycharter.db.models.wiki.concept_relationship import (
            ConceptRelationshipModel,
        )

        if self._session is None:
            raise IngestionError("Session required for concept relationship import")

        file_path = Path(path)
        if not file_path.exists():
            raise IngestionError(f"File not found: {path}")

        with file_path.open() as fh:
            data = yaml.safe_load(fh)

        if not isinstance(data, dict):
            raise IngestionError("concept_relationships file must be a YAML mapping")

        # Build concept name → id lookup
        concepts = self._session.query(ConceptModel).all()
        name_to_id: dict[str, Any] = {c.name: c.id for c in concepts}

        created = 0
        skipped = 0
        for rel in data.get("relationships", []):
            source_name = rel.get("source", "")
            target_name = rel.get("target", "")
            rel_type = rel.get("type", "related_to")

            source_id = name_to_id.get(source_name)
            target_id = name_to_id.get(target_name)

            if source_id is None or target_id is None:
                logger.warning(
                    "Skipping relationship %s -> %s: concept not found",
                    source_name,
                    target_name,
                )
                skipped += 1
                continue

            cr = ConceptRelationshipModel(
                source_concept_id=source_id,
                target_concept_id=target_id,
                relationship_type=rel_type,
                created_by=author,
            )
            self._session.add(cr)
            created += 1

        self._session.flush()
        return {"created": created, "skipped": skipped}

    def _extract_field_types(self, contract_path: str) -> dict[str, str | None]:
        """
        Extract field types from a pycharter contract file.

        Args:
            contract_path: Path to contract.yaml

        Returns:
            Dict mapping field_name -> data_type
        """
        try:
            from pycharter.contract_parser import parse_contract_file

            metadata = parse_contract_file(contract_path, validate=False)
            schema = metadata.schema
            properties = schema.get("properties", {})
            return {
                name: prop.get("type", "unknown")
                for name, prop in properties.items()
                if isinstance(prop, dict)
            }
        except Exception as e:
            logger.warning("Could not extract field types: %s", e)
            return {}

    def _materialize(
        self,
        contract_id: str,
        artifact_dict: dict[str, Any],
        field_types: dict[str, str | None],
        author: str,
    ) -> dict[str, Any]:
        """
        Materialize ontology into knowledge graph tables and create a version.

        Args:
            contract_id: Contract identifier
            artifact_dict: Ontology as dict
            field_types: Field type mapping from contract
            author: Author name

        Returns:
            Dict with materialization counts
        """
        from pycharter.db.models.wiki.concept import ConceptModel
        from pycharter.db.models.wiki.contract_version import ContractVersionModel
        from pycharter.db.models.wiki.field import FieldModel
        from pycharter.db.models.wiki.field_concept import FieldConceptModel
        from pycharter.db.models.wiki.field_relationship import FieldRelationshipModel

        concepts_created = 0
        fields_created = 0
        relationships_created = 0

        # Ensure concepts exist
        concept_cache: dict[str, Any] = {}
        for field_name, field_data in artifact_dict.get("fields", {}).items():
            concept_name = field_data.get("concept", "")
            if concept_name and concept_name not in concept_cache:
                existing = (
                    self._session.query(ConceptModel)
                    .filter(ConceptModel.name == concept_name)
                    .first()
                )
                if existing:
                    concept_cache[concept_name] = existing
                else:
                    concept = ConceptModel(
                        name=concept_name,
                        description=field_data.get("definition", ""),
                        node_kind=field_data.get("node_kind"),
                        created_by=author,
                    )
                    self._session.add(concept)
                    self._session.flush()
                    concept_cache[concept_name] = concept
                    concepts_created += 1

            # Handle broader concept
            broader = field_data.get("broader")
            if broader and broader not in concept_cache:
                existing = (
                    self._session.query(ConceptModel)
                    .filter(ConceptModel.name == broader)
                    .first()
                )
                if existing:
                    concept_cache[broader] = existing
                else:
                    broader_concept = ConceptModel(
                        name=broader,
                        created_by=author,
                    )
                    self._session.add(broader_concept)
                    self._session.flush()
                    concept_cache[broader] = broader_concept
                    concepts_created += 1

            # Set parent relationship
            if broader and concept_name in concept_cache and broader in concept_cache:
                concept_cache[concept_name].parent_id = concept_cache[broader].id

        # Create fields and link to concepts
        field_cache: dict[str, Any] = {}
        for field_name, field_data in artifact_dict.get("fields", {}).items():
            data_type = field_types.get(field_name)
            field_model = FieldModel(
                contract_id=contract_id,
                name=field_name,
                data_type=data_type,
                business_definition=field_data.get("definition", ""),
                created_by=author,
            )
            self._session.add(field_model)
            self._session.flush()
            field_cache[field_name] = field_model
            fields_created += 1

            # Link field to concept
            concept_name = field_data.get("concept", "")
            if concept_name and concept_name in concept_cache:
                fc = FieldConceptModel(
                    field_id=field_model.id,
                    concept_id=concept_cache[concept_name].id,
                )
                self._session.add(fc)

        # Create field relationships
        for field_name, field_data in artifact_dict.get("fields", {}).items():
            for rel in field_data.get("relationships", []):
                target_name = rel.get("target", "")
                # Target might be in format "table.field" — use field part
                target_field = (
                    target_name.split(".")[-1] if "." in target_name else target_name
                )
                if field_name in field_cache and target_field in field_cache:
                    fr = FieldRelationshipModel(
                        source_field_id=field_cache[field_name].id,
                        target_field_id=field_cache[target_field].id,
                        relationship_type=rel.get("type", "related_to"),
                        created_by=author,
                    )
                    self._session.add(fr)
                    relationships_created += 1

        # Create initial version
        version = ContractVersionModel(
            contract_id=contract_id,
            content=artifact_dict,
            message=f"Initial import of {contract_id} ontology",
            author=author,
            created_by=author,
        )
        self._session.add(version)
        self._session.flush()

        return {
            "materialized": True,
            "concepts_created": concepts_created,
            "fields_created": fields_created,
            "relationships_created": relationships_created,
            "version_id": str(version.id),
        }
