from __future__ import annotations

from datetime import UTC, datetime

from fastapi import HTTPException, status
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.security import create_access_token
from app.models.user import User
from app.services.captcha_service import CaptchaService
from app.services.sms_code_service import SmsCodeService


class AuthService:
    _revoked_token_jti: set[str] = set()

    @classmethod
    def send_sms_code(cls, db: Session, phone: str, captcha_id: str, captcha_code: str) -> None:
        CaptchaService.verify(captcha_id, captcha_code)
        user = cls._get_existing_user(db, phone)
        cls._ensure_user_can_login(user)
        SmsCodeService.send_code(db, phone)

    @classmethod
    def login_with_sms(cls, db: Session, phone: str, code: str) -> dict:
        SmsCodeService.verify_code(db, phone, code)
        user = cls._get_existing_user(db, phone)
        cls._ensure_user_can_login(user)

        now = datetime.now(UTC).replace(tzinfo=None)
        user.last_login_at = now
        db.add(user)
        db.commit()
        db.refresh(user)

        token, expired_at = create_access_token(str(user.id), "user")
        return {"token": token, "expiredAt": expired_at, "user": user}

    @classmethod
    def logout(cls, token_payload: dict) -> None:
        jti = token_payload.get("jti")
        if jti:
            cls._revoked_token_jti.add(str(jti))

    @classmethod
    def is_token_revoked(cls, token_payload: dict) -> bool:
        jti = token_payload.get("jti")
        return bool(jti and str(jti) in cls._revoked_token_jti)

    @classmethod
    def _get_existing_user(cls, db: Session, phone: str) -> User:
        user = db.scalar(select(User).where(User.phone == phone, User.is_deleted == 0))
        if user is not None:
            return user

        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="手机号不在学员名单中，请联系管理员")

    @staticmethod
    def _ensure_user_can_login(user: User) -> None:
        now = datetime.now(UTC).replace(tzinfo=None)
        if user.status != 1:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号已禁用")
        if user.effective_at and user.effective_at > now:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号尚未生效")
        if user.expired_at and user.expired_at < now:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号已过期")
