# -*- coding: utf-8 -*-
"""
KARDS 二战卡牌图鉴 & 创新实验室 —— 桌面版（Qt / PySide6）
=================================================================
运行:  python3 kards_gui.py
依赖:  pip install PySide6-Essentials
功能:  主页介绍 / 规则速查 / 卡牌库筛选 / 卡组构建与校验 /
       战斗模拟 / 创新实验室 / AI 自动对战
=================================================================
"""
import json, random, re, sys
from dataclasses import dataclass, field
from PySide6.QtWidgets import *
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QFont, QColor, QPalette, QPainter, QBrush, QPen

# ==================== 卡牌数据 ====================
CARDS = [
    # 德国
    {"id": 1, "name": "闪电突击兵", "faction": "德国", "type": "步兵", "deploy": 2, "action": 1, "atk": 2, "def": 2, "kw": ["闪击"], "desc": "具有闪击能力，下场即可移动/攻击。", "tier": "普通"},
    {"id": 2, "name": "三号突击炮", "faction": "德国", "type": "坦克", "deploy": 4, "action": 2, "atk": 3, "def": 4, "kw": ["闪击"], "desc": "经典的突击炮支援单位。", "tier": "稀有"},
    {"id": 3, "name": "虎式坦克", "faction": "德国", "type": "坦克", "deploy": 7, "action": 3, "atk": 6, "def": 7, "kw": ["奋战"], "desc": "一回合可攻击两次，装甲厚实。", "tier": "史诗"},
    {"id": 4, "name": "斯图卡俯冲轰炸机", "faction": "德国", "type": "轰炸机", "deploy": 3, "action": 1, "atk": 2, "def": 1, "kw": ["俯冲"], "desc": "进场时对敌方单位造成 2 点伤害。", "tier": "稀有"},
    {"id": 5, "name": "88mm 高炮", "faction": "德国", "type": "火炮", "deploy": 5, "action": 1, "atk": 4, "def": 2, "kw": ["防空"], "desc": "可攻击空军单位，对轰炸机威胁巨大。", "tier": "稀有"},
    {"id": 6, "name": "装甲掷弹兵", "faction": "德国", "type": "步兵", "deploy": 3, "action": 1, "atk": 3, "def": 3, "kw": ["闪击"], "desc": "摩托化步兵，配合坦克推进。", "tier": "普通"},
    {"id": 7, "name": "Bf-109 战斗机", "faction": "德国", "type": "战斗机", "deploy": 3, "action": 1, "atk": 2, "def": 2, "kw": ["拦截"], "desc": "可拦截敌方轰炸机，阻止其攻击。", "tier": "普通"},
    {"id": 8, "name": "装甲集群", "faction": "德国", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": ["闪击"], "desc": "本回合你所有坦克获得闪击。", "tier": "稀有"},
    {"id": 9, "name": "元帅参谋部", "faction": "德国", "type": "指令", "deploy": 4, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "抽 2 张牌，本回合指挥点 +2。", "tier": "史诗"},
    # 苏联
    {"id": 10, "name": "动员兵", "faction": "苏联", "type": "步兵", "deploy": 1, "action": 1, "atk": 1, "def": 2, "kw": ["动员"], "desc": "部署时若前线为空，召唤一个复制。", "tier": "普通"},
    {"id": 11, "name": "T-34 坦克", "faction": "苏联", "type": "坦克", "deploy": 5, "action": 2, "atk": 4, "def": 5, "kw": ["闪击"], "desc": "产量惊人的主力中型坦克。", "tier": "稀有"},
    {"id": 12, "name": "喀秋莎火箭炮", "faction": "苏联", "type": "火炮", "deploy": 4, "action": 1, "atk": 3, "def": 1, "kw": ["齐射"], "desc": "进场时对随机 2 个敌方单位各造成 1 点伤害。", "tier": "史诗"},
    {"id": 13, "name": "伊尔-2 强击机", "faction": "苏联", "type": "轰炸机", "deploy": 4, "action": 2, "atk": 3, "def": 2, "kw": [], "desc": "对地面单位造成有效打击。", "tier": "稀有"},
    {"id": 14, "name": "雅克战斗机", "faction": "苏联", "type": "战斗机", "deploy": 2, "action": 1, "atk": 2, "def": 1, "kw": ["拦截"], "desc": "廉价拦截机，保护前线。", "tier": "普通"},
    {"id": 15, "name": "政委动员令", "faction": "苏联", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "一个友军步兵本回合 +2/+2。", "tier": "普通"},
    {"id": 16, "name": "乌拉冲锋", "faction": "苏联", "type": "指令", "deploy": 3, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "前线所有友军步兵获得闪击。", "tier": "稀有"},
    {"id": 17, "name": "KV-2 重型坦克", "faction": "苏联", "type": "坦克", "deploy": 6, "action": 3, "atk": 5, "def": 6, "kw": [], "desc": "厚装甲的重型突破坦克。", "tier": "史诗"},
    # 美国
    {"id": 18, "name": "美国大兵", "faction": "美国", "type": "步兵", "deploy": 2, "action": 1, "atk": 2, "def": 2, "kw": [], "desc": "标准步兵，可靠的火力支援。", "tier": "普通"},
    {"id": 19, "name": "谢尔曼坦克", "faction": "美国", "type": "坦克", "deploy": 4, "action": 2, "atk": 3, "def": 4, "kw": ["闪击"], "desc": "数量众多、易于量产的中型坦克。", "tier": "稀有"},
    {"id": 20, "name": "B-17 空中堡垒", "faction": "美国", "type": "轰炸机", "deploy": 6, "action": 2, "atk": 4, "def": 3, "kw": ["护航"], "desc": "空军优势，进场时抽 1 张牌。", "tier": "史诗"},
    {"id": 21, "name": "P-51 野马", "faction": "美国", "type": "战斗机", "deploy": 3, "action": 1, "atk": 3, "def": 2, "kw": ["拦截"], "desc": "长航程护航战斗机。", "tier": "稀有"},
    {"id": 22, "name": "M7 牧师火炮", "faction": "美国", "type": "火炮", "deploy": 4, "action": 1, "atk": 3, "def": 2, "kw": [], "desc": "机动自行火炮，支援前线。", "tier": "普通"},
    {"id": 23, "name": "补给运输", "faction": "美国", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "抽 2 张牌。", "tier": "普通"},
    {"id": 24, "name": "舰炮支援", "faction": "美国", "type": "指令", "deploy": 5, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "对一个敌方单位造成 5 点伤害。", "tier": "史诗"},
    # 英国
    {"id": 25, "name": "掷弹兵卫队", "faction": "英国", "type": "步兵", "deploy": 3, "action": 1, "atk": 2, "def": 4, "kw": ["守护"], "desc": "守护单位，保护相邻友军。", "tier": "稀有"},
    {"id": 26, "name": "丘吉尔坦克", "faction": "英国", "type": "坦克", "deploy": 6, "action": 2, "atk": 4, "def": 7, "kw": ["守护"], "desc": "重装甲守护坦克。", "tier": "史诗"},
    {"id": 27, "name": "喷火战斗机", "faction": "英国", "type": "战斗机", "deploy": 2, "action": 1, "atk": 2, "def": 2, "kw": ["拦截"], "desc": "传奇的不列颠守护者。", "tier": "稀有"},
    {"id": 28, "name": "皇家炮兵", "faction": "英国", "type": "火炮", "deploy": 3, "action": 1, "atk": 2, "def": 2, "kw": [], "desc": "精准的远程火力支援。", "tier": "普通"},
    {"id": 29, "name": "雷达预警", "faction": "英国", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：对手打出空军单位时，使其费用 +1。", "tier": "稀有"},
    {"id": 30, "name": "坚守防线", "faction": "英国", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "一个友军单位获得守护直到回合结束。", "tier": "普通"},
    # 日本
    {"id": 31, "name": "精锐步兵", "faction": "日本", "type": "步兵", "deploy": 1, "action": 1, "atk": 2, "def": 1, "kw": ["闪击"], "desc": "廉价的激进步兵，擅长突袭。", "tier": "普通"},
    {"id": 32, "name": "零式战斗机", "faction": "日本", "type": "战斗机", "deploy": 2, "action": 1, "atk": 2, "def": 1, "kw": ["拦截"], "desc": "高机动性的轻型战斗机。", "tier": "稀有"},
    {"id": 33, "name": "神风特攻", "faction": "日本", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：友军死亡时对随机敌方造成 2 点伤害。", "tier": "史诗"},
    {"id": 34, "name": "九七式坦克", "faction": "日本", "type": "坦克", "deploy": 3, "action": 2, "atk": 2, "def": 3, "kw": ["闪击"], "desc": "轻型快速坦克，适合突进。", "tier": "普通"},
    {"id": 35, "name": "一式陆攻", "faction": "日本", "type": "轰炸机", "deploy": 4, "action": 1, "atk": 3, "def": 1, "kw": [], "desc": "双发重型陆上攻击机。", "tier": "稀有"},
    {"id": 36, "name": "玉碎冲锋", "faction": "日本", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "前线所有友军 +1 攻，本回合获得闪击。", "tier": "史诗"},
    # 通用
    {"id": 37, "name": "战地医院", "faction": "通用", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "为一个友军单位恢复 3 点防御。", "tier": "普通"},
    {"id": 38, "name": "战术轰炸", "faction": "通用", "type": "指令", "deploy": 3, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "对一个敌方单位造成 3 点伤害。", "tier": "普通"},
    {"id": 39, "name": "物资空投", "faction": "通用", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": [], "desc": "抽 1 张牌，本回合指挥点 +1。", "tier": "普通"},
    {"id": 40, "name": "战略撤退", "faction": "通用", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：友军将受 3+ 伤害时改为 1。", "tier": "稀有"},
    {"id": 41, "name": "情报窃取", "faction": "通用", "type": "指令", "deploy": 2, "action": 0, "atk": 0, "def": 0, "kw": ["情报"], "desc": "窥视对手 2 张手牌。", "tier": "稀有"},
    {"id": 42, "name": "烟幕掩护", "faction": "通用", "type": "指令", "deploy": 1, "action": 0, "atk": 0, "def": 0, "kw": ["烟幕"], "desc": "一个友军单位获得烟幕（不能被敌方单位攻击）。", "tier": "普通"},
]

FACTIONS = ["德国", "苏联", "美国", "英国", "日本", "通用"]
TYPES = ["步兵", "坦克", "火炮", "战斗机", "轰炸机", "指令"]
TIERS = ["普通", "稀有", "史诗"]
MAX_COPIES = 3

FACTION_COLOR = {
    "德国": "#5b7a9d", "苏联": "#c0392b", "美国": "#3a78b5",
    "英国": "#7d5ba6", "日本": "#c05c2a", "通用": "#1f9e8f",
}
RARITY_COLOR = {"普通": "#9fb0c0", "稀有": "#4aa3df", "史诗": "#b14aed"}

def get_card(cid):
    for c in CARDS:
        if c["id"] == cid: return c
    return None

# ==================== 卡组校验与填充 ====================
def validate_deck(deck):
    errs, warns = [], []
    total = len(deck)
    if total != 40:
        errs.append(f"卡组必须为 40 张，当前 {total} 张")
    counts = {}
    for c in deck:
        counts[c["id"]] = counts.get(c["id"], 0) + 1
    for cid, n in counts.items():
        if n > MAX_COPIES:
            errs.append(f"「{get_card(cid)['name']}」最多 {MAX_COPIES} 张，当前 {n} 张")
    over2 = [get_card(cid)["name"] for cid, n in counts.items() if 2 < n <= MAX_COPIES]
    if over2:
        warns.append(f"扩编模式：{', '.join(over2)} 带满 {MAX_COPIES} 张")
    factions = {}
    for c in deck:
        factions[c["faction"]] = factions.get(c["faction"], 0) + 1
    if not factions:
        errs.append("卡组为空")
        return {"errors": errs, "warnings": warns, "stats": {}}
    main = max(factions, key=factions.get)
    non_main = {k: v for k, v in factions.items() if k != main and k != "通用"}
    ally_count = sum(non_main.values())
    if ally_count > 12:
        errs.append(f"盟国卡不得超过 12 张，当前 {ally_count} 张")
    if main == "通用":
        warns.append("主阵营不应为通用，建议选德/苏/美/英/日之一")
    curve = [0]*8
    for c in deck:
        curve[min(c["deploy"], 7)] += 1
    low, mid, high = sum(curve[:3]), sum(curve[3:6]), sum(curve[6:])
    if low < 10: warns.append(f"低费牌偏少({low}张)，前期可能卡手")
    if high > 8: warns.append(f"高费牌偏多({high}张)，容易卡费用")
    types = {}
    for c in deck:
        types[c["type"]] = types.get(c["type"], 0) + 1
    if types.get("指令", 0) < 6:
        warns.append(f"指令牌仅 {types.get('指令',0)} 张，解场/补牌能力不足")
    arch = "⚡ 快攻抢脸" if low >= 14 else ("🐢 控制反打" if high >= 7 else "⚖️ 中速抢线")
    return {"errors": errs, "warnings": warns,
            "stats": {"main": main, "ally_count": ally_count, "factions": factions,
                      "curve": curve, "types": types, "low": low, "mid": mid, "high": high},
            "archetype": arch}

def fill_deck(main_faction, ally_faction):
    main_pool = list({c["id"]: c for c in CARDS if c["faction"] == main_faction}.values())
    ally_pool = list({c["id"]: c for c in CARDS if c["faction"] == ally_faction}.values()) if ally_faction else []
    generic_pool = list({c["id"]: c for c in CARDS if c["faction"] == "通用"}.values())
    random.shuffle(main_pool); random.shuffle(ally_pool); random.shuffle(generic_pool)
    deck = []
    def pool_max(pool): return len(pool) * MAX_COPIES
    def add_copies(pool, cap):
        used = 0
        for c in pool:
            if used >= cap: break
            if sum(1 for x in deck if x["id"] == c["id"]) == 0:
                deck.append(c); used += 1
        for _ in range(MAX_COPIES - 1):
            for c in pool:
                if used >= cap: break
                if sum(1 for x in deck if x["id"] == c["id"]) < MAX_COPIES:
                    deck.append(c); used += 1
        return used
    main_quota = min(pool_max(main_pool), 40 - 12)
    add_copies(main_pool, main_quota)
    ally_quota = min(pool_max(ally_pool), 12, 40 - len(deck))
    add_copies(ally_pool, ally_quota)
    add_copies(generic_pool, 40)
    if len(deck) < 40:
        add_copies(main_pool, 40)
    return deck[:40]

# ==================== 战斗模拟 ====================
class BattleState:
    def __init__(self):
        self.my_front = []      # [{name, faction, atk, def, active}]
        self.en_front = []
        self.my_hp = 20
        self.en_hp = 20

    def reset(self):
        self.__init__()

    def toggle(self, side, idx):
        arr = self.my_front if side == "my" else self.en_front
        if 0 <= idx < len(arr):
            arr[idx]["active"] = not arr[idx]["active"]

    def add_friendly(self, card):
        if card["type"] == "指令": return
        self.my_front.append({"name": card["name"], "faction": card["faction"],
                             "atk": card["atk"], "def": card["def"], "active": True})

    def add_enemy(self, card):
        if card["type"] == "指令": return
        self.en_front.append({"name": card["name"], "faction": card["faction"],
                             "atk": card["atk"], "def": card["def"], "active": False})

    def attack(self):
        log = []
        for u in [x for x in self.my_front if x["active"]]:
            if self.en_front:
                t = self.en_front[0]
                log.append(f"{u['name']} 攻击 {t['name']}：{u['atk']} 伤害")
                t["def"] -= u["atk"]
                if t["def"] <= 0:
                    log.append(f"  → {t['name']} 被摧毁！")
                    self.en_front.pop(0)
            else:
                log.append(f"{u['name']} 直击敌方总部：+{u['atk']} 伤害")
                self.en_hp -= u["atk"]
        if self.en_hp <= 0:
            log.append("🏆 己方获胜！")
        return log

# ==================== AI 对战 ====================
def ai_battle(me, enemy, inno=""):
    log = []
    log.append(f"⚔️ 对局开始：玩家【{me}】 vs AI【{enemy}】"
               + (f"  · 创新模式：{inno}" if inno else "  · 原版规则"))
    if inno in ("weather", "all"):
        log.append("🌦️ 天气系统已开启：每 3 回合切换暴雨/浓雾/寒冬/晴天。")
    if inno in ("morale", "all"):
        log.append("🎖️ 士气系统已开启：未占领前线士气 -2，士气归零惩罚行动费。")
    opts = [f for f in FACTIONS if f not in (me, "通用")]
    ally_me = random.choice(opts)
    opts2 = [f for f in FACTIONS if f not in (enemy, "通用")]
    ally_en = random.choice(opts2)
    my_deck = fill_deck(me, ally_me)
    en_deck = fill_deck(enemy, ally_en)
    vm, ve = validate_deck(my_deck), validate_deck(en_deck)
    log.append(f"📋 玩家卡组（{me} + {ally_me}，{len(my_deck)} 张，{vm['archetype']}）")
    log.append(f"📋 AI 卡组（{enemy} + {ally_en}，{len(en_deck)} 张，{ve['archetype']}）")

    my_hp, en_hp = 20, 20
    morale = {"me": 5, "en": 5}
    weather = None
    for turn in range(1, 21):
        if inno in ("weather", "all") and turn % 3 == 0:
            weather = random.choice(["暴雨", "浓雾", "寒冬", "晴天"])
            log.append(f"—— 第 {turn} 回合天气：{weather} ——")
        front = random.choice(["me", "en"])
        if inno in ("morale", "all"):
            if front == "me":
                morale["me"] = min(10, morale["me"] + 1); morale["en"] = max(0, morale["en"] - 2)
            else:
                morale["en"] = min(10, morale["en"] + 1); morale["me"] = max(0, morale["me"] - 2)
            log.append(f"第 {turn} 回合：士气 玩家 {morale['me']} / AI {morale['en']}　前线：{'玩家' if front=='me' else 'AI'}")
        else:
            log.append(f"—— 第 {turn} 回合 ——")
        for owner, hp_ref in (("me", "en_hp"), ("en", "my_hp")):
            play = random.randint(1, 3)
            dmg = 0
            dk = my_deck if owner == "me" else en_deck
            for _ in range(play):
                c = random.choice(dk)
                if c["type"] == "指令":
                    m = re.search(r"造成\s*(\d+)\s*点伤害", c.get("desc", ""))
                    if m: dmg += int(m.group(1))
                else:
                    dmg += c["atk"]
                    if "奋战" in c["kw"]: dmg += c["atk"]
            if owner == "me":
                en_hp = max(0, en_hp - dmg)
                log.append(f"  玩家 出 {play} 张，{dmg} 伤害 → AI 总部 {en_hp}")
            else:
                my_hp = max(0, my_hp - dmg)
                log.append(f"  AI 出 {play} 张，{dmg} 伤害 → 玩家总部 {my_hp}")
        if my_hp == 0 or en_hp == 0:
            break
    if en_hp == 0 and my_hp == 0:
        log.append("🤝 同归于尽！")
    elif en_hp == 0:
        log.append(f"🏆 玩家【{me}】获胜！剩余总部 {my_hp} 点。")
    else:
        log.append(f"💀 AI【{enemy}】获胜！剩余总部 {en_hp} 点。")
    return log

# ==================== 主窗口 ====================
class KardsApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("KARDS 二战卡牌图鉴 & 创新实验室")
        self.resize(1080, 720)
        self.deck = []
        self.battle = BattleState()
        self._setup_ui()
        self._apply_dark_theme()

    def _apply_dark_theme(self):
        self.setStyleSheet("""
            QMainWindow { background: #12181f; }
            QWidget { background: #12181f; color: #e8edf2; font-size: 13px; }
            QTabWidget::pane { border: 1px solid #2e3a47; border-radius: 6px; margin-top: 6px; }
            QTabBar::tab { background: #1a2129; color: #9fb0c0; padding: 9px 20px; border-radius: 5px; margin-right: 3px; }
            QTabBar::tab:selected { background: #e0a800; color: #12181f; font-weight: 700; }
            QPushButton { background: #232c36; color: #e8edf2; border: 1px solid #2e3a47;
                border-radius: 6px; padding: 7px 14px; }
            QPushButton:hover { border-color: #e0a800; }
            QPushButton#primary { background: #e0a800; color: #12181f; font-weight: 700; }
            QPushButton#primary:hover { background: #ffcc33; }
            QLineEdit, QComboBox { background: #232c36; color: #e8edf2; border: 1px solid #2e3a47;
                border-radius: 6px; padding: 6px 10px; }
            QListWidget { background: #1a2129; border: 1px solid #2e3a47; border-radius: 6px; }
            QListWidget::item { padding: 6px; border-bottom: 1px solid #232c36; }
            QListWidget::item:selected { background: #2e3a47; color: #e0a800; }
            QTextEdit { background: #1a2129; border: 1px solid #2e3a47; border-radius: 6px;
                color: #c8d6e2; font-family: "DejaVu Sans Mono", monospace; }
            QGroupBox { border: 1px solid #2e3a47; border-radius: 6px; margin-top: 10px; padding-top: 16px; }
            QGroupBox::title { color: #e0a800; padding: 0 6px; }
            QScrollBar:vertical { background: #1a2129; width: 10px; }
            QScrollBar::handle:vertical { background: #3a4653; border-radius: 5px; }
            QLabel#heroTitle { font-size: 30px; font-weight: 900; color: #e0a800; }
            QLabel#heroSub { color: #9fb0c0; font-size: 15px; }
        """)

    def _setup_ui(self):
        tabs = QTabWidget()
        self.setCentralWidget(tabs)
        tabs.addTab(self._home_tab(), "🏠 主页")
        tabs.addTab(self._rules_tab(), "📖 规则")
        tabs.addTab(self._cards_tab(), "🃏 卡牌库")
        tabs.addTab(self._builder_tab(), "🎯 卡组构建")
        tabs.addTab(self._battle_tab(), "⚔️ 战斗模拟")
        tabs.addTab(self._innovate_tab(), "🧪 创新实验室")

    # ---------- 主页 ----------
    def _home_tab(self):
        w = QWidget()
        v = QVBoxLayout(w)
        v.setContentsMargins(40, 40, 40, 40)
        v.setSpacing(20)
        t = QLabel("指挥你的 钢铁军团\n决胜于三条战线")
        t.setObjectName("heroTitle")
        t.setAlignment(Qt.AlignCenter)
        t.setStyleSheet("font-size: 34px; font-weight: 900; color: #e0a800;")
        sub = QLabel("KARDS 是一款以第二次世界大战为背景的策略卡牌游戏。\n占领前线、调度单位、摧毁敌方总部——用真正的战术智慧，而非单纯比拼卡牌稀有度，来决定胜负。")
        sub.setAlignment(Qt.AlignCenter)
        sub.setStyleSheet("color: #9fb0c0; font-size: 15px;")
        sub.setWordWrap(True)
        v.addWidget(t); v.addWidget(sub)

        grid = QGridLayout()
        grid.setSpacing(14)
        cards = [
            ("🎯 总部决胜", "把敌方总部 20 点防御降到 0 即获胜。"),
            ("🗺️ 三线战场", "支援区 / 前线 / 支援区，抢线才能打脸。"),
            ("🎟️ 双费用系统", "部署费 + 行动费，每动一次都要精打细算。"),
            ("🛡️ 五大阵营", "德 / 苏 / 美 / 英 / 日，各有独门战术。"),
        ]
        for i, (h, p) in enumerate(cards):
            box = QGroupBox(h)
            box.setStyleSheet("QGroupBox { font-weight: 700; }")
            bl = QVBoxLayout(box)
            lab = QLabel(p)
            lab.setStyleSheet("color: #9fb0c0; font-size: 13px;")
            lab.setWordWrap(True)
            bl.addWidget(lab)
            grid.addWidget(box, i // 2, i % 2)
        v.addLayout(grid)

        tip = QLabel("💡 赢的关键一句话：<b>线 = 第三种资源</b>。抢到前线就抢到攻击权，\n这比单纯比拼单卡身材，更贴近真实战场的推进逻辑。")
        tip.setStyleSheet("color: #e0a800; background: #1a2129; border-radius: 6px; padding: 14px;")
        tip.setWordWrap(True)
        v.addWidget(tip)
        v.addStretch()
        return w

    # ---------- 规则 ----------
    def _rules_tab(self):
        w = QWidget()
        v = QVBoxLayout(w)
        v.setContentsMargins(20, 20, 20, 20)
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        inner = QWidget()
        lv = QVBoxLayout(inner)
        lv.setSpacing(14)

        def group(title, items):
            g = QGroupBox(title)
            gl = QVBoxLayout(g)
            for it in items:
                lab = QLabel("• " + it)
                lab.setStyleSheet("color: #c8d6e2; font-size: 13px;")
                lab.setWordWrap(True)
                gl.addWidget(lab)
            return g

        lv.addWidget(group("① 胜利与战场", [
            "目标：把敌方总部防御从 20 降到 0 或以下。",
            "战场三线：己方支援区 → 前线 → 敌方支援区。",
            "前线同时只能由一方占领，最多容纳 5 个单位。",
            "只有占领前线的单位才能攻击敌方总部。",
        ]))
        lv.addWidget(group("② 资源系统", [
            "指挥点：每回合槽位 +1，上限 12；回合开始按槽位补满。",
            "双费用：大数字=部署费，小数字=行动费。移动/攻击都要再付行动费。",
            "手牌上限 9 张，超抽自动弃牌——这是隐性资源。",
            "先手 4 张首回合不抽牌，后手 5 张首回合抽 1 张。",
        ]))
        lv.addWidget(group("③ 卡牌类型", [
            "步兵：便宜守线，吃正面伤害。",
            "坦克：移动+攻击同回合，行动费贵。",
            "火炮：可跨线攻击，身板脆。",
            "战斗机：拦截轰炸机，跨线作战。",
            "轰炸机：打任意线且不被反击，怕战斗机。",
            "指令：一次性生效，部分可反制。",
        ]))
        lv.addWidget(group("④ 卡组与流程", [
            "共 40 张，1 个主阵营 + 1 个盟国，盟国 ≤ 12 张，同名卡 ≤ 3 张（扩编规则）。",
            "起手可调度：先手 4 张，后手 5 张。",
            "回合：抽牌 → 部署 / 移动 / 攻击 → 结束回合。",
            "核心循环：铺场 → 抢线 → 激活 → 打脸/解场，四件事抢同一笔指挥点。",
        ]))

        # 关键词表
        g = QGroupBox("关键机制速查表")
        gl = QVBoxLayout(g)
        tbl = QTableWidget(8, 2)
        tbl.setHorizontalHeaderLabels(["机制", "效果 / 典型单位"])
        rows = [
            ("闪击", "下场当回合即可移动/攻击　闪电突击兵、T-34"),
            ("守护", "相邻非守护单位只能被炮兵/轰炸机打　掷弹兵卫队"),
            ("奋战", "一回合可攻击两次　虎式坦克"),
            ("伏击", "首次被攻击时先手结算伤害"),
            ("压制", "不能移动，拥有者下回合结束解除"),
            ("烟幕", "不能被敌方单位攻击，一动即失去"),
            ("钳击", "与另一友军联动，双方吃加成"),
            ("反制", "藏在手里，对手满足条件时触发　雷达预警"),
        ]
        for i, (a, b) in enumerate(rows):
            tbl.setItem(i, 0, QTableWidgetItem(a))
            tbl.setItem(i, 1, QTableWidgetItem(b))
        tbl.horizontalHeader().setStretchLastSection(True)
        tbl.setMaximumHeight(300)
        gl.addWidget(tbl)
        lv.addWidget(g)
        lv.addStretch()
        scroll.setWidget(inner)
        v.addWidget(scroll)
        return w

    # ---------- 卡牌库 ----------
    def _cards_tab(self):
        w = QWidget()
        v = QVBoxLayout(w)
        v.setContentsMargins(16, 16, 16, 16)
        # 筛选栏
        bar = QHBoxLayout()
        self.f_faction = QComboBox(); self.f_faction.addItem("全部"); self.f_faction.addItems(FACTIONS)
        self.f_type = QComboBox(); self.f_type.addItem("全部"); self.f_type.addItems(TYPES)
        self.f_tier = QComboBox(); self.f_tier.addItem("全部"); self.f_tier.addItems(TIERS)
        self.f_cost = QComboBox(); self.f_cost.addItems(["不限"] + [str(i) for i in range(1, 8)])
        self.f_kw = QLineEdit(); self.f_kw.setPlaceholderText("关键词，如 闪击")
        for c in (self.f_faction, self.f_type, self.f_tier, self.f_cost):
            c.currentIndexChanged.connect(self._render_cards)
        self.f_kw.textChanged.connect(self._render_cards)
        for lbl, wd in (("阵营", self.f_faction), ("类型", self.f_type),
                        ("稀有度", self.f_tier), ("部署费≤", self.f_cost)):
            lab = QLabel(lbl); lab.setStyleSheet("color:#9fb0c0")
            bar.addWidget(lab); bar.addWidget(wd)
        bar.addWidget(QLabel("关键词")); bar.addWidget(self.f_kw)
        bar.addStretch()
        reset = QPushButton("重置"); reset.clicked.connect(self._reset_filters)
        bar.addWidget(reset)
        v.addLayout(bar)

        self.card_list = QListWidget()
        self.card_list.setSpacing(2)
        v.addWidget(self.card_list)
        self._render_cards()
        return w

    def _filtered_cards(self):
        f = self.f_faction.currentText()
        t = self.f_type.currentText()
        r = self.f_tier.currentText()
        mx = self.f_cost.currentText()
        kw = self.f_kw.text().strip().lower()
        mxv = 99 if mx == "不限" else int(mx)
        return [c for c in CARDS if
                (f == "全部" or c["faction"] == f) and
                (t == "全部" or c["type"] == t) and
                (r == "全部" or c["tier"] == r) and
                c["deploy"] <= mxv and
                (not kw or kw in (c["name"] + c["desc"] + "".join(c["kw"])).lower())]

    def _render_cards(self):
        cards = self._filtered_cards()
        self.card_list.clear()
        for c in cards:
            fc = FACTION_COLOR.get(c["faction"], "#555")
            rc = RARITY_COLOR.get(c["tier"], "#9fb0c0")
            stat = f"⚔️{c['atk']} / 🛡️{c['def']}" if c["type"] != "指令" else "指令牌"
            kw = "  ".join("⚡" + k for k in c["kw"])
            text = (f"[{c['faction']}] {c['name']}  ({c['type']})  {c['deploy']}/{c['action']}  {stat}\n"
                    f"     {c['desc']}" + (f"   [{kw}]" if kw else ""))
            item = QListWidgetItem(text)
            item.setForeground(QColor("#c8d6e2"))
            font = QFont(); font.setBold(True); font.setPointSize(12)
            item.setFont(font)
            self.card_list.addItem(item)

    def _reset_filters(self):
        for c in (self.f_faction, self.f_type, self.f_tier):
            c.setCurrentIndex(0)
        self.f_cost.setCurrentIndex(0)
        self.f_kw.clear()
        self._render_cards()

    # ---------- 卡组构建 ----------
    def _builder_tab(self):
        w = QWidget()
        v = QHBoxLayout(w)
        v.setContentsMargins(16, 16, 16, 16)
        # 左：卡池
        left = QVBoxLayout()
        lb = QHBoxLayout()
        self.b_faction = QComboBox(); self.b_faction.addItem("全部"); self.b_faction.addItems(FACTIONS)
        self.b_type = QComboBox(); self.b_type.addItem("全部"); self.b_type.addItems(TYPES)
        self.b_faction.currentIndexChanged.connect(self._render_collection)
        self.b_type.currentIndexChanged.connect(self._render_collection)
        lb.addWidget(QLabel("阵营")); lb.addWidget(self.b_faction)
        lb.addWidget(QLabel("类型")); lb.addWidget(self.b_type)
        left.addLayout(lb)
        self.collection = QListWidget()
        self.collection.itemDoubleClicked.connect(self._add_selected_to_deck)
        left.addWidget(self.collection)
        v.addLayout(left, 1)

        # 右：卡组
        right = QVBoxLayout()
        self.deck_label = QLabel("我的卡组 0/40")
        self.deck_label.setStyleSheet("font-weight:700; color:#e0a800; font-size:15px;")
        right.addWidget(self.deck_label)
        tb = QHBoxLayout()
        for name, fn in (("🎲 随机生成", self._random_deck), ("🗑️ 清空", self._clear_deck),
                         ("📋 导出", self._export_deck), ("➕ 加入选中卡", self._add_selected_to_deck)):
            btn = QPushButton(name)
            if "随机" in name: btn.setObjectName("primary")
            btn.clicked.connect(fn)
            tb.addWidget(btn)
        right.addLayout(tb)
        self.deck_list = QListWidget()
        right.addWidget(self.deck_list, 1)
        self.deck_result = QTextEdit()
        self.deck_result.setReadOnly(True)
        self.deck_result.setMaximumHeight(200)
        right.addWidget(self.deck_result)
        v.addLayout(right, 1)
        self._render_collection()
        self._render_deck()
        return w

    def _render_collection(self):
        f = self.b_faction.currentText()
        t = self.b_type.currentText()
        pool = [c for c in CARDS if (f == "全部" or c["faction"] == f) and (t == "全部" or c["type"] == t)]
        self.collection.clear()
        for c in pool:
            cnt = sum(1 for x in self.deck if x["id"] == c["id"])
            stat = f"⚔️{c['atk']}/🛡️{c['def']}" if c["type"] != "指令" else "指令"
            self.collection.addItem(
                f"{c['name']}  [{c['type']}]  {c['deploy']}/{c['action']}  {stat}  (已有{cnt})")

    def _add_selected_to_deck(self, *args):
        row = self.collection.currentRow()
        if row < 0:
            QMessageBox.information(self, "提示", "请先在左侧卡池选中一张卡")
            return
        f = self.b_faction.currentText()
        t = self.b_type.currentText()
        pool = [c for c in CARDS if (f == "全部" or c["faction"] == f) and (t == "全部" or c["type"] == t)]
        if row >= len(pool): return
        c = pool[row]
        if len(self.deck) >= 40:
            QMessageBox.warning(self, "卡组已满", "卡组已达 40 张")
            return
        if sum(1 for x in self.deck if x["id"] == c["id"]) >= MAX_COPIES:
            QMessageBox.warning(self, "数量上限", f"「{c['name']}」最多 {MAX_COPIES} 张")
            return
        self.deck.append(c)
        self._render_collection()
        self._render_deck()

    def _clear_deck(self):
        self.deck = []
        self._render_collection()
        self._render_deck()

    def _random_deck(self):
        fac = self.b_faction.currentText()
        if fac == "全部": fac = "德国"
        opts = [f for f in FACTIONS if f not in (fac, "通用")]
        ally, ok = QInputDialog.getItem(self, "选择盟国", "盟国阵营（可取消则随机）：",
                                        opts, 0, False)
        if not ok: ally = random.choice(opts)
        self.deck = fill_deck(fac, ally)
        random.shuffle(self.deck)
        self._render_collection()
        self._render_deck()

    def _export_deck(self):
        if not self.deck:
            QMessageBox.warning(self, "卡组为空", "当前没有卡牌可导出")
            return
        path, _ = QFileDialog.getSaveFileName(self, "导出卡组", "kards_deck.txt", "文本文件 (*.txt)")
        if path:
            with open(path, "w", encoding="utf-8") as f:
                f.write("\n".join(c["name"] for c in self.deck))
            QMessageBox.information(self, "已导出", f"已保存 {len(self.deck)} 张卡牌到：\n{path}")

    def _render_deck(self):
        self.deck_label.setText(f"我的卡组 {len(self.deck)}/40")
        self.deck_list.clear()
        for i, c in enumerate(self.deck):
            self.deck_list.addItem(f"{i+1}. {c['name']}  [{c['faction']}]  {c['type']}  {c['deploy']}/{c['action']}")
        res = validate_deck(self.deck)
        html = []
        if res["stats"].get("main"):
            html.append(f"主阵营：<b style='color:#e0a800'>{res['stats']['main']}</b>　"
                        f"盟国卡：<b>{res['stats']['ally_count']}</b>/12")
            curve = res["stats"]["curve"]
            parts = []
            for i, v in enumerate(curve):
                if v: parts.append(f"{i if i < 7 else '7+'}费×{v}")
            html.append(f"费用曲线：L{res['stats']['low']} / M{res['stats']['mid']} / H{res['stats']['high']}　"
                        f"分布：{' '.join(parts)}")
            types = res["stats"]["types"]
            html.append("类型：" + " · ".join(f"{k} {v}" for k, v in types.items()))
        if res["errors"]:
            html.append("<span style='color:#e74c3c'>✖ " + "<br>✖ ".join(res["errors"]) + "</span>")
        else:
            html.append("<span style='color:#4a9c5c'>✔ 卡组合法，可上阵！</span>")
        if res["warnings"]:
            html.append("<span style='color:#e0a800'>⚠ " + "<br>⚠ ".join(res["warnings"]) + "</span>")
        if res.get("archetype"):
            html.append(f"推荐流派：<b style='color:#e0a800'>{res['archetype']}</b>")
        self.deck_result.setHtml("<br>".join(html))

    # ---------- 战斗模拟 ----------
    def _battle_tab(self):
        w = QWidget()
        v = QVBoxLayout(w)
        v.setContentsMargins(16, 16, 16, 16)
        bar = QHBoxLayout()
        self.add_friend_cb = QComboBox()
        self.add_enemy_cb = QComboBox()
        for c in CARDS:
            if c["type"] != "指令":
                self.add_friend_cb.addItem(f"{c['name']}（{c['deploy']}/{c['action']} · {c['atk']}/{c['def']}）", userData=c["id"])
                self.add_enemy_cb.addItem(f"{c['name']}（{c['atk']}/{c['def']}）", userData=c["id"])
        bar.addWidget(QLabel("己方单位"))
        bar.addWidget(self.add_friend_cb)
        btn = QPushButton("➕ 加入己方前线"); btn.clicked.connect(lambda: self.battle.add_friendly(
            get_card(self.add_friend_cb.currentData())))
        bar.addWidget(btn)
        bar.addWidget(QLabel("敌方单位"))
        bar.addWidget(self.add_enemy_cb)
        btn2 = QPushButton("➕ 加入敌方前线"); btn2.clicked.connect(lambda: self.battle.add_enemy(
            get_card(self.add_enemy_cb.currentData())))
        bar.addWidget(btn2)
        bar.addStretch()
        atk = QPushButton("⚔️ 执行攻击"); atk.setObjectName("primary"); atk.clicked.connect(self._do_attack)
        bar.addWidget(atk)
        rst = QPushButton("🔄 重置"); rst.clicked.connect(self._reset_battle)
        bar.addWidget(rst)
        v.addLayout(bar)

        # 战场
        field = QHBoxLayout()
        # 己方
        left = QVBoxLayout()
        self.my_hp_lab = QLabel("20"); self.my_hp_lab.setStyleSheet("font-size:28px;font-weight:900;color:#e0a800;")
        mybox = QGroupBox("己方总部")
        myv = QVBoxLayout(mybox); myv.addWidget(self.my_hp_lab)
        left.addWidget(mybox)
        self.my_front_list = QListWidget()
        self.my_front_list.itemClicked.connect(lambda it: self._toggle_front("my", self.my_front_list.row(it)))
        left.addWidget(self.my_front_list)
        # 敌方
        right = QVBoxLayout()
        self.en_hp_lab = QLabel("20"); self.en_hp_lab.setStyleSheet("font-size:28px;font-weight:900;color:#e0a800;")
        enbox = QGroupBox("敌方总部")
        env = QVBoxLayout(enbox); env.addWidget(self.en_hp_lab)
        right.addWidget(enbox)
        self.en_front_list = QListWidget()
        self.en_front_list.itemClicked.connect(lambda it: self._toggle_front("en", self.en_front_list.row(it)))
        right.addWidget(self.en_front_list)
        field.addLayout(left); field.addWidget(QLabel("⚔️")); field.addLayout(right)
        v.addLayout(field)
        self.battle_log = QTextEdit(); self.battle_log.setReadOnly(True); self.battle_log.setMaximumHeight(180)
        v.addWidget(self.battle_log)
        self._refresh_battle()
        return w

    def _toggle_front(self, side, idx):
        self.battle.toggle(side, idx)
        self._refresh_battle()

    def _do_attack(self):
        log = self.battle.attack()
        self.battle_log.setText("\n".join(log))
        self._refresh_battle()

    def _reset_battle(self):
        self.battle.reset()
        self.battle_log.clear()
        self._refresh_battle()

    def _refresh_battle(self):
        self.my_hp_lab.setText(str(max(self.battle.my_hp, 0)))
        self.en_hp_lab.setText(str(max(self.battle.en_hp, 0)))
        self.my_front_list.clear()
        for i, u in enumerate(self.battle.my_front):
            self.my_front_list.addItem(f"{'🟢' if u['active'] else '⚪'} {u['name']}  ⚔️{u['atk']} 🛡️{u['def']}")
        self.en_front_list.clear()
        for i, u in enumerate(self.battle.en_front):
            self.en_front_list.addItem(f"{'🟢' if u['active'] else '⚪'} {u['name']}  ⚔️{u['atk']} 🛡️{u['def']}")
        if not self.battle.my_front: self.my_front_list.addItem("（空）")
        if not self.battle.en_front: self.en_front_list.addItem("（空）")

    # ---------- 创新实验室 ----------
    def _innovate_tab(self):
        w = QWidget()
        v = QVBoxLayout(w)
        v.setContentsMargins(16, 16, 16, 16)
        scroll = QScrollArea(); scroll.setWidgetResizable(True)
        inner = QWidget(); lv = QVBoxLayout(inner); lv.setSpacing(12)

        ideas = [
            ("🧪 创新一：天气系统（动态战场）",
             ["每 3 回合随机一种天气，持续 2 回合：",
              "暴雨：轰炸机伤害 -1，火炮费用 +1。",
              "浓雾：所有单位获得烟幕，前线不可被指定。",
              "寒冬：步兵行动费 +1，坦克行动费 -1。",
              "晴天：战斗机攻击 +1。"],
             "设计意图：打破固定最优解，让同一套卡组需要不同打法。"),
            ("🧪 创新二：士气值（第二血量）",
             ["双方各有一个 0–10 的士气值。",
              "连续两回合未占领前线则士气 -2；占领则 +1。",
              "士气归零：本回合所有单位行动费 +1，抽牌少 1 张。"],
             "设计意图：把抢线从战术选择升级为资源压力。"),
            ("🧪 创新三：战区事件（PvE 剧情）",
             ["每个战役包（斯大林格勒/诺曼底/阿登）带 5 张事件牌，按回合触发：",
              "「斯大林格勒巷战」第 5 回合起所有步兵 +1/+1。",
              "「敦刻尔克撤退」第 3 回合双方可免费撤退 2 个单位。"],
             "设计意图：用历史叙事增加确定性节点，适合做战役模式。"),
            ("🧪 创新四：装备槽（卡牌成长）",
             ["每张单位卡自带 1 个装备槽，可装武器/装甲/配件：",
              "长 88mm 炮：+2 攻击，行动费 +1。",
              "附加装甲：+3 防御，但不能闪击。",
              "短波电台：相邻单位获得闪击。"],
             "设计意图：在 CCG 里塞进轻量 RPG 成长感。"),
            ("🧪 创新五：将领卡（身份系统）",
             ["卡组可含 1 张不可被解的将领卡（绑定总部），自带被动：",
              "隆美尔「沙漠之狐」：前线坦克 +1 攻击。",
              "朱可夫「防御反击」：每第三个友军死亡抽一张。",
              "蒙哥马利「坚守」：友军获得守护。"],
             "设计意图：天然的内容化钩子，适合做角色介绍。"),
            ("🧪 创新六：情报战（隐藏信息）",
             ["新增情报点（每回合 +1，上限 3），可发动：",
              "侦察：窥视对手 2 张手牌。",
              "误导：本回合一张指令牌对对手显示为另一张。",
              "破译：复制对手上一张使用的指令牌。"],
             "设计意图：把完全信息博弈变成不完全信息博弈。"),
        ]
        for title, pts, intent in ideas:
            g = QGroupBox(title)
            gl = QVBoxLayout(g)
            for p in pts:
                lab = QLabel("• " + p)
                lab.setStyleSheet("color:#c8d6e2; font-size:13px;")
                lab.setWordWrap(True)
                gl.addWidget(lab)
            it = QLabel(intent)
            it.setStyleSheet("color:#e0a800; font-size:13px; font-style:italic;")
            it.setWordWrap(True)
            gl.addWidget(it)
            lv.addWidget(g)

        # AI 对战
        ai_box = QGroupBox("🤖 自制对局 AI 试玩")
        al = QVBoxLayout(ai_box)
        hb = QHBoxLayout()
        self.ai_me = QComboBox(); self.ai_me.addItems([f for f in FACTIONS if f != "通用"])
        self.ai_en = QComboBox(); self.ai_en.addItems([f for f in FACTIONS if f != "通用"])
        self.ai_me.setCurrentText("德国"); self.ai_en.setCurrentText("苏联")
        self.ai_inno = QComboBox(); self.ai_inno.addItems(["", "weather", "morale", "all"])
        self.ai_inno.setItemText(0, "仅原版规则")
        self.ai_inno.setItemText(1, "+ 天气系统"); self.ai_inno.setItemText(2, "+ 士气值")
        self.ai_inno.setItemText(3, "+ 全部创新")
        hb.addWidget(QLabel("玩家")); hb.addWidget(self.ai_me)
        hb.addWidget(QLabel("AI")); hb.addWidget(self.ai_en)
        hb.addWidget(QLabel("创新")); hb.addWidget(self.ai_inno)
        go = QPushButton("▶️ 开始对局"); go.setObjectName("primary")
        go.clicked.connect(self._run_ai)
        hb.addWidget(go)
        hb.addStretch()
        al.addLayout(hb)
        self.ai_log = QTextEdit(); self.ai_log.setReadOnly(True); self.ai_log.setMaximumHeight(260)
        al.addWidget(self.ai_log)
        lv.addWidget(ai_box)
        lv.addStretch()
        scroll.setWidget(inner)
        v.addWidget(scroll)
        return w

    def _run_ai(self):
        self.ai_log.clear()
        log = ai_battle(self.ai_me.currentText(), self.ai_en.currentText(), self.ai_inno.currentText())
        self.ai_log.setText("\n".join(log))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = KardsApp()
    win.show()
    sys.exit(app.exec())
