"""
授权与秘钥验证模块 — 单机版 v1.3.0
- Ed25519 非对称签名验证
- 试用码本地生成（HMAC-SHA256，无需私钥）
- 机器码采集（硬件指纹）
- 激活状态管理（三保险：SQLite + 注册表 + .license 文件）
- 序列号格式: ZFZS-{机器码12位}-{元数据8位}-{签名8位}
- 试用码格式: ZFZS-{机器码12位}-0601001E-{校验码8位}
"""
import hashlib
import json
import os
import subprocess
import sys
import logging
from datetime import date, datetime, timedelta
from typing import Optional, Tuple
from .usb_mode import is_usb_mode

logger = logging.getLogger(__name__)

# ── 常量 ──
PRODUCT_ID = "ZFZS"
EPOCH_DATE = date(2026, 1, 1)  # 有效期计算基准日
TRIAL_DAYS = 30  # 试用天数

# ── Ed25519 公钥（内嵌到 EXE，仅用于验证，无法伪造） ──
# 厂家持有私钥，此处只有公钥
# 首次使用需运行 tools/license_generator.py 生成密钥对
_PUBLIC_KEY_PEM = """-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAXtZCxzhkU0bThuh5UE0+4kbszCMLZ0Ux+KPwAZTbUpc=
-----END PUBLIC KEY-----"""

# ── 类型映射 ──
LICENSE_TYPE_MAP = {
    "01": "永久授权",
    "02": "1年授权",
    "03": "2年授权",
    "04": "3年授权",
    "05": "按月授权",
    "06": "试用",
}

# ── 试用码常量 ──
TRIAL_TYPE_CODE = "06"
TRIAL_METADATA = "0601001E"  # 06=试用, 01=版本, 001E=30天

# ── 三保险存储路径 ──
_REG_KEY_PATH = r"Software\ZFZS"
_REG_VALUE_NAME = "LicenseInfo"


def _get_license_file_path() -> str:
    """获取 .license 文件路径"""
    from . import paths
    return os.path.join(paths.get_exe_dir(), "data", ".license")


def _get_db_config_key() -> str:
    return "license_info"


# ═══════════════════════════════════════════════════
#  硬件指纹采集
# ═══════════════════════════════════════════════════

def get_machine_code() -> str:
    """获取本机硬件指纹（12位 hex）"""
    parts = []

    # 1. 系统盘卷序列号
    try:
        result = subprocess.run(
            ["cmd", "/c", "vol", "C:"],
            capture_output=True, text=True, encoding="gbk", timeout=5,
            creationflags=0x08000000,
        )
        for line in result.stdout.splitlines():
            if "序列号" in line or "Serial" in line:
                serial = line.strip().split()[-1].replace("-", "").replace(" ", "")
                if serial:
                    parts.append(f"VOL:{serial}")
                break
    except Exception:
        pass

    # 2. 主板 UUID
    try:
        result = subprocess.run(
            ["wmic", "csproduct", "get", "uuid"],
            capture_output=True, text=True, encoding="gbk", timeout=5,
            creationflags=0x08000000,
        )
        for line in result.stdout.splitlines():
            line = line.strip()
            if line and line.lower() != "uuid":
                parts.append(f"UUID:{line}")
                break
    except Exception:
        pass

    # 3. 第一块物理网卡 MAC
    try:
        result = subprocess.run(
            ["wmic", "nic", "where", "PhysicalAdapter=True", "get", "MACAddress"],
            capture_output=True, text=True, encoding="gbk", timeout=5,
            creationflags=0x08000000,
        )
        for line in result.stdout.splitlines():
            line = line.strip()
            if line and line.lower() != "macaddress":
                mac = line.replace(":", "").replace("-", "")
                parts.append(f"MAC:{mac}")
                break
    except Exception:
        pass

    # 降级方案
    if not parts:
        import uuid
        parts.append(f"FALLBACK:{uuid.getnode()}")

    raw = "|".join(parts)
    return hashlib.sha256(raw.encode()).hexdigest()[:12].upper()


def get_usb_fingerprint() -> str:
    """
    获取 U 盘硬件指纹（12位 hex）
    通过 WMI 查询 EXE 所在盘对应的物理磁盘序列号
    降级方案：使用卷序列号（vol 命令）
    """
    if not getattr(sys, 'frozen', False):
        return get_machine_code()  # 开发模式降级

    exe_dir = os.path.dirname(sys.executable)
    drive_letter = os.path.splitdrive(exe_dir)[0]  # 如 "E:"
    drive_idx = drive_letter.rstrip(":")  # 如 "E"

    parts = []

    # 方案1: WMI 查询物理磁盘序列号（最稳定）
    try:
        result = subprocess.run(
            ["wmic", "diskdrive", "where", f"MediaType='Removable Media'", "get", "SerialNumber,Index"],
            capture_output=True, text=True, encoding="gbk", timeout=5,
            creationflags=0x08000000,
        )
        lines = [l.strip() for l in result.stdout.splitlines() if l.strip() and "serial" not in l.lower()]
        for line in lines:
            # 格式: "序列号    索引"
            tokens = line.split()
            if tokens and tokens[0]:
                parts.append(f"USB:{tokens[0]}")
                break
    except Exception:
        pass

    # 方案2: 卷序列号（降级方案，换台电脑盘符可能变但序列号不变）
    if not parts:
        try:
            result = subprocess.run(
                ["cmd", "/c", "vol", drive_letter],
                capture_output=True, text=True, encoding="gbk", timeout=5,
                creationflags=0x08000000,
            )
            for line in result.stdout.splitlines():
                if "序列号" in line or "Serial" in line:
                    serial = line.strip().split()[-1].replace("-", "").replace(" ", "")
                    if serial:
                        parts.append(f"VOL:{serial}")
                    break
        except Exception:
            pass

    # 最终降级: 用 EXE 所在路径（不稳定，仅保底）
    if not parts:
        parts.append(f"PATH:{exe_dir}")

    raw = "|".join(parts)
    return hashlib.sha256(raw.encode()).hexdigest()[:12].upper()


def get_machine_code_unified() -> str:
    """
    统一入口：根据运行模式返回对应的机器码/U盘码
    - U盘模式: U 盘硬件指纹（跟随 U 盘，跨机器不变）
    - 单机模式: 宿主机硬件指纹（跟随电脑）
    """
    if is_usb_mode():
        return get_usb_fingerprint()
    return get_machine_code()


# ═══════════════════════════════════════════════════
#  Ed25519 签名验证
# ═══════════════════════════════════════════════════

def _get_public_key():
    """加载 Ed25519 公钥"""
    from cryptography.hazmat.primitives.serialization import load_pem_public_key
    return load_pem_public_key(_PUBLIC_KEY_PEM.encode())


def _verify_signature(payload: str, signature_hex: str) -> bool:
    """验证 Ed25519 签名"""
    try:
        public_key = _get_public_key()
        # 签名在序列号中是 8 位 hex（前 4 字节的 hex 表示）
        # 实际完整签名是 64 字节，这里我们用完整签名做验证
        # 生成工具端保存完整签名，序列号中只放前 8 位 hex 用于用户输入
        # 验证时需要从数据库或激活记录中获取完整签名
        sig_bytes = bytes.fromhex(signature_hex)
        public_key.verify(sig_bytes, payload.encode("utf-8"))
        return True
    except Exception:
        return False


def _sign_payload(payload: str, private_key_pem: str) -> str:
    """用私钥签名（仅厂家端使用，EXE 中不调用）"""
    from cryptography.hazmat.primitives.serialization import load_pem_private_key
    private_key = load_pem_private_key(private_key_pem.encode(), password=None)
    signature = private_key.sign(payload.encode("utf-8"))
    return signature.hex()


# ═══════════════════════════════════════════════════
#  序列号验证
# ═══════════════════════════════════════════════════

def parse_license_key(license_key: str) -> Optional[dict]:
    """
    解析序列号，返回各段信息
    新格式: ZFZS-{机器码}-{元数据}-{完整签名128位hex}
    旧格式: ZFZS-{机器码}-{元数据}-{签名前缀8位hex}
    """
    key = license_key.strip().upper()
    parts = key.split("-")
    if len(parts) != 4:
        return None
    if len(parts[0]) != 4 or len(parts[1]) != 12 or len(parts[2]) != 8:
        return None
    sig_part = parts[3]
    # 8位=旧格式(签名前缀)，128位=新格式(完整签名)
    if len(sig_part) not in (8, 128):
        return None
    is_full_sig = len(sig_part) == 128
    return {
        "product_id": parts[0],
        "machine_code": parts[1],
        "metadata": parts[2],
        "signature_prefix": sig_part[:8],
        "full_signature": sig_part if is_full_sig else None,
        "raw": key,
    }


def decode_metadata(metadata: str) -> dict:
    """
    解码元数据段
    格式: VVTTDDDD
    """
    if len(metadata) != 8:
        return {}
    version = metadata[:2]
    license_type = metadata[2:4]
    expire_code = metadata[4:8]

    if expire_code == "FFFF":
        expire_date = "2099-12-31"
        is_permanent = True
    else:
        try:
            days = int(expire_code, 16)
            expire_dt = date.fromordinal(EPOCH_DATE.toordinal() + days)
            expire_date = expire_dt.isoformat()
            is_permanent = False
        except (ValueError, OverflowError):
            expire_date = "unknown"
            is_permanent = False

    return {
        "version": f"v{version}" if version.isdigit() else version,
        "type_code": license_type,
        "type_name": LICENSE_TYPE_MAP.get(license_type, "未知"),
        "expire_code": expire_code,
        "expire_date": expire_date,
        "is_permanent": is_permanent,
    }


def validate_license_key(license_key: str, machine_code: str) -> Tuple[bool, str, Optional[dict]]:
    """
    验证序列号
    返回: (是否有效, 消息, 授权信息)
    """
    parsed = parse_license_key(license_key)
    if not parsed:
        return False, "序列号格式错误，正确格式: ZFZS-XXXXXXXXXXXX-XXXXXXXX-XXXXXXXX", None

    # 1. 产品标识校验
    if parsed["product_id"] != PRODUCT_ID:
        return False, "产品标识不正确", None

    # 2. 机器码校验
    if parsed["machine_code"] != machine_code.upper():
        return False, "序列号与本机不匹配，请确认机器码正确", None

    # 3. 元数据解码
    meta = decode_metadata(parsed["metadata"])
    if not meta:
        return False, "序列号元数据错误", None

    # 4. 有效期校验
    if not meta["is_permanent"]:
        try:
            expire_dt = date.fromisoformat(meta["expire_date"])
            if date.today() > expire_dt:
                return False, f"序列号已过期（{meta['expire_date']}）", None
        except ValueError:
            return False, "序列号有效期格式错误", None

    # 5. 签名校验
    payload = f"ZFZS|{parsed['machine_code']}|{parsed['metadata']}"

    # 优先从激活码内嵌的完整签名验证（新格式）
    if parsed.get("full_signature"):
        if _verify_signature(payload, parsed["full_signature"]):
            return True, "激活成功", {
                "version": meta["version"],
                "type": meta["type_name"],
                "expire_date": meta["expire_date"],
                "is_permanent": meta["is_permanent"],
            }
        else:
            return False, "激活码签名验证失败，可能已被篡改", None

    # 其次从已保存的完整签名中验证
    saved_info = read_license_info()
    if saved_info and saved_info.get("full_signature"):
        if _verify_signature(payload, saved_info["full_signature"]):
            return True, "激活成功", {
                "version": meta["version"],
                "type": meta["type_name"],
                "expire_date": meta["expire_date"],
                "is_permanent": meta["is_permanent"],
            }

    # 旧格式且无保存签名：拒绝激活（防止伪造）
    logger.warning("旧格式激活码无完整签名，拒绝激活")
    return False, "旧格式激活码需要完整签名验证，请使用新格式激活码或联系厂家获取", None


def validate_license_key_with_full_sig(license_key: str, machine_code: str, full_signature_hex: str) -> Tuple[bool, str, Optional[dict]]:
    """
    使用完整签名验证序列号（激活时调用）
    """
    parsed = parse_license_key(license_key)
    if not parsed:
        return False, "序列号格式错误", None

    if parsed["product_id"] != PRODUCT_ID:
        return False, "产品标识不正确", None

    if parsed["machine_code"] != machine_code.upper():
        return False, "序列号与本机不匹配", None

    meta = decode_metadata(parsed["metadata"])
    if not meta:
        return False, "序列号元数据错误", None

    if not meta["is_permanent"]:
        try:
            expire_dt = date.fromisoformat(meta["expire_date"])
            if date.today() > expire_dt:
                return False, f"序列号已过期（{meta['expire_date']}）", None
        except ValueError:
            return False, "序列号有效期格式错误", None

    # 完整签名验证
    payload = f"ZFZS|{parsed['machine_code']}|{parsed['metadata']}"
    if not _verify_signature(payload, full_signature_hex):
        return False, "序列号签名验证失败，可能已被篡改", None

    return True, "激活成功", {
        "version": meta["version"],
        "type": meta["type_name"],
        "expire_date": meta["expire_date"],
        "is_permanent": meta["is_permanent"],
    }


# ═══════════════════════════════════════════════════
#  三保险存储
# ═══════════════════════════════════════════════════

def _encode_data(data: dict) -> str:
    """将数据编码为 Base64 字符串"""
    import base64
    return base64.b64encode(json.dumps(data, ensure_ascii=False).encode()).decode()


def _decode_data(encoded: str) -> Optional[dict]:
    """从 Base64 字符串解码数据"""
    import base64
    try:
        return json.loads(base64.b64decode(encoded).decode())
    except Exception:
        return None


def _write_to_registry(data: dict):
    """写入 Windows 注册表"""
    if sys.platform != "win32":
        return
    try:
        import winreg
        key = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, _REG_KEY_PATH, 0, winreg.KEY_SET_VALUE)
        winreg.SetValueEx(key, _REG_VALUE_NAME, 0, winreg.REG_SZ, _encode_data(data))
        winreg.CloseKey(key)
    except Exception as e:
        logger.warning(f"写入注册表失败: {e}")


def _read_from_registry() -> Optional[dict]:
    """从 Windows 注册表读取"""
    if sys.platform != "win32":
        return None
    try:
        import winreg
        key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, _REG_KEY_PATH, 0, winreg.KEY_READ)
        value, _ = winreg.QueryValueEx(key, _REG_VALUE_NAME)
        winreg.CloseKey(key)
        return _decode_data(value)
    except Exception:
        return None


def _write_to_file(data: dict):
    """写入 .license 文件"""
    try:
        filepath = _get_license_file_path()
        os.makedirs(os.path.dirname(filepath), exist_ok=True)
        with open(filepath, "w", encoding="utf-8") as f:
            f.write(_encode_data(data))
        # U盘模式：同时写入备份文件
        if is_usb_mode():
            bak_path = filepath + ".bak"
            with open(bak_path, "w", encoding="utf-8") as f:
                f.write(_encode_data(data))
    except Exception as e:
        logger.warning(f"写入 .license 文件失败: {e}")


def _read_from_file() -> Optional[dict]:
    """从 .license 文件读取（U盘模式同时检查备份）"""
    try:
        filepath = _get_license_file_path()
        # 主文件
        if os.path.exists(filepath):
            with open(filepath, "r", encoding="utf-8") as f:
                result = _decode_data(f.read().strip())
                if result:
                    return result
        # U盘模式：尝试备份文件
        if is_usb_mode():
            bak_path = filepath + ".bak"
            if os.path.exists(bak_path):
                with open(bak_path, "r", encoding="utf-8") as f:
                    result = _decode_data(f.read().strip())
                    if result:
                        # 恢复主文件
                        _write_to_file(result)
                        logger.info("从 .license.bak 恢复授权信息")
                        return result
    except Exception:
        pass
    return None


async def _write_to_db(data: dict):
    """写入 SQLite runtime.db system_configs"""
    try:
        from .database import get_runtime_db
        async for conn, cur in get_runtime_db():
            await cur.execute(
                """INSERT INTO system_configs (config_key, config_value, updated_at)
                   VALUES (?, ?, datetime('now'))
                   ON CONFLICT(config_key) DO UPDATE SET config_value = ?, updated_at = datetime('now')""",
                (_get_db_config_key(), json.dumps(data, ensure_ascii=False), json.dumps(data, ensure_ascii=False)),
            )
            await conn.commit()
            break
    except Exception as e:
        logger.warning(f"写入 runtime.db 失败: {e}")


async def _read_from_db() -> Optional[dict]:
    """从 SQLite runtime.db system_configs 读取"""
    try:
        from .database import get_runtime_db
        async for conn, cur in get_runtime_db():
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = ?",
                (_get_db_config_key(),),
            )
            row = await cur.fetchone()
            if row:
                val = row["config_value"] if isinstance(row, dict) else row[0]
                return json.loads(val) if isinstance(val, str) else val
            break
    except Exception:
        pass
    return None


def write_license_info(data: dict):
    """同步写入存储（U盘模式跳过注册表）"""
    if not is_usb_mode():
        _write_to_registry(data)
    _write_to_file(data)
    # 数据库写入是异步的，需要在调用处单独处理


async def write_license_info_async(data: dict):
    """异步写入存储（U盘模式跳过注册表，包含数据库）"""
    if not is_usb_mode():
        _write_to_registry(data)
    _write_to_file(data)
    await _write_to_db(data)


def read_license_info() -> Optional[dict]:
    """读取授权信息（同步，不含数据库；U盘模式跳过注册表）"""
    reg_data = _read_from_registry() if not is_usb_mode() else None
    file_data = _read_from_file()

    # 两处都有数据，取 activate_time 更早的
    if reg_data and file_data:
        t1 = reg_data.get("activate_time", reg_data.get("trial_start", ""))
        t2 = file_data.get("activate_time", file_data.get("trial_start", ""))
        return reg_data if t1 <= t2 else file_data

    return reg_data or file_data


async def read_license_info_full() -> Optional[dict]:
    """读取授权信息（异步，含数据库；U盘模式跳过注册表，双保险）"""
    db_data = await _read_from_db()
    reg_data = _read_from_registry() if not is_usb_mode() else None
    file_data = _read_from_file()

    candidates = [d for d in [db_data, reg_data, file_data] if d is not None]

    if not candidates:
        return None

    # 取 activate_time 或 trial_start 最早的（防篡改）
    def get_time(d):
        return d.get("activate_time", d.get("trial_start", "9999-99-99"))

    best = min(candidates, key=get_time)

    # 修复不一致的存储
    if db_data != best:
        await _write_to_db(best)
    if reg_data != best:
        _write_to_registry(best)
    if file_data != best:
        _write_to_file(best)

    return best


# ═══════════════════════════════════════════════════
#  重装恢复：从注册表恢复激活状态
# ═══════════════════════════════════════════════════

async def _recover_from_registry_if_needed():
    """
    重装场景：数据库为空但注册表有激活信息 → 自动恢复
    这样用户重装后不需要重新激活
    """
    try:
        from .database import get_runtime_db

        # 检查 runtime.db 中是否有激活信息
        async for conn, cur in get_runtime_db():
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = ?",
                (_get_db_config_key(),),
            )
            db_row = await cur.fetchone()
            if db_row:
                return  # 数据库已有激活信息，无需恢复

            # 数据库无激活信息，检查注册表
            reg_data = _read_from_registry()
            if not reg_data or not reg_data.get("activated"):
                return  # 注册表也没有激活信息

            # 注册表有激活信息，恢复到数据库
            logger.info("检测到注册表中有激活信息，正在恢复...")
            await _write_to_db(reg_data)

            # 同时恢复到 .license 文件
            _write_to_file(reg_data)

            logger.info("激活信息已从注册表恢复")
            break
    except Exception as e:
        logger.warning(f"从注册表恢复激活信息失败: {e}")


# ═══════════════════════════════════════════════════
#  试用期管理
# ═══════════════════════════════════════════════════

async def check_trial_status() -> dict:
    """
    检查试用/激活状态
    返回: {can_use, reason, days_left, message, activated, is_trial, trial_available, license_info}
    """
    # 检查是否需要从注册表恢复（重装场景）
    await _recover_from_registry_if_needed()

    info = await read_license_info_full()

    # 检查试用是否可用（未使用过）
    trial_available = await _check_trial_available()

    # 已激活
    if info and info.get("activated"):
        license_info = info.get("license_info", {})
        expire_date = license_info.get("expire_date")
        is_trial = license_info.get("type_code") == TRIAL_TYPE_CODE

        # 永久授权
        if license_info.get("is_permanent") or expire_date == "2099-12-31":
            return {
                "can_use": True,
                "reason": "activated_permanent",
                "days_left": -1,
                "message": "已激活（永久授权）",
                "activated": True,
                "is_trial": False,
                "trial_available": trial_available,
                "license_info": license_info,
            }

        # 有期限授权（含试用）
        if expire_date:
            try:
                expire_dt = date.fromisoformat(expire_date)
                days_left = (expire_dt - date.today()).days
                if days_left > 0:
                    return {
                        "can_use": True,
                        "reason": "trial_active" if is_trial else "activated_active",
                        "days_left": days_left,
                        "message": f"试用期剩余 {days_left} 天" if is_trial else f"已激活，剩余 {days_left} 天",
                        "activated": True,
                        "is_trial": is_trial,
                        "trial_available": trial_available,
                        "license_info": license_info,
                    }
                else:
                    return {
                        "can_use": False,
                        "reason": "trial_expired" if is_trial else "activated_expired",
                        "days_left": 0,
                        "message": "试用期已结束，请购买正式授权" if is_trial else f"授权已过期（{expire_date}），请联系厂家续费",
                        "activated": True,
                        "is_trial": is_trial,
                        "trial_available": False,  # 试用已用完
                        "license_info": license_info,
                    }
            except ValueError:
                pass

    # 未激活
    return {
        "can_use": False,
        "reason": "not_activated",
        "days_left": 0,
        "message": "系统尚未激活",
        "activated": False,
        "is_trial": False,
        "trial_available": trial_available,
        "license_info": None,
    }


# ═══════════════════════════════════════════════════
#  试用码管理
# ═══════════════════════════════════════════════════

async def _check_trial_available() -> bool:
    """检查试用是否可用（未使用过）"""
    try:
        from .database import get_runtime_db
        async for conn, cur in get_runtime_db():
            await cur.execute(
                "SELECT config_value FROM system_configs WHERE config_key = 'trial_used'"
            )
            row = await cur.fetchone()
            if row:
                val = row["config_value"] if isinstance(row, dict) else row[0]
                return val != "true"
            break
    except Exception:
        pass
    # 也检查三保险存储
    info = read_license_info()
    if info and info.get("trial_used"):
        return False
    return True


async def _mark_trial_used():
    """标记试用已使用（写入 runtime.db + 三保险）"""
    try:
        from .database import get_runtime_db
        async for conn, cur in get_runtime_db():
            await cur.execute(
                """INSERT INTO system_configs (config_key, config_value, updated_at)
                   VALUES ('trial_used', 'true', datetime('now'))
                   ON CONFLICT(config_key) DO UPDATE SET config_value = 'true', updated_at = datetime('now')""",
            )
            await conn.commit()
            break
    except Exception as e:
        logger.warning(f"标记试用已使用失败: {e}")


def generate_trial_code(machine_code: str) -> str:
    """
    本地生成试用码（不需要私钥）
    格式: ZFZS-{机器码}-0601001E-{校验码}
    """
    import hmac
    mc = machine_code.strip().upper()
    payload = f"ZFZS|{mc}|{TRIAL_METADATA}"
    sig = hmac.new(mc.encode(), payload.encode(), hashlib.sha256).hexdigest()[:8].upper()
    return f"ZFZS-{mc}-{TRIAL_METADATA}-{sig}"


def validate_trial_code(trial_code: str, machine_code: str) -> bool:
    """
    验证试用码（本地验证）
    """
    import hmac
    parts = trial_code.strip().upper().split("-")
    if len(parts) != 4:
        return False
    if parts[0] != "ZFZS":
        return False
    if parts[1] != machine_code.upper():
        return False
    if parts[2] != TRIAL_METADATA:
        return False

    # 验证校验码
    payload = f"ZFZS|{parts[1]}|{parts[2]}"
    expected = hmac.new(
        parts[1].encode(), payload.encode(), hashlib.sha256
    ).hexdigest()[:8].upper()

    return parts[3] == expected


async def activate_trial() -> dict:
    """
    激活试用码
    """
    machine_code = get_machine_code_unified()

    # 检查试用是否已使用
    if not await _check_trial_available():
        return {"success": False, "message": "试用已使用过，无法重复试用"}

    # 生成试用码
    trial_code = generate_trial_code(machine_code)

    # 计算到期日
    expire_date = (date.today() + timedelta(days=TRIAL_DAYS)).isoformat()

    # 写入激活信息
    trial_data = {
        "activated": True,
        "activate_time": datetime.now().isoformat(),
        "trial_start": date.today().isoformat(),
        "trial_used": True,
        "license_key": trial_code,
        "license_info": {
            "version": "标准版",
            "type": "试用",
            "type_code": TRIAL_TYPE_CODE,
            "expire_date": expire_date,
            "is_permanent": False,
        },
        "full_signature": None,
    }
    await write_license_info_async(trial_data)

    # 标记试用已使用
    await _mark_trial_used()

    return {
        "success": True,
        "message": f"试用已激活，有效期 {TRIAL_DAYS} 天",
        "trial_code": trial_code,
        "expire_date": expire_date,
    }


async def activate_license(license_key: str, full_signature_hex: str = "") -> dict:
    """
    激活序列号
    新格式激活码已内嵌完整签名，无需单独传入 full_signature_hex
    """
    machine_code = get_machine_code_unified()

    # 从激活码中提取内嵌的完整签名
    parsed = parse_license_key(license_key)
    embedded_sig = parsed.get("full_signature") if parsed else None

    # 优先使用内嵌签名，其次用传入的参数
    sig_to_use = embedded_sig or full_signature_hex

    if sig_to_use:
        valid, msg, info = validate_license_key_with_full_sig(license_key, machine_code, sig_to_use)
    else:
        valid, msg, info = validate_license_key(license_key, machine_code)

    if not valid:
        return {"success": False, "message": msg}

    # 读取现有信息
    existing = await read_license_info_full() or {}

    # 续费检查：新到期日必须晚于当前到期日
    if existing.get("activated") and existing.get("license_info"):
        current_expire = existing["license_info"].get("expire_date")
        new_expire = info.get("expire_date")
        if (current_expire and new_expire
                and current_expire != "2099-12-31"
                and new_expire != "2099-12-31"):
            try:
                if date.fromisoformat(new_expire) <= date.fromisoformat(current_expire):
                    return {
                        "success": False,
                        "message": f"新序列号到期日({new_expire})必须晚于当前到期日({current_expire})",
                    }
            except ValueError:
                pass

    # 写入激活信息
    activate_data = {
        "activated": True,
        "activate_time": datetime.now().isoformat(),
        "license_key": license_key,
        "license_info": info,
        "full_signature": sig_to_use or None,
        "trial_start": existing.get("trial_start"),
    }
    await write_license_info_async(activate_data)

    return {
        "success": True,
        "message": msg,
        "license_info": info,
    }
