"""Apps resource — the canonical pattern for resource classes."""

from __future__ import annotations

import builtins
import time

from .._client import AplikuClient
from .._models import (
    AdvancedLogsResponse,
    App,
    AppCreate,
    AppLogsRequest,
    AppUpdate,
    Deployment,
    LogsRequestResponse,
    NginxLogsRequest,
    NginxLogsResponse,
    ServiceLogsResponse,
)

_list = builtins.list


class AppsResource:
    def __init__(self, client: AplikuClient) -> None:
        self._client = client

    def _base(self, team: str) -> str:
        return f"/api/team/{team}/applications"

    def list(self, team: str) -> _list[App]:
        """List all apps in a team."""
        data = self._client.get(f"{self._base(team)}/list/")
        return [App.model_validate(item) for item in data]

    def get(self, team: str, app_id: int) -> App:
        """Get a single app."""
        data = self._client.get(f"{self._base(team)}/{app_id}/")
        return App.model_validate(data)

    def create(self, team: str, *, name: str, branch: str, **kwargs: object) -> App:
        """Create an app."""
        body = AppCreate(name=name, branch=branch, **kwargs)  # type: ignore[arg-type]
        data = self._client.post(
            f"{self._base(team)}/create/",
            json=body.model_dump(exclude_none=True),
        )
        return App.model_validate(data)

    def update(self, team: str, app_id: int, **kwargs: object) -> App:
        """Partially update an app."""
        body = AppUpdate(**kwargs)  # type: ignore[arg-type]
        data = self._client.patch(
            f"{self._base(team)}/{app_id}/",
            json=body.model_dump(exclude_none=True),
        )
        return App.model_validate(data)

    def delete(self, team: str, app_id: int) -> None:
        """Delete an app."""
        self._client.delete(f"{self._base(team)}/{app_id}/")

    def deploy(self, team: str, app_id: int) -> Deployment:
        """Trigger a deployment."""
        data = self._client.post(f"{self._base(team)}/{app_id}/deploy")
        return Deployment.model_validate(data)

    def get_config_vars(self, team: str, app_id: int) -> dict[str, str]:
        """Get config vars for an app."""
        data = self._client.get(f"{self._base(team)}/{app_id}/config-vars")
        result: dict[str, str] = data.get("vars", {})
        return result

    def set_config_vars(self, team: str, app_id: int, vars: dict[str, str]) -> dict[str, str]:
        """Set/update config vars."""
        data = self._client.patch(
            f"{self._base(team)}/{app_id}/config-vars",
            json={"vars": vars},
        )
        result: dict[str, str] = data.get("vars", {})
        return result

    # --- Logs ---

    def service_logs(
        self, team: str, app_id: int, service: str, tail: int = 100
    ) -> ServiceLogsResponse:
        """Get logs for a specific process/service (direct GET)."""
        data = self._client.get(f"{self._base(team)}/{app_id}/service_logs/{service}/{tail}")
        return ServiceLogsResponse.model_validate(data)

    def load_balancer_logs(
        self, team: str, app_id: int, domain: str, tail: int = 100
    ) -> ServiceLogsResponse:
        """Get load balancer logs for a domain."""
        data = self._client.get(f"{self._base(team)}/{app_id}/load_balancer_logs/{domain}/{tail}")
        return ServiceLogsResponse.model_validate(data)

    def _request_app_logs(
        self, team: str, app_id: int, process: str, tail: int = 100
    ) -> LogsRequestResponse:
        """Request advanced app logs (async step 1)."""
        body = AppLogsRequest(process=process, tail=tail)
        data = self._client.post(
            f"{self._base(team)}/{app_id}/request_advanced_logs",
            json=body.model_dump(),
        )
        return LogsRequestResponse.model_validate(data)

    def _retrieve_app_logs(self, team: str, app_id: int, request_id: float) -> AdvancedLogsResponse:
        """Retrieve advanced app logs by request_id (async step 2)."""
        data = self._client.get(f"{self._base(team)}/{app_id}/retrieve_advanced_logs/{request_id}")
        return AdvancedLogsResponse.model_validate(data)

    def app_logs(
        self,
        team: str,
        app_id: int,
        process: str,
        tail: int = 100,
        poll_interval: float = 2.0,
        max_polls: int = 30,
    ) -> AdvancedLogsResponse:
        """Get advanced app logs with polling until ready."""
        req = self._request_app_logs(team, app_id, process, tail)
        for _ in range(max_polls):
            result = self._retrieve_app_logs(team, app_id, req.request_id)
            if result.status != "pending":
                return result
            time.sleep(poll_interval)
        return result  # return last result even if still pending

    def _request_nginx_logs(
        self, team: str, app_id: int, domain: str, tail: int = 100
    ) -> LogsRequestResponse:
        """Request nginx logs (async step 1)."""
        body = NginxLogsRequest(domain=domain, tail=tail)
        data = self._client.post(
            f"{self._base(team)}/{app_id}/request_nginx_logs",
            json=body.model_dump(),
        )
        return LogsRequestResponse.model_validate(data)

    def _retrieve_nginx_logs(self, team: str, app_id: int, request_id: float) -> NginxLogsResponse:
        """Retrieve nginx logs by request_id (async step 2)."""
        data = self._client.get(f"{self._base(team)}/{app_id}/retrieve_nginx_logs/{request_id}")
        return NginxLogsResponse.model_validate(data)

    def nginx_logs(
        self,
        team: str,
        app_id: int,
        domain: str,
        tail: int = 100,
        poll_interval: float = 2.0,
        max_polls: int = 30,
    ) -> NginxLogsResponse:
        """Get nginx logs with polling until ready."""
        req = self._request_nginx_logs(team, app_id, domain, tail)
        for _ in range(max_polls):
            result = self._retrieve_nginx_logs(team, app_id, req.request_id)
            if result.logs is not None:
                return result
            time.sleep(poll_interval)
        return result  # return last result even if logs still None
