"""Tests for models.py — frozen dataclasses."""

from datetime import UTC, datetime

import pytest

from kuberoku.models import (
    Addon,
    App,
    ClusterInfo,
    ConnectionTarget,
    DetachedJob,
    Domain,
    Dyno,
    ExecResult,
    ExposedEndpoint,
    LogLine,
    PortMapping,
    ProcessFormation,
    Release,
)


class TestProcessFormation:
    def test_create(self) -> None:
        pf = ProcessFormation(replicas=3, command="gunicorn app:app", command_source="manual")
        assert pf.replicas == 3
        assert pf.command == "gunicorn app:app"
        assert pf.command_source == "manual"

    def test_frozen(self) -> None:
        pf = ProcessFormation(replicas=1, command=None, command_source="default")
        with pytest.raises(AttributeError):
            pf.replicas = 5  # type: ignore[misc]


class TestApp:
    def test_create(self) -> None:
        now = datetime.now(UTC)
        app = App(
            name="myapi",
            cluster="production",
            created_at=now,
            created_by="user@host",
            process_types={},
            release_version=0,
            maintenance=False,
        )
        assert app.name == "myapi"
        assert app.maintenance is False

    def test_frozen(self) -> None:
        now = datetime.now(UTC)
        app = App(
            name="myapi",
            cluster="prod",
            created_at=now,
            created_by="u",
            process_types={},
            release_version=0,
            maintenance=False,
        )
        with pytest.raises(AttributeError):
            app.name = "other"  # type: ignore[misc]


class TestDyno:
    def test_create(self) -> None:
        dyno = Dyno(
            name="web.1",
            app="myapi",
            process_type="web",
            command=None,
            state="up",
            started_at=datetime.now(UTC),
            image="myapi:v1",
            node="node-1",
        )
        assert dyno.name == "web.1"
        assert dyno.state == "up"


class TestRelease:
    def test_create(self) -> None:
        now = datetime.now(UTC)
        release = Release(
            version=1,
            app="myapi",
            description="Deploy myapi:v1",
            images={"web": "myapi:v1"},
            formation={"web": ProcessFormation(1, None, "default")},
            commit="abc123",
            ref="main",
            created_at=now,
            created_by="user@host",
            env_diff={},
        )
        assert release.version == 1
        assert release.images["web"] == "myapi:v1"


class TestAddon:
    def test_create(self) -> None:
        addon = Addon(
            instance_name="postgres",
            addon_type="postgres",
            app="myapi",
            status="running",
            image="postgres:16",
            cpu="250m",
            memory="256Mi",
            storage="1Gi",
            env_key="DATABASE_URL",
            external=False,
            ephemeral=False,
            created_at=datetime.now(UTC),
        )
        assert addon.env_key == "DATABASE_URL"
        assert addon.storage == "1Gi"
        assert addon.external is False


class TestDomain:
    def test_create(self) -> None:
        domain = Domain(
            hostname="myapp.com",
            app="myapi",
            process_type="web",
            port=8080,
            tls=True,
            issuer="letsencrypt-prod",
            auto_generated=False,
            status="active",
            created_at=datetime.now(UTC),
        )
        assert domain.hostname == "myapp.com"
        assert domain.tls is True
        assert domain.process_type == "web"
        assert domain.port == 8080
        assert domain.issuer == "letsencrypt-prod"
        assert domain.auto_generated is False


class TestLogLine:
    def test_create(self) -> None:
        line = LogLine(
            timestamp=datetime.now(UTC),
            source="app",
            dyno="web.1",
            message="Listening on :8080",
        )
        assert line.source == "app"
        assert line.dyno == "web.1"


class TestExposedEndpoint:
    def test_create(self) -> None:
        ep = ExposedEndpoint(
            name="web",
            app="myapi",
            kind="process",
            service_type="LoadBalancer",
            method="loadbalancer",
            external_ip="1.2.3.4",
            hostname=None,
            ports=(8080,),
        )
        assert ep.name == "web"
        assert ep.kind == "process"
        assert ep.service_type == "LoadBalancer"
        assert ep.external_ip == "1.2.3.4"
        assert ep.ports == (8080,)

    def test_frozen(self) -> None:
        ep = ExposedEndpoint(
            name="postgres",
            app="myapi",
            kind="addon",
            service_type="ClusterIP",
            method="clusterip",
            external_ip=None,
            hostname=None,
            ports=(5432,),
        )
        with pytest.raises(AttributeError):
            ep.name = "other"  # type: ignore[misc]


class TestPortMapping:
    def test_create(self) -> None:
        pm = PortMapping(port=8080, protocol="TCP")
        assert pm.port == 8080
        assert pm.protocol == "TCP"

    def test_frozen(self) -> None:
        pm = PortMapping(port=8080, protocol="TCP")
        with pytest.raises(AttributeError):
            pm.port = 9090  # type: ignore[misc]


class TestConnectionTarget:
    def test_create(self) -> None:
        pm = PortMapping(port=5432, protocol="TCP")
        ct = ConnectionTarget(
            name="postgres",
            app="myapi",
            kind="addon",
            service_name="kuberoku-addon-myapi-postgres",
            namespace="kuberoku",
            ports=(pm,),
        )
        assert ct.name == "postgres"
        assert ct.kind == "addon"
        assert ct.service_name == "kuberoku-addon-myapi-postgres"
        assert ct.ports == (pm,)

    def test_frozen(self) -> None:
        ct = ConnectionTarget(
            name="web",
            app="myapi",
            kind="process",
            service_name="kuberoku-myapi-web",
            namespace="kuberoku",
            ports=(PortMapping(port=8080, protocol="TCP"),),
        )
        with pytest.raises(AttributeError):
            ct.name = "other"  # type: ignore[misc]


class TestExecResult:
    def test_create(self) -> None:
        er = ExecResult(output="hello\n", pod_name="myapp-web-abc12", process_type="web")
        assert er.output == "hello\n"
        assert er.pod_name == "myapp-web-abc12"
        assert er.process_type == "web"

    def test_frozen(self) -> None:
        er = ExecResult(output="", pod_name="p", process_type="web")
        with pytest.raises(AttributeError):
            er.output = "x"  # type: ignore[misc]


class TestDetachedJob:
    def test_create(self) -> None:
        dj = DetachedJob(
            job_name="kuberoku-run-myapp-abc", app="myapp", command=["bash", "-c", "echo hi"]
        )
        assert dj.job_name == "kuberoku-run-myapp-abc"
        assert dj.app == "myapp"
        assert dj.command == ["bash", "-c", "echo hi"]

    def test_frozen(self) -> None:
        dj = DetachedJob(job_name="j", app="a", command=["ls"])
        with pytest.raises(AttributeError):
            dj.app = "other"  # type: ignore[misc]


class TestClusterInfo:
    def test_create(self) -> None:
        info = ClusterInfo(
            name="production",
            context="prod-eks",
            is_current=True,
            k8s_version="1.35.0",
            node_count=3,
            app_count=5,
        )
        assert info.is_current is True
        assert info.k8s_version == "1.35.0"
