from dataclasses import dataclass
from datetime import UTC, datetime
from email.utils import parsedate_to_datetime
from pathlib import Path
from typing import Any, cast

from googleapiclient.discovery import build

from home_agent.auth.google_auth import GoogleAuthManager
from home_agent.auth.token_store import EncryptedTokenStore
from home_agent.contracts.protocols import MailReader
from home_agent.contracts.types import Item, ItemKind
from home_agent.plugins_builtin._google_api_types import GmailService

GMAIL_SCOPES = ("https://www.googleapis.com/auth/gmail.readonly",)


@dataclass
class GmailReader:
    service: GmailService

    def list_recent(self) -> list[Item]:
        raw = self.service.users().messages().list(userId="me", maxResults=25).execute()
        messages = raw.get("messages", [])
        if not isinstance(messages, list):
            return []
        items: list[Item] = []
        for entry in messages:
            if not isinstance(entry, dict):
                continue
            message_id = entry.get("id")
            if not isinstance(message_id, str):
                continue
            item = self.get_message(message_id)
            if item is not None:
                items.append(item)
        return items

    def search(self, query: str) -> list[Item]:
        raw = self.service.users().messages().list(userId="me", q=query, maxResults=25).execute()
        messages = raw.get("messages", [])
        if not isinstance(messages, list):
            return []
        items: list[Item] = []
        for entry in messages:
            if not isinstance(entry, dict):
                continue
            message_id = entry.get("id")
            if not isinstance(message_id, str):
                continue
            item = self.get_message(message_id)
            if item is not None:
                items.append(item)
        return items

    def get_message(self, external_id: str) -> Item | None:
        raw = (
            self.service.users()
            .messages()
            .get(userId="me", id=external_id, format="metadata")
            .execute()
        )
        headers = _extract_headers(raw)
        subject = headers.get("subject", "(no subject)")
        sender = headers.get("from", "unknown")
        date_value = _parse_mail_datetime(headers.get("date"))
        return Item(
            provider="gmail",
            external_id=external_id,
            kind=ItemKind.EMAIL,
            received_at=date_value,
            subject=subject,
            sender=sender,
            metadata={"snippet": str(raw.get("snippet", ""))},
        )


class GmailPlugin:
    id: str = "gmail"
    version: str = "0.2.0"

    def __init__(self) -> None:
        self._store: EncryptedTokenStore = EncryptedTokenStore(Path(".data/tokens"))

    def init_auth(self) -> None:
        manager = GoogleAuthManager(self._store, token_name="google")
        manager.init_desktop_auth(GMAIL_SCOPES)

    def get_mail_reader(self) -> MailReader | None:
        manager = GoogleAuthManager(self._store, token_name="google")
        creds = manager.get_credentials(GMAIL_SCOPES)
        resource = build("gmail", "v1", credentials=creds, cache_discovery=False)
        service = cast("GmailService", cast("object", resource))
        return GmailReader(service=service)

    def get_calendar_reader(self) -> None:
        return None


def _extract_headers(raw: dict[str, Any]) -> dict[str, str]:
    payload = raw.get("payload")
    if not isinstance(payload, dict):
        return {}
    headers = payload.get("headers")
    if not isinstance(headers, list):
        return {}

    out: dict[str, str] = {}
    for header in headers:
        if not isinstance(header, dict):
            continue
        name = header.get("name")
        value = header.get("value")
        if isinstance(name, str) and isinstance(value, str):
            out[name.lower()] = value
    return out


def _parse_mail_datetime(value: str | None) -> datetime:
    if value is None:
        return datetime.now(tz=UTC)
    try:
        parsed = parsedate_to_datetime(value)
        if parsed.tzinfo is None:
            return parsed.replace(tzinfo=UTC)
        return parsed.astimezone(UTC)
    except (TypeError, ValueError):
        return datetime.now(tz=UTC)
