"""Tool type request/response schemas."""

import uuid

from datetime import datetime

from pydantic import BaseModel, ConfigDict, field_validator, model_validator

from src.api.tool.tool_types_validation import (
    check_port_exposed,
    validate_compose_yaml,
    validate_required_variables,
)


class ToolTypeCreate(BaseModel):
    name: str
    display_name: str
    description: str | None = None
    default_port: int = 0
    definition_type: str = "compose"
    manifest_id: uuid.UUID | None = None
    compose_template: str | None = None
    dockerfile_template: str | None = None
    build_context: dict | None = None
    readiness_probe: dict | None = None
    startup_command: str | None = None
    required_variables: list[str] = []
    category: str = "other"
    interface_type: str = "web"
    requires_port: bool = True

    @field_validator("definition_type")
    @classmethod
    def validate_definition_type(cls, v: str) -> str:
        if v not in ("compose", "dockerfile", "manifest"):
            raise ValueError(
                "definition_type must be 'compose', 'dockerfile', or 'manifest'"
            )
        return v

    @field_validator("compose_template")
    @classmethod
    def validate_compose_template(cls, v: str | None, info) -> str | None:
        data = info.data
        if data.get("definition_type") != "compose":
            return v

        if v is None or not v.strip():
            raise ValueError(
                "compose_template is required when definition_type is 'compose'"
            )

        validate_compose_yaml(v)
        return v

    @field_validator("dockerfile_template")
    @classmethod
    def validate_dockerfile_template(cls, v: str | None, info) -> str | None:
        data = info.data
        if data.get("definition_type") != "dockerfile":
            return v

        if v is None or not v.strip():
            raise ValueError(
                "dockerfile_template is required when definition_type is 'dockerfile'"
            )

        if not v.strip().startswith("FROM"):
            raise ValueError("Dockerfile must start with a FROM instruction")

        return v

    @field_validator("interface_type")
    @classmethod
    def validate_interface_type(cls, v: str) -> str:
        if v not in ("web", "terminal"):
            raise ValueError("interface_type must be 'web' or 'terminal'")
        return v

    @field_validator("default_port")
    @classmethod
    def validate_default_port(cls, v: int, info) -> int:
        data = info.data
        requires_port = data.get("requires_port")
        if requires_port is False:
            return v
        if v <= 0 or v > 65535:
            raise ValueError("Port must be between 1 and 65535")
        return v

    @field_validator("required_variables")
    @classmethod
    def validate_required_variables(cls, v: list[str], info) -> list[str]:
        if not v:
            return v

        data = info.data
        if data.get("definition_type") != "compose":
            return v

        template = data.get("compose_template")
        if not template:
            return v

        validate_required_variables(template, v)
        return v

    @model_validator(mode="after")
    def validate_templates(self) -> "ToolTypeCreate":
        if self.definition_type == "manifest":
            if self.manifest_id is None:
                raise ValueError(
                    "manifest_id is required when definition_type is 'manifest'"
                )
            return self

        if self.definition_type == "dockerfile" and (
            self.dockerfile_template is None or not self.dockerfile_template.strip()
        ):
            raise ValueError(
                "dockerfile_template is required when definition_type is 'dockerfile'"
            )
        if self.definition_type == "compose" and (
            self.compose_template is None or not self.compose_template.strip()
        ):
            raise ValueError(
                "compose_template is required when definition_type is 'compose'"
            )

        if (
            self.requires_port
            and self.definition_type == "compose"
            and self.compose_template
        ):
            try:
                parsed = validate_compose_yaml(self.compose_template)
            except ValueError:
                return self

            if not check_port_exposed(parsed, self.default_port):
                raise ValueError(
                    f"default_port {self.default_port} is not exposed in the compose template. Add it to the 'ports' section."
                )

        return self


class ToolTypeUpdate(BaseModel):
    display_name: str | None = None
    description: str | None = None
    default_port: int | None = None
    definition_type: str | None = None
    manifest_id: uuid.UUID | None = None
    compose_template: str | None = None
    dockerfile_template: str | None = None
    build_context: dict | None = None
    readiness_probe: dict | None = None
    startup_command: str | None = None
    required_variables: list[str] | None = None
    category: str | None = None
    interface_type: str | None = None
    requires_port: bool | None = None

    @field_validator("definition_type")
    @classmethod
    def validate_definition_type(cls, v: str | None) -> str | None:
        if v is None:
            return v
        if v not in ("compose", "dockerfile", "manifest"):
            raise ValueError(
                "definition_type must be 'compose', 'dockerfile', or 'manifest'"
            )
        return v

    @field_validator("interface_type")
    @classmethod
    def validate_interface_type(cls, v: str | None) -> str | None:
        if v is None:
            return v
        if v not in ("web", "terminal"):
            raise ValueError("interface_type must be 'web' or 'terminal'")
        return v

    @field_validator("compose_template")
    @classmethod
    def validate_compose_template(cls, v: str | None, info) -> str | None:
        if v is None:
            return v
        validate_compose_yaml(v)
        return v

    @field_validator("dockerfile_template")
    @classmethod
    def validate_dockerfile_template(cls, v: str | None, info) -> str | None:
        if v is None:
            return v
        if not v.strip().startswith("FROM"):
            raise ValueError("Dockerfile must start with a FROM instruction")
        return v


class ToolTypeResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    name: str
    display_name: str
    description: str | None
    category: str
    interface_type: str
    requires_port: bool
    default_port: int
    definition_type: str
    manifest_id: uuid.UUID | None
    compose_template: str | None
    dockerfile_template: str | None
    build_context: dict | None
    readiness_probe: dict | None
    startup_command: str | None
    required_variables: list[str]
    created_by_id: uuid.UUID | None
    created_at: datetime
    updated_at: datetime


class ToolTypeValidateRequest(BaseModel):
    definition_type: str
    compose_template: str | None = None
    dockerfile_template: str | None = None
