from __future__ import annotations

import json
import tempfile
from contextlib import contextmanager
from typing import Annotated, Iterator

from pydantic import BaseModel, Field

from .helm import (
    HelmChartValues,
    NodePool,
    _check_overrides_match,
)


class ClusterConfig(BaseModel):
    """Authoritative schema for cluster deployment configuration.

    Read from config-*.yaml (CLI) or environments.yml (dev_cli).
    All instantiations MUST be semantically valid.
    """

    class Terraform(BaseModel):
        class TerraformCloud(BaseModel):
            workspace: str
            organization: str

        class GCS(BaseModel):
            bucket: str
            prefix: str

        terraform_cloud: TerraformCloud | None = None
        gcs: GCS | None = None
        workspace: str | None = None

    class Deployment(BaseModel):
        gcp: HelmChartValues.GCP
        domain: str
        pam_domain: Annotated[str, Field(alias="pamDomain")] = ""
        container_registry: str = ""
        public_ip: str = ""
        cluster_name: str = ""
        service_account: str = ""
        image_tags: dict[str, str] = Field(default_factory=dict)
        tls_secret_name: str = ""
        acme_email: str = ""
        prodigy_pypi_index_url: str = ""
        database: HelmChartValues.Database
        nfs_pvc_name: str = ""
        infra_secret_name: str = ""
        system_node_pool_machine_type: str = ""
        system_node_pool_size: int = 1
        model_config = {"populate_by_name": True}

        @property
        def region(self) -> str:
            return self.gcp.zone[:-2]

    name: str
    terraform: Terraform
    deployment: Deployment
    node_pools: list[NodePool] = Field(default_factory=list)


class ClusterConfigOverrides(BaseModel):
    """Partial overrides for ClusterConfig — all fields Optional."""

    class Terraform(BaseModel):
        class TerraformCloud(BaseModel):
            workspace: str | None = None
            organization: str | None = None

        class GCS(BaseModel):
            bucket: str | None = None
            prefix: str | None = None

        terraform_cloud: TerraformCloud | None = None
        gcs: GCS | None = None
        workspace: str | None = None

    class Deployment(BaseModel):
        gcp: HelmChartValues.GCP | None = None
        domain: str | None = None
        pam_domain: Annotated[str | None, Field(alias="pamDomain")] = None
        container_registry: str | None = None
        public_ip: str | None = None
        cluster_name: str | None = None
        service_account: str | None = None
        image_tags: dict[str, str] | None = None
        tls_secret_name: str | None = None
        acme_email: str | None = None
        prodigy_pypi_index_url: str | None = None
        database: HelmChartValues.Database | None = None
        nfs_pvc_name: str | None = None
        infra_secret_name: str | None = None
        system_node_pool_machine_type: str | None = None
        system_node_pool_size: int | None = None
        model_config = {"populate_by_name": True}

        @property
        def region(self) -> str | None:
            return self.gcp.zone[:-2] if self.gcp else None

    name: str | None = None
    terraform: Terraform | None = None
    deployment: Deployment | None = None
    node_pools: list[NodePool] | None = None

    @contextmanager
    def write_to_temp(self) -> Iterator[str]:
        """Write override values to a temp JSON file, yielding its path."""
        data = self.model_dump(by_alias=True, exclude_none=True)
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=True) as tmp:
            tmp.write(json.dumps(data, indent=2))
            tmp.flush()
            yield tmp.name


_check_overrides_match(ClusterConfig, ClusterConfigOverrides)
