#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
================================================================================
「西行云途」小程序 —— 兑换码批量生成工具
================================================================================

功能说明：
    本脚本用于批量生成三类兑换码，支持导出 Excel、CSV、SQL 三种格式。
    生成的兑换码为 16 位字母数字混合编码，带类型前缀，全局唯一。

兑换码类型（脱敏映射）：
    - XIANTU  ：仙途勋礼包      前缀 XIANTU_ + 10位随机
    - FANYAN  ：梵言书卷礼包    前缀 FANYAN_ + 10位随机
    - YOULI   ：积分抵扣券      前缀 YOULI_  + 10位随机

输出列：
    兑换码 | 类型 | 奖励内容 | 有效期(天) | 单ID限领 | 全局库存 | 生成时间

使用方法（命令行）：
    # 生成 100 个仙途勋礼包兑换码，输出到 ./output 目录
    python3 兑换码批量生成工具.py --type XIANTU --count 100 --output ./output

    # 生成 50 个梵言书卷礼包，有效期 30 天，单ID限领 2 次，全局库存 500
    python3 兑换码批量生成工具.py --type FANYAN --count 50 --days 30 \\
        --per-id-limit 2 --stock 500 --output ./output

    # 自定义奖励内容（JSON 字符串）
    python3 兑换码批量生成工具.py --type YOULI --count 20 \\
        --reward '{"仙途勋值": 100}' --output ./output

    # 同时生成多种类型（多次运行即可，或使用 --type all）
    python3 兑换码批量生成工具.py --type all --count 30 --output ./output

依赖：
    pip install openpyxl

作者：西行云途工具开发组
================================================================================
"""

import os
import csv
import json
import random
import string
import argparse
from datetime import datetime, timedelta

try:
    from openpyxl import Workbook
    from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
except ImportError:
    print("[警告] 未安装 openpyxl，Excel 导出功能将不可用。")
    print("       请执行: pip install openpyxl")
    Workbook = None


# ============================================================================
# 类型映射表（脱敏映射，不可随意修改）
# ============================================================================
# 前缀映射：原始前缀 -> 脱敏前缀
PREFIX_MAP = {
    "XIANTU": "XIANTU",   # 仙途勋礼包
    "FANYAN": "FANYAN",   # 梵言书卷礼包
    "YOULI":  "YOULI",    # 积分抵扣券
}

# 类型中文名称映射
TYPE_NAME_MAP = {
    "XIANTU": "仙途勋礼包",
    "FANYAN": "梵言书卷礼包",
    "YOULI":  "积分抵扣券",
}

# 各类型默认奖励内容（脱敏后的积分名称）
DEFAULT_REWARD = {
    "XIANTU": {"仙途勋值": 100},      # 仙勋分 -> 仙途勋值
    "FANYAN": {"梵言书卷值": 50},     # 经书分 -> 梵言书卷值
    "YOULI":  {"积分抵扣": 100},       # 积分抵扣券
}

# 随机字符池：大写字母 + 数字（去除易混淆字符 0/O, 1/I/L）
RANDOM_CHARS = string.ascii_uppercase + string.digits
CONFUSING_CHARS = set("0O1IL")
SAFE_CHARS = [c for c in RANDOM_CHARS if c not in CONFUSING_CHARS]


class ExchangeCodeGenerator:
    """
    兑换码生成器类

    封装兑换码的生成、去重、导出（Excel/CSV/SQL）等全部逻辑。
    """

    def __init__(self, code_type="XIANTU", count=10, per_id_limit=1,
                 stock=9999, days=7, reward=None):
        """
        初始化生成器

        参数:
            code_type    : 兑换码类型 XIANTU / FANYAN / YOULI
            count        : 生成数量
            per_id_limit : 单ID限领次数（默认1）
            stock        : 全局库存（默认9999）
            days         : 有效期天数（默认7）
            reward       : 奖励内容字典，如 {"仙途勋值": 100}
                          为 None 时使用该类型的默认奖励
        """
        # 校验类型
        if code_type not in PREFIX_MAP:
            raise ValueError(
                f"不支持的兑换码类型: {code_type}，"
                f"可选值: {list(PREFIX_MAP.keys())}"
            )

        self.code_type = code_type          # 兑换码类型
        self.count = count                  # 生成数量
        self.per_id_limit = per_id_limit    # 单ID限领次数
        self.stock = stock                  # 全局库存
        self.days = days                    # 有效期天数
        self.reward = reward if reward else DEFAULT_REWARD.get(code_type, {})
        self.generate_time = datetime.now() # 生成时间

        # 用于去重的集合
        self._code_set = set()
        # 存储已生成的兑换码记录列表
        self.codes = []

    # ------------------------------------------------------------------
    # 生成单个兑换码
    # ------------------------------------------------------------------
    def _generate_single_code(self):
        """
        生成单个 16 位兑换码

        格式: 前缀(7位) + 下划线(1位) + 随机字符(8位)
        总长度 = 7 + 1 + 8 = 16 位

        返回:
            str: 生成的兑换码
        """
        prefix = PREFIX_MAP[self.code_type]  # 如 XIANTU
        # 生成 8 位随机字符（实际总长度 16）
        random_part = "".join(random.choices(SAFE_CHARS, k=8))
        return f"{prefix}_{random_part}"

    # ------------------------------------------------------------------
    # 批量生成兑换码
    # ------------------------------------------------------------------
    def generate_codes(self):
        """
        批量生成兑换码，使用 set 去重确保唯一性

        返回:
            list: 生成的兑换码记录列表，每条记录为字典
        """
        generated = 0
        # 防止死循环：最多尝试 count * 10 次
        max_attempts = self.count * 10
        attempts = 0

        while generated < self.count and attempts < max_attempts:
            attempts += 1
            code = self._generate_single_code()

            # 去重检查
            if code in self._code_set:
                continue

            # 记录该兑换码
            self._code_set.add(code)
            record = {
                "兑换码": code,
                "类型": TYPE_NAME_MAP[self.code_type],
                "类型代码": self.code_type,
                "奖励内容": json.dumps(self.reward, ensure_ascii=False),
                "有效期(天)": self.days,
                "单ID限领": self.per_id_limit,
                "全局库存": self.stock,
                "生成时间": self.generate_time.strftime("%Y-%m-%d %H:%M:%S"),
                # 过期时间（用于 SQL 插入）
                "过期时间": (self.generate_time + timedelta(days=self.days)
                             ).strftime("%Y-%m-%d %H:%M:%S"),
            }
            self.codes.append(record)
            generated += 1

        if generated < self.count:
            print(f"[警告] 仅成功生成 {generated}/{self.count} 个兑换码，"
                  f"可能因随机空间不足导致重复率过高。")

        return self.codes

    # ------------------------------------------------------------------
    # 导出 Excel
    # ------------------------------------------------------------------
    def export_to_excel(self, output_dir):
        """
        将生成的兑换码导出为 Excel 文件

        参数:
            output_dir: 输出目录

        返回:
            str: 生成的 Excel 文件路径
        """
        if Workbook is None:
            print("[错误] openpyxl 未安装，无法导出 Excel。")
            return None

        # 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)

        # 创建工作簿
        wb = Workbook()
        ws = wb.active
        ws.title = "兑换码列表"

        # ---- 表头 ----
        headers = ["兑换码", "类型", "奖励内容", "有效期(天)",
                   "单ID限领", "全局库存", "生成时间"]
        ws.append(headers)

        # 表头样式：加粗、居中、浅蓝底色
        header_font = Font(bold=True, size=11, color="FFFFFF")
        header_fill = PatternFill(start_color="4472C4",
                                  end_color="4472C4", fill_type="solid")
        center_align = Alignment(horizontal="center", vertical="center")
        thin_border = Border(
            left=Side(style="thin"),
            right=Side(style="thin"),
            top=Side(style="thin"),
            bottom=Side(style="thin"),
        )

        for col_idx in range(1, len(headers) + 1):
            cell = ws.cell(row=1, column=col_idx)
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = center_align
            cell.border = thin_border

        # ---- 数据行 ----
        for record in self.codes:
            row_data = [
                record["兑换码"],
                record["类型"],
                record["奖励内容"],
                record["有效期(天)"],
                record["单ID限领"],
                record["全局库存"],
                record["生成时间"],
            ]
            ws.append(row_data)

        # ---- 列宽自适应 ----
        col_widths = [25, 16, 35, 12, 12, 12, 22]
        for i, width in enumerate(col_widths, 1):
            ws.column_dimensions[chr(64 + i)].width = width

        # 冻结首行
        ws.freeze_panes = "A2"

        # ---- 保存文件 ----
        timestamp = self.generate_time.strftime("%Y%m%d_%H%M%S")
        filename = f"兑换码_{self.code_type}_{timestamp}.xlsx"
        filepath = os.path.join(output_dir, filename)
        wb.save(filepath)

        print(f"[Excel] 已导出: {filepath}（共 {len(self.codes)} 条）")
        return filepath

    # ------------------------------------------------------------------
    # 导出 CSV
    # ------------------------------------------------------------------
    def export_to_csv(self, output_dir):
        """
        将生成的兑换码导出为 CSV 文件

        参数:
            output_dir: 输出目录

        返回:
            str: 生成的 CSV 文件路径
        """
        os.makedirs(output_dir, exist_ok=True)

        timestamp = self.generate_time.strftime("%Y%m%d_%H%M%S")
        filename = f"兑换码_{self.code_type}_{timestamp}.csv"
        filepath = os.path.join(output_dir, filename)

        # 使用 utf-8-sig 编码，确保 Excel 打开不乱码
        headers = ["兑换码", "类型", "奖励内容", "有效期(天)",
                   "单ID限领", "全局库存", "生成时间"]

        with open(filepath, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=headers)
            writer.writeheader()
            for record in self.codes:
                writer.writerow({h: record[h] for h in headers})

        print(f"[CSV]   已导出: {filepath}（共 {len(self.codes)} 条）")
        return filepath

    # ------------------------------------------------------------------
    # 导出 SQL
    # ------------------------------------------------------------------
    def export_to_sql(self, output_dir, table_name="t_exchange_code"):
        """
        将生成的兑换码导出为 SQL INSERT 语句文件

        参数:
            output_dir: 输出目录
            table_name: 数据库表名（默认 t_exchange_code）

        返回:
            str: 生成的 SQL 文件路径
        """
        os.makedirs(output_dir, exist_ok=True)

        timestamp = self.generate_time.strftime("%Y%m%d_%H%M%S")
        filename = f"兑换码_{self.code_type}_{timestamp}.sql"
        filepath = os.path.join(output_dir, filename)

        with open(filepath, "w", encoding="utf-8") as f:
            # 写入文件头注释
            f.write("-- ============================================================\n")
            f.write(f"-- 西行云途 · 兑换码批量生成 SQL 脚本\n")
            f.write(f"-- 类型: {self.code_type}（{TYPE_NAME_MAP[self.code_type]}）\n")
            f.write(f"-- 数量: {len(self.codes)}\n")
            f.write(f"-- 生成时间: {self.generate_time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write("-- ============================================================\n\n")

            # 逐条生成 INSERT 语句
            for record in self.codes:
                sql = (
                    f"INSERT INTO `{table_name}` "
                    f"(`code`, `type`, `type_code`, `reward_content`, "
                    f"`valid_days`, `per_id_limit`, `total_stock`, "
                    f"`expire_time`, `create_time`) "
                    f"VALUES ("
                    f"'{record['兑换码']}', "
                    f"'{record['类型']}', "
                    f"'{record['类型代码']}', "
                    f"'{record['奖励内容']}', "
                    f"{record['有效期(天)']}, "
                    f"{record['单ID限领']}, "
                    f"{record['全局库存']}, "
                    f"'{record['过期时间']}', "
                    f"'{record['生成时间']}'"
                    f");\n"
                )
                f.write(sql)

        print(f"[SQL]   已导出: {filepath}（共 {len(self.codes)} 条）")
        return filepath

    # ------------------------------------------------------------------
    # 一键导出全部格式
    # ------------------------------------------------------------------
    def export_all(self, output_dir):
        """
        同时导出 Excel、CSV、SQL 三种格式

        参数:
            output_dir: 输出目录

        返回:
            dict: 各格式文件路径
        """
        results = {}
        results["excel"] = self.export_to_excel(output_dir)
        results["csv"] = self.export_to_csv(output_dir)
        results["sql"] = self.export_to_sql(output_dir)
        return results


# ============================================================================
# 命令行入口
# ============================================================================
def main():
    """
    命令行主函数，解析参数并执行兑换码生成与导出
    """
    parser = argparse.ArgumentParser(
        description="「西行云途」兑换码批量生成工具",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  # 生成 100 个仙途勋礼包
  python3 兑换码批量生成工具.py --type XIANTU --count 100 --output ./output

  # 生成 50 个梵言书卷礼包，有效期 30 天
  python3 兑换码批量生成工具.py --type FANYAN --count 50 --days 30 --output ./output

  # 自定义奖励内容
  python3 兑换码批量生成工具.py --type YOULI --count 20 \\
      --reward '{"积分抵扣": 200}' --output ./output

  # 生成全部三种类型各 30 个
  python3 兑换码批量生成工具.py --type all --count 30 --output ./output
        """,
    )

    parser.add_argument(
        "--type", "-t",
        required=True,
        choices=["XIANTU", "FANYAN", "YOULI", "all"],
        help="兑换码类型: XIANTU(仙途勋礼包) / FANYAN(梵言书卷礼包) / "
             "YOULI(积分抵扣券) / all(全部三种)",
    )
    parser.add_argument(
        "--count", "-n",
        type=int,
        default=10,
        help="生成数量（默认10）",
    )
    parser.add_argument(
        "--per-id-limit",
        type=int,
        default=1,
        help="单ID限领次数（默认1）",
    )
    parser.add_argument(
        "--stock", "-s",
        type=int,
        default=9999,
        help="全局库存（默认9999）",
    )
    parser.add_argument(
        "--days", "-d",
        type=int,
        default=7,
        help="有效期天数（默认7）",
    )
    parser.add_argument(
        "--reward", "-r",
        type=str,
        default=None,
        help='奖励内容 JSON 字符串，如 \'{"仙途勋值": 100}\'',
    )
    parser.add_argument(
        "--output", "-o",
        type=str,
        default="./output",
        help="输出目录（默认 ./output）",
    )
    parser.add_argument(
        "--table",
        type=str,
        default="t_exchange_code",
        help="SQL 表名（默认 t_exchange_code）",
    )

    args = parser.parse_args()

    # 解析奖励内容 JSON
    reward = None
    if args.reward:
        try:
            reward = json.loads(args.reward)
        except json.JSONDecodeError as e:
            print(f"[错误] 奖励内容 JSON 解析失败: {e}")
            return

    # 确定要生成的类型列表
    if args.type == "all":
        types_to_generate = ["XIANTU", "FANYAN", "YOULI"]
    else:
        types_to_generate = [args.type]

    print("=" * 60)
    print("  西行云途 · 兑换码批量生成工具")
    print("=" * 60)
    print(f"  类型:       {', '.join(types_to_generate)}")
    print(f"  每类数量:   {args.count}")
    print(f"  单ID限领:   {args.per_id_limit}")
    print(f"  全局库存:   {args.stock}")
    print(f"  有效期:     {args.days} 天")
    if reward:
        print(f"  自定义奖励: {reward}")
    print(f"  输出目录:   {args.output}")
    print("=" * 60)

    # 逐个类型生成
    for code_type in types_to_generate:
        print(f"\n>>> 正在生成 [{TYPE_NAME_MAP[code_type]}] 兑换码...")

        # 每种类型使用各自的默认奖励（除非用户指定了 reward）
        type_reward = reward if reward else None

        # 创建生成器实例
        generator = ExchangeCodeGenerator(
            code_type=code_type,
            count=args.count,
            per_id_limit=args.per_id_limit,
            stock=args.stock,
            days=args.days,
            reward=type_reward,
        )

        # 生成兑换码
        generator.generate_codes()

        # 导出全部格式
        generator.export_all(args.output)

    print("\n" + "=" * 60)
    print("  全部生成完毕！文件已保存至:", args.output)
    print("=" * 60)


# ============================================================================
# 脚本入口
# ============================================================================
if __name__ == "__main__":
    main()
