"""Google Tasks provider adapter (OAuth token mode)."""

from __future__ import annotations

from datetime import datetime, timezone
import os
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
GOOGLE_TASKS_API_BASE = "https://tasks.googleapis.com/tasks/v1"
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"


class GoogleTasksProvider:
    """Google Tasks OAuth adapter."""

    provider_id = "google_tasks"
    display_name = "Google Tasks"

    def __init__(self, *, access_token: str, refresh_token: str | None = None, task_list_id: str | None = None):
        self._access_token = (access_token or "").strip()
        self._refresh_token = (refresh_token or "").strip() or None
        self._task_list_id = (task_list_id or "").strip() or "@default"
        if not self._access_token and not self._refresh_token:
            raise ProviderNotConfiguredError("Google Tasks OAuth token is required")

    def _headers(self) -> dict[str, str]:
        if not self._access_token:
            raise ProviderNotConfiguredError("Google Tasks access token is missing")
        return {"Authorization": f"Bearer {self._access_token}"}

    def _refresh_access_token(self) -> None:
        if not self._refresh_token:
            raise ProviderConnectionError("Google refresh token not available")
        client_id = (os.environ.get("GOOGLE_OAUTH_CLIENT_ID") or "").strip()
        client_secret = (os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET") or "").strip()
        if not client_id or not client_secret:
            raise ProviderConnectionError("Google OAuth client credentials are missing in environment")

        payload = {
            "client_id": client_id,
            "client_secret": client_secret,
            "refresh_token": self._refresh_token,
            "grant_type": "refresh_token",
        }
        try:
            response = requests.post(GOOGLE_TOKEN_URL, data=payload, timeout=REQUEST_TIMEOUT_SECONDS)
        except RequestException as exc:
            raise ProviderConnectionError(f"Google token refresh failed: {exc}") from exc
        if response.status_code >= 400:
            raise ProviderConnectionError(f"Google token refresh failed ({response.status_code}): {response.text.strip()}")
        try:
            body = response.json()
        except ValueError as exc:
            raise ProviderConnectionError("Google token refresh returned invalid JSON") from exc
        token = str(body.get("access_token", "")).strip()
        if not token:
            raise ProviderConnectionError("Google token refresh did not return access_token")
        self._access_token = token

    def _request(self, method: str, path: str, *, json_payload: dict[str, Any] | None = None) -> dict[str, Any] | list[Any]:
        url = f"{GOOGLE_TASKS_API_BASE}{path}"

        def _send() -> requests.Response:
            return requests.request(
                method=method,
                url=url,
                headers=self._headers(),
                json=json_payload,
                timeout=REQUEST_TIMEOUT_SECONDS,
            )

        try:
            response = _send()
        except RequestException as exc:
            raise ProviderConnectionError(f"Google Tasks request failed: {exc}") from exc

        if response.status_code == 401 and self._refresh_token:
            self._refresh_access_token()
            try:
                response = _send()
            except RequestException as exc:
                raise ProviderConnectionError(f"Google Tasks retry failed: {exc}") from exc

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

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

    @staticmethod
    def _to_task(item: dict[str, Any]) -> UnifiedTask:
        due_raw = str(item.get("due", "")).strip()
        due_date = due_raw[:10] if due_raw else None
        status = str(item.get("status", "")).strip().lower()
        completed = status == "completed"
        return UnifiedTask(
            provider="google_tasks",
            id=str(item.get("id", "")),
            title=str(item.get("title", "")).strip() or "Untitled task",
            description=str(item.get("notes", "")).strip() or None,
            due_date=due_date,
            due_string=due_date,
            completed=completed,
            raw={"status": item.get("status"), "updated": item.get("updated")},
        )

    def list_tasks(self, *, include_completed: bool = False) -> list[UnifiedTask]:
        show_completed = "true" if include_completed else "false"
        payload = self._request(
            "GET",
            f"/lists/{self._task_list_id}/tasks?showCompleted={show_completed}&showHidden={show_completed}&maxResults=100",
        )
        items = payload.get("items") if isinstance(payload, dict) else None
        if not isinstance(items, list):
            return []
        tasks = [self._to_task(item) for item in items if isinstance(item, dict)]
        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:
        payload: dict[str, Any] = {"title": content}
        if description:
            payload["notes"] = description
        if due_string:
            due = due_string
            if "T" not in due:
                due = f"{due}T00:00:00.000Z"
            payload["due"] = due
        task = self._request("POST", f"/lists/{self._task_list_id}/tasks", json_payload=payload)
        if not isinstance(task, dict):
            raise ProviderConnectionError("Unexpected Google Tasks response")
        return self._to_task(task)

    def complete_task(self, *, task_id: str) -> None:
        _ = self._request(
            "PATCH",
            f"/lists/{self._task_list_id}/tasks/{task_id}",
            json_payload={"status": "completed"},
        )

    def update_due(self, *, task_id: str, due_string: str) -> UnifiedTask | None:
        due = due_string
        if "T" not in due:
            due = f"{due}T00:00:00.000Z"
        task = self._request("PATCH", f"/lists/{self._task_list_id}/tasks/{task_id}", json_payload={"due": due})
        if not isinstance(task, dict):
            return None
        return self._to_task(task)

    def test_connection(self) -> ProviderConnectionResult:
        try:
            payload = self._request("GET", "/users/@me/lists")
        except Exception as exc:
            return ProviderConnectionResult(ok=False, detail=str(exc))
        items = payload.get("items") if isinstance(payload, dict) else None
        count = len(items) if isinstance(items, list) else 0
        return ProviderConnectionResult(ok=True, detail=f"Google Tasks connected ({count} list(s)) at {datetime.now(timezone.utc).isoformat()}")
