from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

import requests

from home_agent.auth.microsoft_auth import MicrosoftAuthManager
from home_agent.auth.token_store import EncryptedTokenStore
from home_agent.contracts.protocols import MailReader
from home_agent.contracts.types import Item, ItemKind

GRAPH_SCOPES = ("User.Read", "Mail.Read")
GRAPH_BASE = "https://graph.microsoft.com/v1.0"


@dataclass
class GraphMailReader:
    auth_manager: MicrosoftAuthManager

    def list_recent(self) -> list[Item]:
        payload = self._get(
            "/me/messages",
            {
                "$top": "25",
                "$select": "id,subject,from,receivedDateTime,importance,webLink",
                "$orderby": "receivedDateTime DESC",
            },
        )
        return _extract_items(payload)

    def search(self, query: str) -> list[Item]:
        payload = self._get(
            "/me/messages",
            {
                "$top": "25",
                "$search": f'"{query}"',
                "$select": "id,subject,from,receivedDateTime,importance,webLink",
            },
        )
        return _extract_items(payload)

    def get_message(self, external_id: str) -> Item | None:
        payload = self._get(
            f"/me/messages/{external_id}",
            {"$select": "id,subject,from,receivedDateTime,importance,webLink"},
        )
        return _to_item(payload)

    def _get(self, path: str, params: dict[str, str]) -> dict[str, Any]:
        token = self.auth_manager.get_access_token(GRAPH_SCOPES)
        response = requests.get(
            f"{GRAPH_BASE}{path}",
            headers={"Authorization": f"Bearer {token}"},
            params=params,
            timeout=30,
        )
        response.raise_for_status()
        body = response.json()
        if isinstance(body, dict):
            return body
        msg = "unexpected Graph response payload"
        raise RuntimeError(msg)


class GraphMailPlugin:
    id: str = "graph_mail"
    version: str = "0.2.0"

    def __init__(self) -> None:
        store = EncryptedTokenStore(Path(".data/tokens"))
        self._auth: MicrosoftAuthManager = MicrosoftAuthManager(store=store, token_name="microsoft")

    def init_auth(self) -> None:
        self._auth.init_device_code_auth(GRAPH_SCOPES)

    def get_mail_reader(self) -> MailReader | None:
        return GraphMailReader(auth_manager=self._auth)

    def get_calendar_reader(self) -> None:
        return None


def _extract_items(payload: dict[str, Any]) -> list[Item]:
    raw_items = payload.get("value", [])
    if not isinstance(raw_items, list):
        return []
    out: list[Item] = []
    for raw_item in raw_items:
        if isinstance(raw_item, dict):
            item = _to_item(raw_item)
            if item is not None:
                out.append(item)
    return out


def _to_item(raw: dict[str, Any]) -> Item | None:
    external_id = raw.get("id")
    if not isinstance(external_id, str):
        return None
    sender_obj = raw.get("from", {})
    sender = "unknown"
    if isinstance(sender_obj, dict):
        email_address = sender_obj.get("emailAddress", {})
        if isinstance(email_address, dict):
            address = email_address.get("address")
            if isinstance(address, str):
                sender = address

    received_raw = raw.get("receivedDateTime")
    received_at = _parse_received(received_raw if isinstance(received_raw, str) else None)

    subject = raw.get("subject")
    subject_str = subject if isinstance(subject, str) else "(no subject)"

    return Item(
        provider="graph_mail",
        external_id=external_id,
        kind=ItemKind.EMAIL,
        received_at=received_at,
        subject=subject_str,
        sender=sender,
        metadata={
            "importance": raw.get("importance"),
            "webLink": raw.get("webLink"),
        },
    )


def _parse_received(value: str | None) -> datetime:
    if value is None:
        return datetime.now(tz=UTC)
    normalized = value.replace("Z", "+00:00")
    try:
        parsed = datetime.fromisoformat(normalized)
        if parsed.tzinfo is None:
            return parsed.replace(tzinfo=UTC)
        return parsed.astimezone(UTC)
    except ValueError:
        return datetime.now(tz=UTC)
