"""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 __future__ import annotations

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) -> JSONSchema:
        """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",
]
