"""ConfigPig API client."""

from __future__ import annotations

import os
from typing import Any

from . import _http
from .types import (
    APIResponse,
    Config,
    ConfigLabel,
    ConfigSummary,
    ConfigVersion,
    Export,
    FetchResult,
    HealthCheck,
    LabelInfo,
    TeamLabel,
)


class Client:
    """Synchronous client for the ConfigPig API.

    Configuration via constructor args or environment variables:
        CONFIGPIG_API_KEY  — required API key
        CONFIGPIG_URL      — base URL (default: https://configpig.com)
    """

    def __init__(
        self,
        api_key: str | None = None,
        base_url: str | None = None,
    ):
        self.api_key = api_key or os.environ.get("CONFIGPIG_API_KEY", "")
        self.base_url = (
            base_url or os.environ.get("CONFIGPIG_URL", "https://configpig.com")
        ).rstrip("/")

    def _headers(self) -> dict[str, str]:
        h = {"Content-Type": "application/json"}
        if self.api_key:
            h["Authorization"] = f"Bearer {self.api_key}"
        return h

    def _url(self, path: str) -> str:
        return f"{self.base_url}/api/v1{path}"

    def _request(
        self,
        method: str,
        path: str,
        *,
        json_body: dict | None = None,
        params: dict | None = None,
    ) -> tuple[int, dict | None]:
        return _http.request(
            method,
            self._url(path),
            headers=self._headers(),
            json_body=json_body,
            params=params,
        )

    @staticmethod
    def _error(status_code: int, data: dict | None) -> str:
        if data and "detail" in data:
            return data["detail"]
        return f"HTTP {status_code}"

    # ── Configs ──

    def list_configs(
        self, *, tag: str | None = None, search: str | None = None
    ) -> APIResponse[list[ConfigSummary]]:
        params: dict[str, str] = {}
        if tag:
            params["tag"] = tag
        if search:
            params["search"] = search
        status, data = self._request("GET", "/configs/", params=params or None)
        if status == 200 and isinstance(data, list):
            return APIResponse(ok=True, status_code=status, data=[
                ConfigSummary(**item) for item in data
            ])
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def get_config(self, slug: str) -> APIResponse[Config]:
        status, data = self._request("GET", f"/configs/{slug}")
        if status == 200 and data:
            data["labels"] = [LabelInfo(**lb) for lb in data.get("labels", [])]
            return APIResponse(ok=True, status_code=status, data=Config(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def create_config(
        self,
        *,
        name: str,
        slug: str,
        content: str,
        description: str = "",
        format: str = "json",
        tags: list[str] | None = None,
        change_note: str = "Initial version",
    ) -> APIResponse[Config]:
        body: dict[str, Any] = {
            "name": name,
            "slug": slug,
            "content": content,
            "description": description,
            "format": format,
            "tags": tags or [],
            "change_note": change_note,
        }

        status, data = self._request("POST", "/configs/", json_body=body)
        if status == 201 and data:
            data["labels"] = [LabelInfo(**lb) for lb in data.get("labels", [])]
            return APIResponse(ok=True, status_code=status, data=Config(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def update_config(
        self,
        slug: str,
        *,
        name: str | None = None,
        description: str | None = None,
        tags: list[str] | None = None,
    ) -> APIResponse[Config]:
        body: dict[str, Any] = {}
        if name is not None:
            body["name"] = name
        if description is not None:
            body["description"] = description
        if tags is not None:
            body["tags"] = tags

        status, data = self._request("PUT", f"/configs/{slug}", json_body=body)
        if status == 200 and data:
            data["labels"] = [LabelInfo(**lb) for lb in data.get("labels", [])]
            return APIResponse(ok=True, status_code=status, data=Config(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def delete_config(self, slug: str) -> APIResponse[None]:
        status, data = self._request("DELETE", f"/configs/{slug}")
        if status == 204:
            return APIResponse(ok=True, status_code=status, data=None)
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── Versions ──

    def list_versions(self, slug: str) -> APIResponse[list[ConfigVersion]]:
        status, data = self._request("GET", f"/configs/{slug}/versions")
        if status == 200 and isinstance(data, list):
            return APIResponse(ok=True, status_code=status, data=[
                ConfigVersion(**v) for v in data
            ])
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def get_version(self, slug: str, version_number: int) -> APIResponse[ConfigVersion]:
        status, data = self._request("GET", f"/configs/{slug}/versions/{version_number}")
        if status == 200 and data:
            return APIResponse(ok=True, status_code=status, data=ConfigVersion(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def create_version(
        self,
        slug: str,
        *,
        content: str,
        change_note: str = "",
    ) -> APIResponse[ConfigVersion]:
        body = {"content": content, "change_note": change_note}
        status, data = self._request("POST", f"/configs/{slug}/versions", json_body=body)
        if status == 201 and data:
            return APIResponse(ok=True, status_code=status, data=ConfigVersion(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── Fetch ──

    def fetch(
        self,
        slug: str,
        *,
        label: str = "latest",
        output_format: str | None = None,
    ) -> APIResponse[FetchResult]:
        params: dict[str, str] = {"label": label}
        if output_format:
            params["output_format"] = output_format
        status, data = self._request("GET", f"/configs/{slug}/fetch", params=params)
        if status == 200 and data:
            return APIResponse(ok=True, status_code=status, data=FetchResult(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── Promote / Rollback ──

    def promote(
        self, slug: str, *, version: int, label: str
    ) -> APIResponse[Config]:
        body = {"version": version, "label": label}
        status, data = self._request("POST", f"/configs/{slug}/promote", json_body=body)
        if status == 200 and data:
            data["labels"] = [LabelInfo(**lb) for lb in data.get("labels", [])]
            return APIResponse(ok=True, status_code=status, data=Config(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def rollback(self, slug: str, *, label: str) -> APIResponse[Config]:
        body = {"label": label}
        status, data = self._request("POST", f"/configs/{slug}/rollback", json_body=body)
        if status == 200 and data:
            data["labels"] = [LabelInfo(**lb) for lb in data.get("labels", [])]
            return APIResponse(ok=True, status_code=status, data=Config(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── Labels ──

    def list_labels(self) -> APIResponse[list[TeamLabel]]:
        status, data = self._request("GET", "/labels/")
        if status == 200 and isinstance(data, list):
            return APIResponse(ok=True, status_code=status, data=[
                TeamLabel(**lb) for lb in data
            ])
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def create_label(
        self,
        *,
        name: str,
        is_protected: bool = False,
        description: str = "",
    ) -> APIResponse[TeamLabel]:
        body = {"name": name, "is_protected": is_protected, "description": description}
        status, data = self._request("POST", "/labels/", json_body=body)
        if status == 201 and data:
            return APIResponse(ok=True, status_code=status, data=TeamLabel(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def delete_label(self, name: str) -> APIResponse[None]:
        status, data = self._request("DELETE", f"/labels/{name}")
        if status == 204:
            return APIResponse(ok=True, status_code=status, data=None)
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def list_config_labels(self, slug: str) -> APIResponse[list[ConfigLabel]]:
        status, data = self._request("GET", f"/labels/configs/{slug}")
        if status == 200 and isinstance(data, list):
            return APIResponse(ok=True, status_code=status, data=[
                ConfigLabel(**cl) for cl in data
            ])
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def assign_label(
        self, slug: str, *, label: str, version: int
    ) -> APIResponse[ConfigLabel]:
        body = {"label": label, "version": version}
        status, data = self._request("POST", f"/labels/configs/{slug}", json_body=body)
        if status == 200 and data:
            return APIResponse(ok=True, status_code=status, data=ConfigLabel(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── Exports ──

    def list_exports(self) -> APIResponse[list[Export]]:
        status, data = self._request("GET", "/exports/")
        if status == 200 and isinstance(data, list):
            return APIResponse(ok=True, status_code=status, data=[
                Export(**e) for e in data
            ])
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def create_export(
        self, *, name: str, description: str = ""
    ) -> APIResponse[Export]:
        body = {"name": name, "description": description}
        status, data = self._request("POST", "/exports/", json_body=body)
        if status == 201 and data:
            return APIResponse(ok=True, status_code=status, data=Export(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def get_export(self, export_id: str) -> APIResponse[Export]:
        status, data = self._request("GET", f"/exports/{export_id}")
        if status == 200 and data:
            return APIResponse(ok=True, status_code=status, data=Export(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    def delete_export(self, export_id: str) -> APIResponse[None]:
        status, data = self._request("DELETE", f"/exports/{export_id}")
        if status == 204:
            return APIResponse(ok=True, status_code=status, data=None)
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))

    # ── System ──

    def health_check(self) -> APIResponse[HealthCheck]:
        status, data = self._request("GET", "/health")
        if status == 200 and data:
            return APIResponse(ok=True, status_code=status, data=HealthCheck(**data))
        return APIResponse(ok=False, status_code=status, error=self._error(status, data))
