from datetime import datetime, timedelta, timezone
import asyncio
import httpx
import json

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.user_oauth import UserOAuth
from app.models.user_oauth_app import UserOAuthApp
from app.models.base import async_session
from app.config import settings
from app.utils.redis import get_redis
from app.utils.logger import get_logger

REDIS_KEY = "douyin:tokens"
logger = get_logger("token_refresh", "token_refresh")



REFRESH_THRESHOLD_SECONDS = 800
CHECK_INTERVAL_MINUTES = 5


async def _update_redis_token(oauth_id: str, token: str, expired_at: datetime):
    """更新Redis缓存中的token"""
    redis = get_redis()
    if not redis:
        logger.warning("Redis连接未配置，跳过缓存更新")
        return

    try:
        cache = {
            "token": token,
            "expired_at": expired_at.isoformat(),
        }
        await redis.hset(REDIS_KEY, oauth_id, json.dumps(cache))
        logger.info(f"Redis缓存已更新: oauth_id={oauth_id}")
    except Exception as e:
        logger.error(f"更新Redis缓存失败: {str(e)}")


async def refresh_juliang_token(oauth: UserOAuth, app: UserOAuthApp, db: AsyncSession):
    """刷新巨量引擎token"""
    try:
        async with httpx.AsyncClient() as client:
            url = "https://api.oceanengine.com/open_api/oauth2/refresh_token/"
            response = await client.post(
                url,
                json={
                    "app_id": app.app_id,
                    "secret": app.secret,
                    "refresh_token": oauth.refresh_token,
                },
            )
            response.raise_for_status()
            data = response.json()
            if data.get("code") != 0:
                logger.error(f"刷新巨量引擎token失败: oauth_id={oauth.id}, 错误信息: {data}")
                #如果code=40103或者40107，传入refresh_token已失效，失效原因一般是由于refresh_token已被使用，或授权账号重新授权并生成了新的Token
                if data.get("code") in [40103, 40107]:
                    #清空数据库中的token信息，和Redis缓存中的token
                    oauth.access_token = None
                    oauth.access_token_expired = None
                    oauth.refresh_token = None
                    oauth.refresh_token_expired = None
                    await db.commit()
                    await _update_redis_token(oauth.id, "", None)    
                return

            
            data = data.get("data", {})
            new_access_token = data.get("access_token", "")
            new_refresh_token = data.get("refresh_token", "")
            expires_in = datetime.now(tz=oauth.access_token_expired.tzinfo) + timedelta(seconds=data.get("expires_in", 0))
            refresh_token_expires_in = datetime.now(tz=oauth.refresh_token_expired.tzinfo) + timedelta(seconds=data.get("refresh_token_expires_in", 0))

            oauth.access_token = new_access_token
            oauth.access_token_expired = expires_in
            oauth.refresh_token = new_refresh_token
            oauth.refresh_token_expires_in = refresh_token_expires_in
            await db.commit()
            
            await _update_redis_token(oauth.id, new_access_token, expires_in)
            
            logger.info(f"成功刷新巨量引擎token: oauth_id={oauth.id}, account_id={oauth.account_id}")
    except httpx.HTTPError as e:
        logger.error(f"HTTP请求失败: oauth_id={oauth.id}, 错误: {str(e)}")
    except Exception as e:
        logger.error(f"刷新巨量引擎token发生异常: oauth_id={oauth.id}, 错误: {str(e)}")


async def check_and_refresh_tokens():
    """检查并刷新即将过期的token"""
    async with async_session() as db:
        now = datetime.now(timezone.utc)
        
        query = select(UserOAuth).where(
            UserOAuth.deleted_at.is_(None),
            UserOAuth.access_token.is_not(None),
            UserOAuth.access_token_expired.is_not(None),
            UserOAuth.refresh_token.is_not(None),
            UserOAuth.refresh_token_expired.is_not(None),
        )
        
        result = await db.execute(query)
        oauth_list = result.scalars().all()
        
        for oauth in oauth_list:
            try:
                #1.检查access_token是否过期，如果未过期，并且大于800秒，直接跳过不处理
                if not oauth.access_token_expired:
                    continue
                
                remaining_seconds = (oauth.access_token_expired - now).total_seconds()
                
                # access_token剩余时间大于等于800秒，不需要刷新
                if remaining_seconds >= REFRESH_THRESHOLD_SECONDS:
                    continue
                
                #2.如果access_token过期，或者剩余时间小于800秒，需要刷新token
                #3.如果需要刷新token，检查refresh_token是否过期，如果refresh_token过期，说明不可刷新，需要直接重新授权，直接跳过不处理
                if not oauth.refresh_token_expired:
                    continue
                
                refresh_remaining_seconds = (oauth.refresh_token_expired - now).total_seconds()
                if refresh_remaining_seconds <= 0:
                    continue
                
                #5.获取应用配置
                app_result = await db.execute(
                    select(UserOAuthApp).where(UserOAuthApp.app_id == oauth.appid)
                )
                app = app_result.scalar_one_or_none()
                
                if not app:
                    continue

                #检查是否为支持的平台（巨量引擎）
                # port_type: 平台端口（1=巨量，2=磁力，3=巨量星图，4=服务单，5=腾讯）
                if oauth.port_type in [1]:  
                    #刷新token
                    await refresh_juliang_token(oauth, app, db)
                
            except Exception as e:
                #7.增加错误日志
                logger.error(f"刷新token失败: {str(e)}")


async def token_refresh_scheduler():
    """定时任务调度器"""
    while True:
        try:
            await check_and_refresh_tokens()
        except Exception as e:
            logger.error(f"定时任务token_refresh_scheduler执行失败: {str(e)}")
        
        await asyncio.sleep(CHECK_INTERVAL_MINUTES * 60)


def start_token_refresh_task():
    """启动token刷新定时任务"""
    logger.info("启动token刷新定时任务")
    asyncio.create_task(token_refresh_scheduler())