"""Async Gitea API client for connection testing and repository listing."""

from __future__ import annotations

import logging
from typing import Any

import aiohttp

_LOGGER = logging.getLogger(__name__)


class GiteaClient:
    """Async client for the Gitea REST API."""

    def __init__(
        self,
        session: aiohttp.ClientSession,
        url: str,
        api_token: str,
    ) -> None:
        self._session = session
        self._url = url.rstrip("/")
        self._api_token = api_token

    @property
    def url(self) -> str:
        return self._url

    @property
    def _headers(self) -> dict[str, str]:
        return {"Authorization": f"token {self._api_token}"}

    async def ping(self) -> bool:
        """Check connectivity via GET /api/v1/version."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/version",
                headers=self._headers,
            ) as response:
                return response.status == 200
        except aiohttp.ClientError:
            return False

    async def get_server_version(self) -> str | None:
        """Return Gitea version string, or None on failure."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/version",
                headers=self._headers,
            ) as response:
                if response.status == 200:
                    data = await response.json()
                    return data.get("version")
        except aiohttp.ClientError as err:
            _LOGGER.warning("Failed to fetch Gitea version: %s", err)
        return None

    async def get_repos(self, limit: int = 50) -> list[dict[str, Any]]:
        """List repositories accessible to the authenticated user."""
        repos: list[dict[str, Any]] = []
        page = 1
        while True:
            try:
                async with self._session.get(
                    f"{self._url}/api/v1/repos/search",
                    headers=self._headers,
                    params={"page": str(page), "limit": str(limit), "sort": "updated"},
                ) as response:
                    if response.status != 200:
                        _LOGGER.warning("Failed to fetch repos: HTTP %s", response.status)
                        break
                    data = await response.json()
                    # Gitea wraps search results in {"data": [...], "ok": true}
                    items = data.get("data", data) if isinstance(data, dict) else data
                    if not items:
                        break
                    repos.extend(items)
                    if len(items) < limit:
                        break
                    page += 1
            except aiohttp.ClientError as err:
                _LOGGER.warning("Failed to fetch repos: %s", err)
                break
        return repos


    async def get_repo(self, owner: str, repo: str) -> dict[str, Any] | None:
        """Fetch a single repository by owner/repo name."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/repos/{owner}/{repo}",
                headers=self._headers,
            ) as response:
                if response.status == 200:
                    return await response.json()
                _LOGGER.warning("Failed to fetch repo %s/%s: HTTP %s", owner, repo, response.status)
        except aiohttp.ClientError as err:
            _LOGGER.warning("Failed to fetch repo %s/%s: %s", owner, repo, err)
        return None

    async def get_repo_issues(
        self, owner: str, repo: str, state: str = "open", limit: int = 10,
    ) -> list[dict[str, Any]]:
        """Fetch issues for a repository."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/repos/{owner}/{repo}/issues",
                headers=self._headers,
                params={"type": "issues", "state": state, "limit": str(limit)},
            ) as response:
                if response.status == 200:
                    return await response.json()
                _LOGGER.warning("Failed to fetch issues for %s/%s: HTTP %s", owner, repo, response.status)
        except aiohttp.ClientError as err:
            _LOGGER.warning("Failed to fetch issues for %s/%s: %s", owner, repo, err)
        return []

    async def get_repo_pulls(
        self, owner: str, repo: str, state: str = "open", limit: int = 10,
    ) -> list[dict[str, Any]]:
        """Fetch pull requests for a repository."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/repos/{owner}/{repo}/pulls",
                headers=self._headers,
                params={"state": state, "limit": str(limit)},
            ) as response:
                if response.status == 200:
                    return await response.json()
                _LOGGER.warning("Failed to fetch PRs for %s/%s: HTTP %s", owner, repo, response.status)
        except aiohttp.ClientError as err:
            _LOGGER.warning("Failed to fetch PRs for %s/%s: %s", owner, repo, err)
        return []

    async def get_repo_commits(
        self, owner: str, repo: str, limit: int = 10,
    ) -> list[dict[str, Any]]:
        """Fetch recent commits for a repository."""
        try:
            async with self._session.get(
                f"{self._url}/api/v1/repos/{owner}/{repo}/commits",
                headers=self._headers,
                params={"limit": str(limit)},
            ) as response:
                if response.status == 200:
                    return await response.json()
                _LOGGER.warning("Failed to fetch commits for %s/%s: HTTP %s", owner, repo, response.status)
        except aiohttp.ClientError as err:
            _LOGGER.warning("Failed to fetch commits for %s/%s: %s", owner, repo, err)
        return []

    async def get_users(self, limit: int = 200) -> list[dict[str, Any]]:
        """List users known to the Gitea instance via /users/search.

        ``/users/search`` with an empty ``q`` returns all users the
        authenticated token can see, paginated. We cap at ``limit`` to avoid
        unbounded memory on large instances; the picker only needs enough to
        cover senders that may appear in webhook payloads.
        """
        users: list[dict[str, Any]] = []
        page = 1
        per_page = min(50, limit)
        while len(users) < limit:
            try:
                async with self._session.get(
                    f"{self._url}/api/v1/users/search",
                    headers=self._headers,
                    params={"page": str(page), "limit": str(per_page)},
                ) as response:
                    if response.status != 200:
                        _LOGGER.warning("Failed to fetch users: HTTP %s", response.status)
                        break
                    body = await response.json()
                    items = body.get("data", []) if isinstance(body, dict) else body
                    if not items:
                        break
                    users.extend(items)
                    if len(items) < per_page:
                        break
                    page += 1
            except aiohttp.ClientError as err:
                _LOGGER.warning("Failed to fetch users: %s", err)
                break
        return users[:limit]


class GiteaApiError(Exception):
    """Raised when a Gitea API call fails."""
