from __future__ import annotations

from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, Iterable, List, Optional, Tuple


class NotionEnrichmentSleepRepository(ABC):
    @abstractmethod
    def get_last_sleep_at(self, user_id: str, agent_id: str) -> Optional[datetime]:
        raise NotImplementedError

    @abstractmethod
    def set_last_sleep_at(self, user_id: str, agent_id: str, sleep_time: datetime) -> None:
        raise NotImplementedError

    @abstractmethod
    def list_unenriched_notions_created_after(
        self,
        user_id: str,
        agent_id: str,
        since: Optional[datetime],
        limit: Optional[int] = None,
    ) -> List[object]:
        raise NotImplementedError

    @abstractmethod
    def mark_enriched(self, user_id: str, agent_id: str, notion: object) -> None:
        raise NotImplementedError


class InMemoryNotionEnrichmentSleepRepository(NotionEnrichmentSleepRepository):
    def __init__(self):
        self._last_sleep: Dict[Tuple[str, str], datetime] = {}
        self._notions: Dict[Tuple[str, str], List[object]] = {}
        self._enriched_ids: set[str] = set()

    def get_last_sleep_at(self, user_id: str, agent_id: str) -> Optional[datetime]:
        return self._last_sleep.get((user_id, agent_id))

    def set_last_sleep_at(self, user_id: str, agent_id: str, sleep_time: datetime) -> None:
        self._last_sleep[(user_id, agent_id)] = sleep_time

    def list_unenriched_notions_created_after(
        self,
        user_id: str,
        agent_id: str,
        since: Optional[datetime],
        limit: Optional[int] = None,
    ) -> List[object]:
        key = (user_id, agent_id)
        notions = self._notions.get(key, [])
        filtered: List[object] = []
        for notion in notions:
            if self._is_enriched(notion):
                continue
            created_at = _get_notion_created_at(notion)
            if since is not None and created_at is not None and created_at <= since:
                continue
            filtered.append(notion)
            if limit is not None and len(filtered) >= limit:
                break
        return filtered

    def mark_enriched(self, user_id: str, agent_id: str, notion: object) -> None:
        notion_id = _get_notion_id(notion)
        if notion_id:
            self._enriched_ids.add(notion_id)

    def seed_notions(self, user_id: str, agent_id: str, notions: Iterable[object]) -> None:
        key = (user_id, agent_id)
        self._notions.setdefault(key, []).extend(list(notions))

    def _is_enriched(self, notion: object) -> bool:
        notion_id = _get_notion_id(notion)
        if notion_id and notion_id in self._enriched_ids:
            return True
        try:
            return bool(notion.is_enriched)
        except AttributeError:
            return False


def _get_notion_id(notion: object) -> Optional[str]:
    if notion is None:
        return None
    try:
        notion_id = notion.id
    except AttributeError:
        return None
    if notion_id is None:
        return None
    return str(notion_id)


def _get_notion_created_at(notion: object) -> Optional[datetime]:
    if notion is None:
        return None
    try:
        value = notion.created_at
        if value is not None:
            return value
    except AttributeError:
        pass
    try:
        value = notion.createdAt
        if value is not None:
            return value
    except AttributeError:
        pass
    try:
        value = notion.created_on
        if value is not None:
            return value
    except AttributeError:
        pass
    try:
        value = notion.createdOn
        if value is not None:
            return value
    except AttributeError:
        pass
    return None
