import asyncio
import logging
import time
from abc import ABC, abstractmethod

from pydantic import BaseModel

logger = logging.getLogger(__name__)


class OtpItem(BaseModel):
    user_id: int
    verify_code: str
    expire_time: int


class OtpMemoryStore(ABC):
    def __init__(self, type_name: str) -> None:
        self._store: dict[str, OtpItem] = {}

        # 定时清理过期验证码的异步任务
        self._clean_task: asyncio.Task[None] | None = None

        self.type_name = type_name

    def start(self) -> None:
        self._clean_task = asyncio.create_task(self._cleanup())

    def _check(self, user_id: int, address: str) -> bool:
        if address in self._store:
            return False

        return all(item.user_id != user_id for item in self._store.values())

    def _put(self, user_id: int, address: str, verify_code: str) -> None:
        self._store[address] = OtpItem(
            user_id=user_id,
            verify_code=verify_code,
            expire_time=int(time.time()) + 300,
        )

    async def _cleanup(self) -> None:
        while True:
            await asyncio.sleep(60)
            logger.debug(f"[{self.type_name}] 开始定时清理过期验证码。")
            expires = []
            count = 0
            for address, item in self._store.items():
                if item.expire_time < time.time():
                    logger.debug(f"[{self.type_name}] 移除过期的 {address}")
                    expires.append(address)
                    count += 1
            for address in expires:
                self._store.pop(address)
            logger.debug(f"[{self.type_name}] 清理完成，清理了 {count} 个过期验证码。")

    def verify(self, address: str, user_id: int, verify_code: str) -> bool:
        item = self._store.get(address)
        if item is None:
            return False
        if item.expire_time < time.time():
            self._store.pop(address)  # 如果超时，顺手删掉
            return False
        if item.user_id != user_id:
            return False
        if item.verify_code != verify_code:
            return False
        # 验证通过，也要删除
        self._store.pop(address)
        return True

    @abstractmethod
    async def generate_and_send(self, user_id: int, address: str, email_reason: str) -> bool:
        """
        在此实现验证码发送，以及调用 self._check(user_id, address) 检查、 self._put(user_id, address, verify_code) 存储验证码。
        """
        ...
