"""Trello task provider adapter."""

from __future__ import annotations

from typing import Any

import requests
from requests.exceptions import RequestException

from app.services.integrations.errors import ProviderConnectionError, ProviderNotConfiguredError
from app.services.integrations.types import ProviderConnectionResult, UnifiedTask

REQUEST_TIMEOUT_SECONDS = 20
TRELLO_API_BASE = "https://api.trello.com/1"


class TrelloTaskProvider:
    """Token/key-based Trello adapter."""

    provider_id = "trello"
    display_name = "Trello"

    def __init__(self, *, api_key: str, token: str, board_id: str | None = None, list_id: str | None = None, done_list_id: str | None = None):
        self._api_key = (api_key or "").strip()
        self._token = (token or "").strip()
        self._board_id = (board_id or "").strip() or None
        self._list_id = (list_id or "").strip() or None
        self._done_list_id = (done_list_id or "").strip() or None
        if not self._api_key or not self._token:
            raise ProviderNotConfiguredError("Trello API key and token are required")
        if not self._board_id and not self._list_id:
            raise ProviderNotConfiguredError("Trello requires board_id or list_id")

    def _params(self, extra: dict[str, Any] | None = None) -> dict[str, Any]:
        base = {"key": self._api_key, "token": self._token}
        if extra:
            base.update(extra)
        return base

    def _request(self, method: str, path: str, *, params: dict[str, Any] | None = None) -> dict[str, Any] | list[Any]:
        url = f"{TRELLO_API_BASE}{path}"
        try:
            response = requests.request(
                method=method,
                url=url,
                params=self._params(params),
                timeout=REQUEST_TIMEOUT_SECONDS,
            )
        except RequestException as exc:
            raise ProviderConnectionError(f"Trello request failed: {exc}") from exc

        if response.status_code >= 400:
            detail = response.text.strip() or "Trello API request failed"
            raise ProviderConnectionError(f"Trello API error ({response.status_code}): {detail}")

        try:
            return response.json()
        except ValueError as exc:
            raise ProviderConnectionError("Trello API returned invalid JSON") from exc

    @staticmethod
    def _card_to_task(card: dict[str, Any], *, done_list_id: str | None = None) -> UnifiedTask:
        due_date = str(card.get("due", "")).strip() or None
        is_closed = bool(card.get("closed"))
        in_done_list = bool(done_list_id) and str(card.get("idList", "")).strip() == done_list_id
        return UnifiedTask(
            provider="trello",
            id=str(card.get("id", "")),
            title=str(card.get("name", "")).strip() or "Untitled card",
            description=str(card.get("desc", "")).strip() or None,
            due_date=due_date,
            due_string=due_date,
            completed=is_closed or in_done_list,
            raw={"url": card.get("url"), "idList": card.get("idList"), "closed": card.get("closed")},
        )

    def _list_cards(self, *, include_completed: bool) -> list[dict[str, Any]]:
        if self._list_id:
            filter_value = "all" if include_completed else "open"
            payload = self._request(
                "GET",
                f"/lists/{self._list_id}/cards",
                params={"fields": "name,desc,due,url,idList,closed", "filter": filter_value},
            )
        else:
            path = f"/boards/{self._board_id}/cards" if include_completed else f"/boards/{self._board_id}/cards/open"
            payload = self._request(
                "GET",
                path,
                params={"fields": "name,desc,due,url,idList,closed"},
            )
        if not isinstance(payload, list):
            return []
        return [item for item in payload if isinstance(item, dict)]

    def list_tasks(self, *, include_completed: bool = False) -> list[UnifiedTask]:
        tasks = [self._card_to_task(card, done_list_id=self._done_list_id) for card in self._list_cards(include_completed=include_completed)]
        if include_completed:
            return tasks
        return [item for item in tasks if not item.completed]

    def create_task(self, *, content: str, description: str | None = None, due_string: str | None = None) -> UnifiedTask:
        target_list = self._list_id
        if not target_list and self._board_id:
            payload = self._request("GET", f"/boards/{self._board_id}/lists/open", params={"fields": "name"})
            if isinstance(payload, list) and payload:
                first = payload[0]
                if isinstance(first, dict):
                    target_list = str(first.get("id", "")).strip() or None
        if not target_list:
            raise ProviderNotConfiguredError("Trello list_id is required to create tasks")

        params: dict[str, Any] = {
            "idList": target_list,
            "name": content,
            "desc": description or "",
        }
        if due_string:
            params["due"] = due_string
        card = self._request("POST", "/cards", params=params)
        if not isinstance(card, dict):
            raise ProviderConnectionError("Unexpected Trello card response")
        return self._card_to_task(card, done_list_id=self._done_list_id)

    def complete_task(self, *, task_id: str) -> None:
        if self._done_list_id:
            _ = self._request("PUT", f"/cards/{task_id}", params={"idList": self._done_list_id})
            return
        _ = self._request("PUT", f"/cards/{task_id}/closed", params={"value": "true"})

    def update_due(self, *, task_id: str, due_string: str) -> UnifiedTask | None:
        payload = self._request("PUT", f"/cards/{task_id}", params={"due": due_string, "fields": "name,desc,due,url,idList,closed"})
        if not isinstance(payload, dict):
            return None
        return self._card_to_task(payload, done_list_id=self._done_list_id)

    def test_connection(self) -> ProviderConnectionResult:
        try:
            payload = self._request("GET", "/members/me", params={"fields": "fullName,username"})
        except Exception as exc:
            return ProviderConnectionResult(ok=False, detail=str(exc))
        if isinstance(payload, dict):
            who = payload.get("fullName") or payload.get("username") or "member"
            return ProviderConnectionResult(ok=True, detail=f"Connected as {who}")
        return ProviderConnectionResult(ok=False, detail="Trello test returned unexpected payload")
