"""
Ontology Validator - Validates ontology structure and content.

Provides validation for both raw dicts (pre-parse) and
OntologyArtifact instances (post-parse).
"""

from __future__ import annotations

from typing import Any

from pycharter.wiki.ontology.models import OntologyArtifact, RelationshipType


def validate_ontology(ontology_data: dict[str, Any]) -> list[dict[str, str]]:
    """
    Validate raw ontology data before parsing.

    Checks structural requirements:
    - Must have 'version' field
    - Must have 'fields' dict
    - Each field must have 'concept'
    - Relationship types must be valid
    - No circular broader references

    Args:
        ontology_data: Raw ontology dictionary

    Returns:
        List of error dicts with 'path' and 'message' keys.
        Empty list means valid.
    """
    errors: list[dict[str, str]] = []

    if not isinstance(ontology_data, dict):
        errors.append({"path": "", "message": "Ontology must be a dictionary"})
        return errors

    # Check version
    if "version" not in ontology_data:
        errors.append(
            {"path": "version", "message": "Missing required field 'version'"}
        )

    # Check fields
    fields = ontology_data.get("fields")
    if fields is None:
        errors.append({"path": "fields", "message": "Missing required field 'fields'"})
        return errors

    if not isinstance(fields, dict):
        errors.append({"path": "fields", "message": "'fields' must be a dictionary"})
        return errors

    valid_rel_types = {t.value for t in RelationshipType}

    for field_name, field_data in fields.items():
        prefix = f"fields.{field_name}"

        if not isinstance(field_data, dict):
            errors.append(
                {
                    "path": prefix,
                    "message": f"Field '{field_name}' must be a dictionary",
                }
            )
            continue

        # concept is required
        if "concept" not in field_data:
            errors.append(
                {
                    "path": f"{prefix}.concept",
                    "message": "Missing required field 'concept'",
                }
            )

        # tags must be a list
        tags = field_data.get("tags")
        if tags is not None and not isinstance(tags, list):
            errors.append(
                {"path": f"{prefix}.tags", "message": "'tags' must be a list"}
            )

        # lineage must be a dict
        lineage = field_data.get("lineage")
        if lineage is not None and not isinstance(lineage, dict):
            errors.append(
                {
                    "path": f"{prefix}.lineage",
                    "message": "'lineage' must be a dictionary",
                }
            )

        # relationships must be a list of dicts with valid types
        relationships = field_data.get("relationships")
        if relationships is not None:
            if not isinstance(relationships, list):
                errors.append(
                    {
                        "path": f"{prefix}.relationships",
                        "message": "'relationships' must be a list",
                    }
                )
            else:
                for i, rel in enumerate(relationships):
                    rel_prefix = f"{prefix}.relationships[{i}]"
                    if not isinstance(rel, dict):
                        errors.append(
                            {
                                "path": rel_prefix,
                                "message": "Relationship must be a dictionary",
                            }
                        )
                        continue
                    if "target" not in rel:
                        errors.append(
                            {
                                "path": f"{rel_prefix}.target",
                                "message": "Missing 'target'",
                            }
                        )
                    rel_type = rel.get("type")
                    if rel_type is not None and rel_type not in valid_rel_types:
                        errors.append(
                            {
                                "path": f"{rel_prefix}.type",
                                "message": f"Invalid relationship type '{rel_type}'. "
                                f"Valid: {sorted(valid_rel_types)}",
                            }
                        )

    # Check for circular broader references
    if isinstance(fields, dict):
        errors.extend(_check_circular_broader(fields))

    return errors


def _check_circular_broader(fields: dict[str, Any]) -> list[dict[str, str]]:
    """Check for circular references in broader hierarchy."""
    errors: list[dict[str, str]] = []

    # Build concept -> broader mapping
    concept_to_broader: dict[str, str | None] = {}
    for field_name, field_data in fields.items():
        if isinstance(field_data, dict):
            concept = field_data.get("concept")
            broader = field_data.get("broader")
            if concept and broader:
                concept_to_broader[concept] = broader

    # Detect cycles
    for start_concept in concept_to_broader:
        visited: set[str] = set()
        current = start_concept
        while current in concept_to_broader:
            if current in visited:
                errors.append(
                    {
                        "path": f"broader",
                        "message": f"Circular broader reference detected involving '{start_concept}'",
                    }
                )
                break
            visited.add(current)
            current = concept_to_broader[current]

    return errors


def validate_ontology_artifact(artifact: OntologyArtifact) -> list[dict[str, str]]:
    """
    Validate an OntologyArtifact instance.

    Performs the same checks as validate_ontology but on a parsed artifact.

    Args:
        artifact: Parsed OntologyArtifact

    Returns:
        List of error dicts with 'path' and 'message' keys.
        Empty list means valid.
    """
    return validate_ontology(artifact.to_dict())
