#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
API Token Service
"""
import hashlib
import secrets
from datetime import datetime, timezone
from typing import Optional, List, Tuple

from sqlalchemy import select, func, desc
from sqlalchemy.ext.asyncio import AsyncSession

from core.api_token.model import ApiToken

TOKEN_PREFIX = "zqpat_"


def generate_token() -> str:
    """生成随机API Token"""
    return TOKEN_PREFIX + secrets.token_hex(32)


def hash_token(token: str) -> str:
    """对token进行SHA-256哈希"""
    return hashlib.sha256(token.encode()).hexdigest()


def get_token_display_prefix(token: str) -> str:
    """获取token的展示前缀（前12位 + ...）"""
    return token[:12] + "..."


class ApiTokenService:
    """API Token 服务层"""

    @classmethod
    async def create_token(
        cls,
        db: AsyncSession,
        user_id: str,
        name: str,
        expires_at: Optional[datetime] = None,
        description: Optional[str] = None,
    ) -> Tuple[ApiToken, str]:
        """
        创建API Token

        :return: (数据库记录, 明文token) - 明文token仅返回一次
        """
        raw_token = generate_token()

        db_obj = ApiToken(
            name=name,
            token_hash=hash_token(raw_token),
            token_prefix=get_token_display_prefix(raw_token),
            user_id=user_id,
            expires_at=expires_at,
            description=description,
            is_active=True,
            sys_creator_id=user_id,
        )

        db.add(db_obj)
        await db.commit()
        await db.refresh(db_obj)
        return db_obj, raw_token

    @classmethod
    async def get_user_tokens(
        cls,
        db: AsyncSession,
        user_id: str,
    ) -> List[ApiToken]:
        """获取用户的所有API Token"""
        result = await db.execute(
            select(ApiToken)
            .where(
                ApiToken.user_id == user_id,
                ApiToken.is_deleted == False,  # noqa: E712
            )
            .order_by(desc(ApiToken.sys_create_datetime))
        )
        return list(result.scalars().all())

    @classmethod
    async def get_token_by_id(
        cls,
        db: AsyncSession,
        token_id: str,
        user_id: str,
    ) -> Optional[ApiToken]:
        """根据ID获取Token（限定用户）"""
        result = await db.execute(
            select(ApiToken).where(
                ApiToken.id == token_id,
                ApiToken.user_id == user_id,
                ApiToken.is_deleted == False,  # noqa: E712
            )
        )
        return result.scalar_one_or_none()

    @classmethod
    async def revoke_token(
        cls,
        db: AsyncSession,
        token_id: str,
        user_id: str,
    ) -> bool:
        """撤销（软删除）Token"""
        token = await cls.get_token_by_id(db, token_id, user_id)
        if not token:
            return False
        token.is_deleted = True
        token.is_active = False
        await db.commit()
        return True

    @classmethod
    async def verify_token(
        cls,
        db: AsyncSession,
        raw_token: str,
    ) -> Optional[ApiToken]:
        """
        验证API Token

        :return: 有效则返回Token记录，否则返回None
        """
        token_hash_value = hash_token(raw_token)
        result = await db.execute(
            select(ApiToken).where(
                ApiToken.token_hash == token_hash_value,
                ApiToken.is_deleted == False,  # noqa: E712
                ApiToken.is_active == True,  # noqa: E712
            )
        )
        token = result.scalar_one_or_none()
        if not token:
            return None

        now_utc = datetime.now(timezone.utc)
        if token.expires_at:
            expires = token.expires_at if token.expires_at.tzinfo else token.expires_at.replace(tzinfo=timezone.utc)
            if expires < now_utc:
                return None

        token.last_used_at = now_utc.replace(tzinfo=None)
        await db.commit()
        return token
