"""
MarkiTect Metaschema Module for Issue #50

This module provides metaschema validation for MarkiTect JSON schemas,
extending standard JSON Schema with MarkiTect-specific features.

This is a TDD8 implementation - tests are written first, implementation follows.
"""

from pathlib import Path
from typing import Dict, Any, List, Optional
import json

# Path to the MarkiTect metaschema JSON file
MARKITECT_METASCHEMA_PATH = Path(__file__).parent / "schemas" / "markitect-metaschema.json"


class ValidationError:
    """Represents a schema validation error."""

    def __init__(self, message: str, path: str = ""):
        self.message = message
        self.path = path


class ValidationResult:
    """Result of schema validation against metaschema."""

    def __init__(self, is_valid: bool, errors: List[ValidationError] = None):
        self.is_valid = is_valid
        self.errors = errors or []


class MetaschemaValidator:
    """Validates MarkiTect schemas against the MarkiTect metaschema."""

    def __init__(self):
        """Initialize the metaschema validator."""
        self._metaschema_cache = None

    def get_metaschema(self) -> Dict[str, Any]:
        """
        Get the MarkiTect metaschema.

        Returns:
            Dictionary containing the metaschema

        Raises:
            FileNotFoundError: If metaschema file doesn't exist
            json.JSONDecodeError: If metaschema file is invalid JSON
        """
        if self._metaschema_cache is None:
            if not MARKITECT_METASCHEMA_PATH.exists():
                raise FileNotFoundError(f"Metaschema file not found: {MARKITECT_METASCHEMA_PATH}")

            with open(MARKITECT_METASCHEMA_PATH) as f:
                self._metaschema_cache = json.load(f)

        return self._metaschema_cache

    def validate_schema(self, schema: Dict[str, Any]) -> bool:
        """
        Validate a schema against the MarkiTect metaschema.

        Args:
            schema: The schema to validate

        Returns:
            True if valid, False otherwise
        """
        result = self.validate_schema_with_errors(schema)
        return result.is_valid

    def validate_schema_with_errors(self, schema: Dict[str, Any]) -> ValidationResult:
        """
        Validate a schema and return detailed error information.

        Args:
            schema: The schema to validate

        Returns:
            ValidationResult with validity status and error details
        """
        errors = []

        # Basic JSON Schema validation - check required properties
        if not isinstance(schema, dict):
            return ValidationResult(False, [ValidationError("Schema must be an object")])

        # Check for required JSON Schema properties
        if "$schema" not in schema:
            errors.append(ValidationError("Missing required $schema property"))

        if "type" not in schema:
            errors.append(ValidationError("Missing required type property"))

        # Validate MarkiTect extensions
        errors.extend(self._validate_markitect_extensions(schema))

        return ValidationResult(len(errors) == 0, errors)

    def _validate_markitect_extensions(self, schema: Dict[str, Any]) -> List[ValidationError]:
        """Validate MarkiTect-specific extensions in the schema."""
        errors = []

        # Define validation rules for MarkiTect extensions
        validation_rules = {
            "x-markitect-outline-depth": self._validate_outline_depth,
            "x-markitect-outline-mode": self._validate_outline_mode,
            "x-markitect-heading-text": self._validate_heading_text,
            "x-markitect-content-instructions": self._validate_content_instructions,
            "x-markitect-instruction-type": self._validate_instruction_type,
            "x-markitect-generation-mode": self._validate_generation_mode,
            "x-markitect-generated-from": self._validate_generated_from,
        }

        # Apply validation rules
        for property_name, validator in validation_rules.items():
            if property_name in schema:
                error = validator(schema[property_name], property_name)
                if error:
                    errors.append(error)

        # Recursively validate nested properties
        if "properties" in schema:
            for prop_name, prop_schema in schema["properties"].items():
                if isinstance(prop_schema, dict):
                    nested_errors = self._validate_markitect_extensions(prop_schema)
                    errors.extend(nested_errors)

        return errors

    def _validate_outline_depth(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-outline-depth property."""
        if not isinstance(value, int) or value < 1:
            return ValidationError(
                "x-markitect-outline-depth must be an integer >= 1",
                property_name
            )
        return None

    def _validate_outline_mode(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-outline-mode property."""
        if not isinstance(value, bool):
            return ValidationError(
                "x-markitect-outline-mode must be a boolean",
                property_name
            )
        return None

    def _validate_heading_text(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-heading-text property."""
        if not isinstance(value, str):
            return ValidationError(
                "x-markitect-heading-text must be a string",
                property_name
            )
        return None

    def _validate_content_instructions(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-content-instructions property."""
        if not isinstance(value, str):
            return ValidationError(
                "x-markitect-content-instructions must be a string",
                property_name
            )
        return None

    def _validate_instruction_type(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-instruction-type property."""
        valid_types = ["description", "example", "constraint", "template"]
        if not isinstance(value, str) or value not in valid_types:
            return ValidationError(
                f"x-markitect-instruction-type must be one of {valid_types}",
                property_name
            )
        return None

    def _validate_generation_mode(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-generation-mode property."""
        valid_modes = ["outline", "full"]
        if not isinstance(value, str) or value not in valid_modes:
            return ValidationError(
                f"x-markitect-generation-mode must be one of {valid_modes}",
                property_name
            )
        return None

    def _validate_generated_from(self, value: Any, property_name: str) -> Optional[ValidationError]:
        """Validate x-markitect-generated-from property."""
        if not isinstance(value, str):
            return ValidationError(
                "x-markitect-generated-from must be a string",
                property_name
            )
        return None