#!/usr/bin/env python3
"""TG 群管机器人 v2 — FastAPI + Tortoise ORM + python-telegram-bot"""
import os
import time
import asyncio
import logging
from contextlib import asynccontextmanager

# 代理（必须在其他 import 之前）
PROXY_URL = os.getenv("PROXY_URL", "http://192.168.123.10:11055")
TG_PROXY_URLS = [x.strip() for x in os.getenv(
    "TG_PROXY_URLS",
    ",".join([
        PROXY_URL,
        "http://192.168.31.130:11055",
        "socks5://192.168.31.130:11088",
        "socks5://192.168.123.10:11088",
    ]),
).split(",") if x.strip()]
os.environ["HTTPS_PROXY"] = PROXY_URL
os.environ["HTTP_PROXY"] = PROXY_URL
os.environ["NO_PROXY"] = os.getenv("NO_PROXY", "127.0.0.1,localhost")

import uvicorn
from fastapi import FastAPI
from telegram import BotCommand, BotCommandScopeChat, Update
from telegram.ext import Application, ContextTypes

import config
from db import init_db, close_db
from handlers import register_all

# ============ 日志 ============

log_fmt = "%(asctime)s [%(levelname)s] %(message)s"
log_datefmt = "%Y-%m-%d %H:%M:%S"

logging.basicConfig(level=logging.INFO, format=log_fmt, datefmt=log_datefmt)
logger = logging.getLogger("spam_guard")

# 阻止 httpx 刷屏
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)

# ============ Telegram Bot ============

tg_app: Application = None
_last_polling_error_log = 0.0
_proxy_index = 0


def current_tg_proxy() -> str:
    return TG_PROXY_URLS[_proxy_index % len(TG_PROXY_URLS)]


def rotate_tg_proxy() -> str:
    global _proxy_index
    _proxy_index = (_proxy_index + 1) % len(TG_PROXY_URLS)
    return current_tg_proxy()


def polling_error_callback(exc):
    """压缩 Telegram polling 网络异常日志，避免代理偶发断连刷 traceback。"""
    global _last_polling_error_log
    now = time.time()
    # polling 会自动重试；同类网络波动 60 秒内只提示一次。
    if now - _last_polling_error_log < 60:
        return
    _last_polling_error_log = now
    logger.warning(f"⚠️ TG polling 长轮询连接被代理中断，已自动重试: {type(exc).__name__}: {exc}")


async def global_error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
    """全局错误兜底，避免命令无响应且无日志。"""
    err = context.error
    logger.error(f"❌ Handler 异常: {type(err).__name__}: {err}")
    try:
        if isinstance(update, Update) and update.effective_chat:
            await context.bot.send_message(chat_id=update.effective_chat.id, text="❌ 指令处理失败，请稍后重试")
    except Exception as e:
        logger.warning(f"⚠️ 错误提示发送失败: {type(e).__name__}: {e}")


async def start_tg_bot():
    global tg_app

    from telegram.request import HTTPXRequest

    active_proxy = current_tg_proxy()
    logger.info(f"🌐 TG 使用代理: {active_proxy}")
    proxy_request = HTTPXRequest(
        proxy=active_proxy,
        connection_pool_size=64,
        connect_timeout=30.0,
        read_timeout=30.0,
        write_timeout=30.0,
        pool_timeout=30.0,
    )
    get_updates_request = HTTPXRequest(
        proxy=active_proxy,
        connection_pool_size=8,
        connect_timeout=30.0,
        read_timeout=15.0,
        write_timeout=30.0,
        pool_timeout=30.0,
    )
    tg_app = (
        Application.builder()
        .token(config.TG_BOT_TOKEN)
        .request(proxy_request)
        .get_updates_request(get_updates_request)
        .build()
    )

    register_all(tg_app)
    tg_app.add_error_handler(global_error_handler)

    await tg_app.initialize()
    await tg_app.start()
    await tg_app.updater.start_polling(
        poll_interval=0.5,
        timeout=3,
        drop_pending_updates=False,
        error_callback=polling_error_callback,
    )

    # 菜单
    commands = [
        BotCommand("ask", "🤖 向知识库提问"),
        BotCommand("stats", "📊 今日统计"),
        BotCommand("checkin", "✅ 每日签到"),
        BotCommand("trivia", "🎮 知识答题"),
        BotCommand("lottery", "🎁 发起群内抽奖"),
        BotCommand("shop", "🛒 积分商城"),
        BotCommand("panel", "🎛️ 操作面板"),
        BotCommand("bind", "🔗 绑定账号"),
        BotCommand("me", "👤 查看授权信息"),
        BotCommand("dban", "🗑️ 删除并封禁"),
        BotCommand("dmute", "🔇 删除并禁言"),
        BotCommand("dwarn", "⚠️ 删除并警告"),
        BotCommand("help", "📖 帮助"),
    ]
    await tg_app.bot.delete_my_commands()
    await tg_app.bot.set_my_commands(commands, scope=BotCommandScopeChat(chat_id=config.TG_CHAT_ID))
    logger.info(f"✅ TG Bot 已启动，菜单命令已设置 (群组: {config.TG_CHAT_ID})")


async def stop_tg_bot():
    global tg_app
    if tg_app:
        try:
            if tg_app.updater and tg_app.updater.running:
                await tg_app.updater.stop()
            if tg_app.running:
                await tg_app.stop()
            await tg_app.shutdown()
            logger.info("🛑 TG Bot 已停止")
        except Exception as e:
            logger.warning(f"⚠️ TG Bot 停止/清理异常: {type(e).__name__}: {e}")
        finally:
            tg_app = None


# ============ FastAPI ============

from web.api import app as web_app


async def restart_tg_bot_runtime():
    """供 Web API 调用：只重启 TG Bot，不重启 Web。"""
    await stop_tg_bot()
    asyncio.create_task(start_tg_bot_safe())


async def start_tg_bot_safe():
    """启动 TG Bot；初始化阶段网络失败时压缩日志并自动重试。"""
    delay = 10
    while True:
        try:
            await start_tg_bot()
            return
        except asyncio.CancelledError:
            raise
        except Exception as e:
            next_proxy = rotate_tg_proxy()
            logger.error(f"❌ TG Bot 启动失败，将在 {delay}s 后用备用代理重试: {type(e).__name__}: {e}; next={next_proxy}")
            try:
                await stop_tg_bot()
            except Exception:
                pass
            await asyncio.sleep(delay)
            delay = min(120, delay * 2)


@asynccontextmanager
async def lifespan(app):
    # 初始化数据库
    await init_db()
    # 启动 TG Bot
    import web.api as web_api
    web_api.set_restart_bot_callback(restart_tg_bot_runtime)
    task = asyncio.create_task(start_tg_bot_safe())

    def log_task_result(t: asyncio.Task):
        if t.cancelled():
            return
        exc = t.exception()
        if exc:
            logger.error("❌ TG Bot 任务异常结束", exc_info=exc)

    task.add_done_callback(log_task_result)
    logger.info("🚀 服务启动完成")
    try:
        yield
    finally:
        await stop_tg_bot()
        await close_db()
        logger.info("🛑 服务已关闭")


web_app.router.lifespan_context = lifespan


def run():
    uvicorn.run(web_app, host="0.0.0.0", port=config.WEB_PORT, log_level="info", access_log=False)


if __name__ == "__main__":
    run()
