"""小游戏服务"""
import random
import logging
from datetime import datetime, timedelta

from db.models import GameScore, LotteryEvent, LotteryParticipant, ShopItem, ShopTransaction
from tortoise.transactions import in_transaction
from tortoise.exceptions import IntegrityError

logger = logging.getLogger("spam_guard")

TRIVIA_QUESTIONS = [
    {"q": "收割机项目的 GitHub 地址是？", "a": "github.com/ngfchl/go-harvest", "opts": ["github.com/ngfchl/go-harvest", "github.com/ngfchl/harvest", "github.com/harvest/go-harvest"]},
    {"q": "以下哪个不是 PT 站的常见等级？", "a": "Diamond", "opts": ["User", "Power User", "Diamond", "Elite"]},
    {"q": "Docker 默认的日志驱动是？", "a": "json-file", "opts": ["json-file", "syslog", "journald", "fluentd"]},
]

SHOP_ITEMS = {
    "1": {"name": "补签卡", "cost": 50, "desc": "预留权益：后续可用于补签"},
    "2": {"name": "群内称号券", "cost": 80, "desc": "预留权益：联系管理员兑换称号"},
    "3": {"name": "好运盲盒", "cost": 30, "desc": "兑换后记录一次好运盲盒"},
}


async def get_or_create_score(chat_id: int, user_id: int, username: str, game_type: str) -> GameScore:
    score, _ = await GameScore.get_or_create(
        chat_id=chat_id, user_id=user_id, game_type=game_type,
        defaults={"username": username},
    )
    if username and score.username != username:
        score.username = username
        await score.save()
    return score


async def get_trivia_question() -> dict:
    return random.choice(TRIVIA_QUESTIONS)


async def check_trivia_answer(chat_id: int, user_id: int, username: str, answer: str, correct_answer: str) -> dict:
    score = await get_or_create_score(chat_id, user_id, username, "trivia")
    score.total += 1
    is_correct = answer.strip().lower() in correct_answer.strip().lower()
    if is_correct:
        score.score += 10
        score.wins += 1
        score.streak += 1
        if score.streak > 1:
            score.score += 5
    else:
        score.streak = 0
    score.last_played = datetime.now()
    await score.save()
    return {"correct": is_correct, "score": score.score, "streak": score.streak}


async def get_leaderboard(chat_id: int, game_type: str = "trivia", limit: int = 10) -> list:
    return await GameScore.filter(chat_id=chat_id, game_type=game_type).order_by("-score").limit(limit).all()


async def daily_checkin(chat_id: int, user_id: int, username: str) -> dict:
    score = await get_or_create_score(chat_id, user_id, username, "checkin")
    today = datetime.now().strftime("%Y-%m-%d")
    if score.last_played and score.last_played.strftime("%Y-%m-%d") == today:
        return {"ok": False, "reason": "already_checked", "score": score.score, "streak": score.streak}
    score.total += 1
    base = random.randint(5, 12)
    streak_bonus = min(score.streak + 1, 7)
    bonus = base + streak_bonus
    score.score += bonus
    score.streak += 1
    score.wins += 1
    score.last_played = datetime.now()
    await score.save()
    return {"ok": True, "score": score.score, "streak": score.streak, "bonus": bonus, "base": base, "streak_bonus": streak_bonus}


async def get_scores_summary(user_id: int, chat_id: int = 0) -> dict:
    scores = await GameScore.filter(chat_id=chat_id, user_id=user_id).all()
    total = sum((s.score or 0) for s in scores)
    by_type = {s.game_type: s for s in scores}
    return {"scores": scores, "total": total, "by_type": by_type}


async def _deduct_points_locked(conn, chat_id: int, user_id: int, cost: int) -> bool:
    scores = await GameScore.filter(chat_id=chat_id, user_id=user_id).using_db(conn).select_for_update().all()
    total = sum((s.score or 0) for s in scores)
    if total < cost:
        return False
    remaining = cost
    for score in sorted(scores, key=lambda x: x.score or 0, reverse=True):
        if remaining <= 0:
            break
        take = min(score.score or 0, remaining)
        score.score -= take
        remaining -= take
        await score.save(using_db=conn)
    return True


async def deduct_points(chat_id: int, user_id: int, cost: int) -> bool:
    async with in_transaction() as conn:
        return await _deduct_points_locked(conn, chat_id, user_id, cost)


async def lottery_draw(chat_id: int, user_id: int, username: str) -> dict:
    cost = 10
    if not await deduct_points(chat_id, user_id, cost):
        return {"ok": False, "reason": "积分不足", "cost": cost}
    roll = random.random()
    reward = 0
    prize = "谢谢参与"
    if roll < 0.08:
        reward, prize = 60, "一等奖 +60"
    elif roll < 0.25:
        reward, prize = 25, "二等奖 +25"
    elif roll < 0.55:
        reward, prize = 12, "三等奖 +12"
    score = await get_or_create_score(chat_id, user_id, username, "lottery")
    score.total += 1
    if reward:
        score.wins += 1
        score.score += reward
    score.last_played = datetime.now()
    await score.save()
    summary = await get_scores_summary(user_id, chat_id)
    return {"ok": True, "cost": cost, "reward": reward, "prize": prize, "total": summary["total"]}


async def list_shop_items(chat_id: int, include_disabled: bool = False) -> list[dict]:
    rows = await ShopItem.filter(chat_id=chat_id).order_by("item_key").all()
    if not rows and chat_id != 0:
        rows = await ShopItem.filter(chat_id=0).order_by("item_key").all()
    if not rows:
        return [{"item_key": k, "name": v["name"], "cost": v["cost"], "stock": -1, "desc": v["desc"], "enabled": True} for k, v in SHOP_ITEMS.items()]
    result = []
    for r in rows:
        if include_disabled or r.enabled:
            result.append({"id": r.id, "chat_id": r.chat_id, "item_key": r.item_key, "name": r.name, "cost": r.cost, "stock": r.stock, "desc": r.desc, "enabled": r.enabled})
    return result


async def upsert_shop_item(chat_id: int, item_key: str, name: str, cost: int, desc: str = "", enabled: bool = True, stock: int = -1) -> ShopItem:
    item, _ = await ShopItem.get_or_create(chat_id=chat_id, item_key=item_key, defaults={"name": name, "cost": cost, "desc": desc, "enabled": enabled, "stock": stock})
    item.name = name
    item.cost = cost
    item.stock = stock
    item.desc = desc
    item.enabled = enabled
    await item.save()
    return item


async def delete_shop_item(chat_id: int, item_key: str) -> bool:
    deleted = await ShopItem.filter(chat_id=chat_id, item_key=item_key).delete()
    return bool(deleted)


async def list_shop_transactions(chat_id: int, limit: int = 100) -> list[dict]:
    rows = await ShopTransaction.filter(chat_id=chat_id).order_by("-created_at").limit(limit).all()
    return [{"id": r.id, "chat_id": r.chat_id, "user_id": r.user_id, "username": r.username, "item_key": r.item_key, "item_name": r.item_name, "cost": r.cost, "status": r.status, "note": r.note, "created_at": r.created_at} for r in rows]


async def buy_shop_item(chat_id: int, user_id: int, item_id: str, username: str = "") -> dict:
    async with in_transaction() as conn:
        db_item = await ShopItem.filter(chat_id=chat_id, item_key=str(item_id), enabled=True).using_db(conn).select_for_update().first()
        if not db_item:
            # 兼容默认内置商品：没有数据库记录时视为无限库存。
            builtin = SHOP_ITEMS.get(str(item_id))
            if not builtin:
                return {"ok": False, "reason": "商品不存在"}
            item = {"item_key": str(item_id), "name": builtin["name"], "cost": builtin["cost"], "stock": -1, "desc": builtin["desc"], "enabled": True}
        else:
            item = {"id": db_item.id, "chat_id": db_item.chat_id, "item_key": db_item.item_key, "name": db_item.name, "cost": db_item.cost, "stock": db_item.stock, "desc": db_item.desc, "enabled": db_item.enabled}
            if db_item.stock == 0:
                return {"ok": False, "reason": "库存不足", "item": item}
        if not await _deduct_points_locked(conn, chat_id, user_id, int(item["cost"])):
            return {"ok": False, "reason": "积分不足", "item": item}
        if db_item and db_item.stock > 0:
            db_item.stock -= 1
            await db_item.save(using_db=conn, update_fields=["stock", "updated_at"])
            item["stock"] = db_item.stock
        await ShopTransaction.create(
            chat_id=chat_id,
            user_id=user_id,
            username=username,
            item_key=str(item.get("item_key")),
            item_name=item.get("name") or "",
            cost=int(item.get("cost") or 0),
            status="success",
            using_db=conn,
        )
    summary = await get_scores_summary(user_id, chat_id)
    return {"ok": True, "item": item, "total": summary["total"]}


LOTTERY_CONDITION_HELP = "参与条件：all=所有人；checkin=今日已签到；points:N=总积分不少于 N"
LOTTERY_END_HELP = "结束条件：people:N=满 N 人开奖；minutes:N=N 分钟后开奖；manual=手动开奖（预留）"


def parse_lottery_condition(raw: str) -> tuple[str, int, str]:
    text = (raw or "all").strip().lower()
    if text in {"all", "所有人", "不限"}:
        return "all", 0, "所有人可参与"
    if text in {"checkin", "签到", "今日签到"}:
        return "checkin", 0, "今日已签到用户"
    if text.startswith("points:") or text.startswith("积分:"):
        value = int(text.split(":", 1)[1])
        return "min_points", value, f"总积分 ≥ {value}"
    raise ValueError(LOTTERY_CONDITION_HELP)


def parse_lottery_end(raw: str) -> tuple[str, int, str]:
    text = (raw or "people:3").strip().lower()
    if text in {"instant", "即开", "即开即中"}:
        return "instant", 1, "即开即中：首位参与者立即中奖"
    if text.startswith("people:") or text.startswith("人数:"):
        value = max(1, int(text.split(":", 1)[1]))
        return "people", value, f"满 {value} 人自动开奖"
    if text.startswith("minutes:") or text.startswith("分钟:"):
        value = max(1, int(text.split(":", 1)[1]))
        return "time", value, f"{value} 分钟后自动开奖"
    if text.startswith("time:") or text.startswith("时间:"):
        return "time_at", 0, f"指定时间开奖：{raw.split(':', 1)[1].strip()}"
    if text in {"manual", "手动"}:
        return "manual", 0, "手动开奖"
    raise ValueError(LOTTERY_END_HELP)


def parse_lottery_spec(text: str) -> dict:
    parts = [p.strip() for p in text.replace("｜", "|").split("|")]
    if len(parts) != 3:
        raise ValueError("格式：奖品 | 参与条件 | 结束条件")
    prize = parts[0]
    if not prize:
        raise ValueError("奖品不能为空")
    condition_type, condition_value, condition_text = parse_lottery_condition(parts[1])
    end_type, end_value, end_text = parse_lottery_end(parts[2])
    return {
        "prize": prize,
        "condition_type": condition_type,
        "condition_value": condition_value,
        "condition_text": condition_text,
        "end_type": end_type,
        "end_value": end_value,
        "end_text": end_text,
    }


async def create_lottery_event(chat_id: int, creator_id: int, creator_name: str, spec: dict) -> LotteryEvent:
    return await LotteryEvent.create(
        chat_id=chat_id,
        creator_id=creator_id,
        creator_name=creator_name,
        title=spec.get("title") or spec["prize"],
        description=spec.get("description") or "",
        prize=spec["prize"],
        condition_type=spec["condition_type"],
        condition_value=spec["condition_value"],
        draw_type=spec.get("draw_type") or spec["end_type"],
        draw_at=spec.get("draw_at"),
        max_participants=spec.get("max_participants") or (spec["end_value"] if spec["end_type"] == "people" else 0),
        end_type=spec["end_type"],
        end_value=spec["end_value"],
    )


async def lottery_condition_text(event: LotteryEvent) -> str:
    if event.condition_type == "checkin":
        return "今日已签到用户"
    if event.condition_type in {"min_points", "points"}:
        return f"总积分 ≥ {event.condition_value}"
    return "所有人可参与"


async def lottery_end_text(event: LotteryEvent) -> str:
    if event.end_type == "people":
        return f"满 {event.end_value} 人自动开奖"
    if event.end_type == "minutes":
        return f"{event.end_value} 分钟后自动开奖"
    return "手动开奖"


async def count_lottery_participants(event_id: int) -> int:
    return await LotteryParticipant.filter(event_id=event_id).count()


async def check_lottery_eligible(event: LotteryEvent, user_id: int) -> tuple[bool, str]:
    if event.condition_type == "checkin":
        score = await GameScore.filter(chat_id=event.chat_id, user_id=user_id, game_type="checkin").first()
        today = datetime.now().strftime("%Y-%m-%d")
        if not score or not score.last_played or score.last_played.strftime("%Y-%m-%d") != today:
            return False, "需要今日已签到后才能参与"
    elif event.condition_type in {"min_points", "points"}:
        summary = await get_scores_summary(user_id, event.chat_id)
        if summary["total"] < event.condition_value:
            return False, f"总积分需要 ≥ {event.condition_value}，当前 {summary['total']}"
    return True, "ok"


async def join_lottery_event(event_id: int, user_id: int, username: str = "", first_name: str = "") -> dict:
    async with in_transaction() as conn:
        event = await LotteryEvent.filter(id=event_id).using_db(conn).select_for_update().first()
        if not event or event.status != "active":
            return {"ok": False, "reason": "抽奖不存在或已结束"}
        ok, reason = await check_lottery_eligible(event, user_id)
        if not ok:
            return {"ok": False, "reason": reason}
        exists = await LotteryParticipant.filter(event_id=event_id, user_id=user_id).using_db(conn).exists()
        if exists:
            return {"ok": False, "reason": "你已经参与过了"}

        is_instant = event.draw_type == "instant" or event.end_type == "instant"
        if (not is_instant) and event.end_type == "people" and int(event.end_value or 0) > 0:
            current_count = await LotteryParticipant.filter(event_id=event_id).using_db(conn).count()
            if current_count >= int(event.end_value or 0):
                return {"ok": False, "reason": "参与人数已满，等待开奖"}
        if is_instant and int(event.remaining_count or 0) <= 0:
            event.status = "ended"
            event.ended_at = datetime.now()
            await event.save(using_db=conn)
            return {"ok": False, "reason": "奖品已抽完，活动已结束"}
        try:
            participant = await LotteryParticipant.create(event_id=event_id, user_id=user_id, username=username, first_name=first_name, is_winner=is_instant, using_db=conn)
        except IntegrityError:
            return {"ok": False, "reason": "你已经参与过了"}
        count = await LotteryParticipant.filter(event_id=event_id).using_db(conn).count()
        won = False
        ended = False
        if is_instant:
            won = True
            event.remaining_count = max(0, int(event.remaining_count or 0) - 1)
            event.winner_id = user_id
            event.winner_name = username or first_name or str(user_id)
            if event.remaining_count <= 0:
                event.status = "ended"
                event.ended_at = datetime.now()
                ended = True
            await event.save(using_db=conn)
        return {"ok": True, "event": event, "participant": participant, "count": count, "won": won, "ended": ended, "should_draw": (not is_instant) and event.end_type == "people" and count >= event.end_value}


def lottery_event_payload(event: LotteryEvent | None) -> dict | None:
    if not event:
        return None
    return {
        "id": event.id,
        "chat_id": event.chat_id,
        "title": event.title,
        "prize": event.prize,
        "description": event.description,
        "condition_type": event.condition_type,
        "condition_value": event.condition_value,
        "end_type": event.end_type,
        "end_value": event.end_value,
        "draw_at": event.draw_at,
        "prize_count": event.prize_count,
        "remaining_count": event.remaining_count,
        "status": event.status,
        "winner_id": event.winner_id,
        "winner_name": event.winner_name,
        "ended_at": event.ended_at,
        "created_at": event.created_at,
        "updated_at": event.updated_at,
    }


def lottery_participant_payload(p: LotteryParticipant | None) -> dict | None:
    if not p:
        return None
    return {
        "id": p.id,
        "event_id": p.event_id,
        "user_id": p.user_id,
        "username": p.username,
        "first_name": p.first_name,
        "is_winner": getattr(p, "is_winner", False),
        "created_at": p.created_at,
        "updated_at": p.updated_at,
    }


async def draw_lottery_event(event_id: int) -> dict:
    async with in_transaction() as conn:
        event = await LotteryEvent.filter(id=event_id).using_db(conn).select_for_update().first()
        if not event or event.status != "active":
            return {"ok": False, "reason": "抽奖不存在或已结束"}
        participants = await LotteryParticipant.filter(event_id=event_id).using_db(conn).all()
        if not participants:
            event.status = "cancelled"
            event.ended_at = datetime.now()
            await event.save(using_db=conn)
            return {"ok": False, "reason": "无人参与，抽奖已取消", "event": lottery_event_payload(event)}
        winner = random.choice(participants)
        winner.is_winner = True
        await winner.save(using_db=conn)
        event.status = "ended"
        event.winner_id = winner.user_id
        event.winner_name = winner.username or winner.first_name or str(winner.user_id)
        event.ended_at = datetime.now()
        await event.save(using_db=conn)
        return {"ok": True, "event": lottery_event_payload(event), "winner": lottery_participant_payload(winner), "count": len(participants)}
