"""Common typed models for pathways.

Only models with REAL typed fields belong here.
If it's just extra="allow" with Any fields, use JsonObject instead.

All fields use JSONValue (typed recursive JSON) instead of Any.
"""

from typing import Self

from pydantic import BaseModel, Field

from core.common import JsonObject, JSONValue

# =============================================================================
# JSON Schema Models — these have REAL typed fields
# =============================================================================


class JSONSchemaProperty(BaseModel):
    """A single property in a JSON Schema."""

    model_config = {"extra": "allow"}

    type: str | None = None  # "string", "number", "object", "array", "boolean", "null"
    description: str | None = None
    default: JSONValue = None
    required: bool = True
    enum: list[JSONValue] | None = None
    items: JsonObject | None = None  # For array types
    properties: dict[str, "JSONSchemaProperty"] | None = None  # For object types


class JSONSchema(BaseModel):
    """JSON Schema object - typed representation of input/output schemas."""

    model_config = {"extra": "allow"}

    type: str = "object"
    properties: dict[str, JSONSchemaProperty] = Field(default_factory=dict)
    required: list[str] = Field(default_factory=list)
    additionalProperties: bool | JsonObject = True

    @classmethod
    def from_dict(cls, data: JsonObject) -> Self:
        """Create from a plain dict."""
        if isinstance(data, cls):
            return data

        # Handle simple format: {"field": "string"} -> {"field": {"type": "string"}}
        if data and not any(k in data for k in ("type", "properties", "$schema")):
            # Simple format - convert to full schema
            properties = {}
            for key, value in data.items():
                if isinstance(value, str):
                    properties[key] = JSONSchemaProperty(type=value)
                elif isinstance(value, dict):
                    properties[key] = JSONSchemaProperty(**value)
                else:
                    properties[key] = JSONSchemaProperty(type="any")

            return cls(properties=properties)

        # Full schema format
        props = {}
        raw_props = data.get("properties")
        if isinstance(raw_props, dict):
            for key, prop_data in raw_props.items():
                if isinstance(prop_data, dict):
                    props[key] = JSONSchemaProperty(**prop_data)
                else:
                    props[key] = JSONSchemaProperty(type=str(prop_data))

        raw_required = data.get("required")
        required_list: list[str] = []
        if isinstance(raw_required, list):
            required_list = [str(r) for r in raw_required]

        raw_additional = data.get("additionalProperties", True)

        return cls(
            type=str(data.get("type", "object")),
            properties=props,
            required=required_list,
            additionalProperties=raw_additional
            if isinstance(raw_additional, (bool, dict))
            else True,
            **{
                k: v
                for k, v in data.items()
                if k not in ("type", "properties", "required", "additionalProperties")
            },
        )

    def to_dict(self) -> JsonObject:
        """Convert to plain dict (JSON Schema format)."""
        result: JsonObject = {
            "type": self.type,
            "properties": {k: v.model_dump(exclude_none=True) for k, v in self.properties.items()},
        }
        if self.required:
            result["required"] = self.required
        if self.additionalProperties is not True:
            result["additionalProperties"] = self.additionalProperties
        return result


__all__ = [
    "JSONSchema",
    "JSONSchemaProperty",
]
