from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

from app.utils.validators import (
    validate_app_id,
    validate_container_name,
    validate_docker_digest,
    validate_docker_image,
    validate_docker_tag,
    validate_env_name,
    validate_package_name,
    validate_port_mapping,
    validate_restart_policy,
    validate_service_name,
    validate_sha256,
    validate_volume_mapping,
    validate_version,
)


TaskType = Literal["install", "update", "remove"]
TaskStatus = Literal["queued", "running", "success", "failed", "cancelled"]
ComponentType = Literal["deb", "apt", "docker", "docker_compose"]


class CamelModel(BaseModel):
    model_config = ConfigDict(populate_by_name=True, extra="forbid")


class HealthResponse(CamelModel):
    status: str
    agent_version: str = Field(alias="agentVersion")
    hostname: str
    os: str
    architecture: str


class SystemInfoResponse(CamelModel):
    hostname: str
    os: str
    kernel: str
    architecture: str
    disk_free: str = Field(alias="diskFree")
    memory_total: str = Field(alias="memoryTotal")


class InstallRequest(CamelModel):
    app_id: str = Field(alias="appId")
    version: str | None = None
    app_name: str | None = Field(default=None, alias="appName")
    package_name: str | None = Field(default=None, alias="packageName")
    service_name: str | None = Field(default=None, alias="serviceName")
    download_url: str | None = Field(default=None, alias="downloadUrl")
    checksum: str | None = None
    sha256: str | None = None

    @field_validator("app_id")
    @classmethod
    def _app_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("version")
    @classmethod
    def _version(cls, value: str | None) -> str | None:
        return validate_version(value) if value else None

    @field_validator("package_name")
    @classmethod
    def _package_name(cls, value: str | None) -> str | None:
        return validate_package_name(value) if value else None

    @field_validator("service_name")
    @classmethod
    def _service_name(cls, value: str | None) -> str | None:
        return validate_service_name(value)

    @model_validator(mode="after")
    def _direct_package_fields(self) -> "InstallRequest":
        digest = self.sha256 or self.checksum
        if digest:
            normalized = validate_sha256(digest)
            self.sha256 = normalized
            self.checksum = normalized

        has_direct_package = any([self.download_url, self.package_name, self.checksum, self.sha256])
        if has_direct_package and not all([self.download_url, self.package_name, self.checksum or self.sha256]):
            raise ValueError("direct install requires packageName, downloadUrl, and checksum/sha256")
        return self


class UpdateRequest(InstallRequest):
    current_version: str | None = Field(default=None, alias="currentVersion")
    target_version: str | None = Field(default=None, alias="targetVersion")

    @model_validator(mode="after")
    def _normalize_target(self) -> "UpdateRequest":
        if self.target_version:
            self.target_version = validate_version(self.target_version)
            self.version = self.target_version
        return self


class RemoveRequest(CamelModel):
    app_id: str = Field(alias="appId")
    package_name: str | None = Field(default=None, alias="packageName")
    service_name: str | None = Field(default=None, alias="serviceName")
    purge: bool = False

    @field_validator("app_id")
    @classmethod
    def _app_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("package_name")
    @classmethod
    def _package_name(cls, value: str | None) -> str | None:
        return validate_package_name(value) if value else None

    @field_validator("service_name")
    @classmethod
    def _service_name(cls, value: str | None) -> str | None:
        return validate_service_name(value)


class ServiceRequest(CamelModel):
    service_name: str = Field(alias="serviceName")

    @field_validator("service_name")
    @classmethod
    def _service_name(cls, value: str) -> str:
        return validate_service_name(value) or value


class TaskQueuedResponse(CamelModel):
    task_id: str = Field(alias="taskId")
    status: str


class DebComponent(CamelModel):
    component_id: str = Field(alias="componentId")
    type: Literal["deb"] = "deb"
    install_order: int = Field(default=10, alias="installOrder")
    required: bool = True
    package_name: str = Field(alias="packageName")
    version: str
    download_url: str = Field(alias="downloadUrl")
    sha256: str
    service_name: str | None = Field(default=None, alias="serviceName")

    @field_validator("component_id")
    @classmethod
    def _component_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("package_name")
    @classmethod
    def _package_name(cls, value: str) -> str:
        return validate_package_name(value)

    @field_validator("version")
    @classmethod
    def _version(cls, value: str) -> str:
        return validate_version(value)

    @field_validator("sha256")
    @classmethod
    def _sha256(cls, value: str) -> str:
        return validate_sha256(value)

    @field_validator("service_name")
    @classmethod
    def _service_name(cls, value: str | None) -> str | None:
        return validate_service_name(value)


class AptComponent(CamelModel):
    component_id: str = Field(alias="componentId")
    type: Literal["apt"] = "apt"
    install_order: int = Field(default=10, alias="installOrder")
    required: bool = True
    package_name: str = Field(alias="packageName")
    version: str | None = None

    @field_validator("component_id")
    @classmethod
    def _component_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("package_name")
    @classmethod
    def _package_name(cls, value: str) -> str:
        return validate_package_name(value)

    @field_validator("version")
    @classmethod
    def _version(cls, value: str | None) -> str | None:
        return validate_version(value) if value else None


class DockerComponent(CamelModel):
    component_id: str = Field(alias="componentId")
    type: Literal["docker"] = "docker"
    install_order: int = Field(default=10, alias="installOrder")
    required: bool = True
    image: str
    tag: str | None = None
    digest: str | None = None
    container_name: str | None = Field(default=None, alias="containerName")
    restart_policy: str = Field(default="unless-stopped", alias="restartPolicy")
    ports: list[str] = Field(default_factory=list)
    volumes: list[str] = Field(default_factory=list)
    env: dict[str, str] = Field(default_factory=dict)

    @field_validator("component_id")
    @classmethod
    def _component_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("image")
    @classmethod
    def _image(cls, value: str) -> str:
        return validate_docker_image(value)

    @field_validator("tag")
    @classmethod
    def _tag(cls, value: str | None) -> str | None:
        return validate_docker_tag(value)

    @field_validator("digest")
    @classmethod
    def _digest(cls, value: str | None) -> str | None:
        return validate_docker_digest(value)

    @field_validator("restart_policy")
    @classmethod
    def _restart_policy(cls, value: str | None) -> str:
        return validate_restart_policy(value)

    @field_validator("ports")
    @classmethod
    def _ports(cls, values: list[str]) -> list[str]:
        return [validate_port_mapping(value) for value in values]

    @field_validator("volumes")
    @classmethod
    def _volumes(cls, values: list[str]) -> list[str]:
        return [validate_volume_mapping(value) for value in values]

    @field_validator("env")
    @classmethod
    def _env(cls, values: dict[str, Any]) -> dict[str, str]:
        normalized: dict[str, str] = {}
        for key, value in values.items():
            normalized[validate_env_name(str(key))] = str(value)
        return normalized

    @model_validator(mode="after")
    def _container_name(self) -> "DockerComponent":
        self.container_name = validate_container_name(self.container_name or self.component_id)
        return self


class RawComponent(CamelModel):
    component_id: str = Field(alias="componentId")
    type: ComponentType
    install_order: int = Field(default=10, alias="installOrder")
    required: bool = True
    payload: dict[str, Any] = Field(default_factory=dict)


class AppManifest(CamelModel):
    schema_version: str = Field(default="1.0", alias="schemaVersion")
    app_id: str = Field(alias="appId")
    app_name: str = Field(alias="appName")
    version: str
    open_url: str | None = Field(default=None, alias="openUrl")
    architecture: str = "amd64"
    components: list[dict[str, Any]]
    signature: str | None = None

    @field_validator("app_id")
    @classmethod
    def _app_id(cls, value: str) -> str:
        return validate_app_id(value)

    @field_validator("version")
    @classmethod
    def _version(cls, value: str) -> str:
        return validate_version(value)
