from __future__ import annotations

from dataclasses import asdict
from typing import Any, Dict, List, Optional, TYPE_CHECKING

from ..types import (
    CreateSchemaParams,
    UpdateSchemaParams,
    ListSchemasParams,
    VerticalSchema,
    SchemaValidationResult,
)

if TYPE_CHECKING:
    from ..client import Optropic


class Schemas:
    """Schema Registry operations — register and manage vertical config schemas."""

    def __init__(self, client: Optropic) -> None:
        self._client = client

    def create(self, params: CreateSchemaParams) -> VerticalSchema:
        """Register or update a vertical config schema.

        If a schema already exists for the vertical_id, it will be updated.
        """
        data = self._client._request(
            "POST",
            "/schemas",
            json={k: v for k, v in asdict(params).items() if v is not None},
        )
        return VerticalSchema(**data)

    def list(self, params: Optional[ListSchemasParams] = None) -> Dict[str, Any]:
        """List registered vertical schemas with pagination."""
        query = {k: v for k, v in asdict(params).items() if v is not None} if params else None
        return self._client._request("GET", "/schemas", params=query)

    def get(self, vertical_id: str) -> VerticalSchema:
        """Get the active schema for a specific vertical."""
        data = self._client._request("GET", f"/schemas/{vertical_id}")
        return VerticalSchema(**data)

    def update(self, vertical_id: str, params: UpdateSchemaParams) -> VerticalSchema:
        """Update an existing vertical schema."""
        data = self._client._request(
            "PATCH",
            f"/schemas/{vertical_id}",
            json={k: v for k, v in asdict(params).items() if v is not None},
        )
        return VerticalSchema(**data)

    def delete(self, vertical_id: str) -> Dict[str, Any]:
        """Deactivate a vertical schema (soft delete)."""
        return self._client._request("DELETE", f"/schemas/{vertical_id}")

    def validate(self, vertical_id: str, asset_config: Dict[str, Any]) -> SchemaValidationResult:
        """Pre-flight validation: check if asset_config matches the registered schema.

        This is a client-side convenience that fetches the schema and validates locally.
        The server also validates on asset creation.
        """
        try:
            schema = self.get(vertical_id)
        except Exception:
            # No schema registered — permissive mode
            return SchemaValidationResult(valid=True, errors=[])

        errors = []
        metadata_schema = schema.metadataSchema or {}

        for field_name, field_def in metadata_schema.items():
            if not isinstance(field_def, dict):
                continue

            value = asset_config.get(field_name)

            # Required check
            if field_def.get("required") and (value is None or value == ""):
                label = field_def.get("label", field_name)
                errors.append({
                    "field": field_name,
                    "message": f'Required field "{label}" is missing',
                })
                continue

            if value is None:
                continue

            # Type check
            expected_type = field_def.get("type", "string")
            type_valid = _check_type(value, expected_type)
            if not type_valid:
                errors.append({
                    "field": field_name,
                    "message": f'"{field_def.get("label", field_name)}" must be a {expected_type}',
                    "received": type(value).__name__,
                })

        return SchemaValidationResult(valid=len(errors) == 0, errors=errors)


def _check_type(value: Any, expected: str) -> bool:
    """Check if a value matches the expected schema type."""
    if expected == "string":
        return isinstance(value, str)
    elif expected == "number":
        return isinstance(value, (int, float)) and not isinstance(value, bool)
    elif expected == "boolean":
        return isinstance(value, bool)
    elif expected == "date":
        return isinstance(value, str)  # ISO 8601 string
    elif expected == "array":
        return isinstance(value, list)
    return True
