#!/usr/bin/env python3
"""
检查 Alembic migration 文件命名规范。

规则:
1. 文件名必须以数字前缀开头（3位补零），如 001_xxx.py、052_add_table.py
2. 数字前缀必须连续递增（与 check_migration_chain.py 一致，但只看文件名）
3. 数字前缀后必须跟有描述性后缀（不能只有数字）
4. 文件名使用小写+下划线（snake_case）
5. revision 变量值必须与文件名数字前缀一致（可选带描述后缀）

用法:
    python3 scripts/ci/check_migration_naming.py [alembic_versions_dir]

默认目录: alembic/versions/

退出码:
    0 - 全部通过
    1 - 有命名违规
"""

from __future__ import annotations

import re
import sys
from pathlib import Path

# 文件名格式: 3位数字_描述.py
FILE_NAME_PATTERN = re.compile(r"^(\d{3})_[a-z][a-z0-9_]*\.py$")
# 纯数字文件名（不允许）
PURE_NUM_PATTERN = re.compile(r"^\d{3}\.py$")
# revision 值的数字前缀
REV_NUM_PATTERN = re.compile(r"^(\d{3})")
# revision 变量行
REV_LINE_PATTERN = re.compile(
    r'^\s*revision\s*(?::\s*str\s*)?=\s*["\']([^"\']+)["\']',
    re.MULTILINE,
)


def check_naming(versions_dir: Path) -> list[str]:
    """检查 migration 文件命名，返回错误列表。"""
    errors: list[str] = []

    if not versions_dir.is_dir():
        return [f"目录不存在: {versions_dir}"]

    py_files = sorted(f for f in versions_dir.iterdir() if f.suffix == ".py")
    if not py_files:
        return [f"目录下没有 migration 文件: {versions_dir}"]

    print(f"检查 migration 文件命名: {versions_dir}")
    print(f"共 {len(py_files)} 个文件")
    print()

    # 1. 文件名格式检查
    print("1. 文件名格式检查...")
    file_nums: list[int] = []
    for f in py_files:
        name = f.name
        if PURE_NUM_PATTERN.match(name):
            errors.append(f"  ❌ {name}: 只有数字编号，缺少描述性后缀")
            continue
        m = FILE_NAME_PATTERN.match(name)
        if not m:
            errors.append(f"  ❌ {name}: 命名格式不规范，应为 NNN_description.py " f"（3位数字前缀+下划线+小写描述）")
            continue
        file_nums.append(int(m.group(1)))

    if not any("命名格式不规范" in e or "缺少描述性后缀" in e for e in errors):
        print(f"  ✅ 全部 {len(py_files)} 个文件名格式正确")
    else:
        for e in errors:
            if "命名格式不规范" in e or "缺少描述性后缀" in e:
                print(e)

    # 2. 编号连续性检查（基于文件名数字前缀）
    print()
    print("2. 编号连续性检查...")
    if file_nums:
        expected = set(range(min(file_nums), max(file_nums) + 1))
        actual = set(file_nums)
        missing = sorted(expected - actual)
        if missing:
            errors.append(f"  ❌ 编号不连续，缺少: {', '.join(f'{n:03d}' for n in missing)}")
            print(f"  ❌ 编号不连续，缺少 {len(missing)} 个: " f"{', '.join(f'{n:03d}' for n in missing)}")
        else:
            print(f"  ✅ 编号连续（{min(file_nums):03d} ~ {max(file_nums):03d}）")

    # 3. revision 变量与文件名前缀一致性检查
    print()
    print("3. revision变量与文件名一致性检查...")
    rev_mismatch = 0
    for f in py_files:
        m = FILE_NAME_PATTERN.match(f.name)
        if not m:
            continue  # 格式不对的已经报过了
        file_num = m.group(1)
        content = f.read_text(encoding="utf-8")
        rev_match = REV_LINE_PATTERN.search(content)
        if not rev_match:
            errors.append(f"  ❌ {f.name}: 未找到 revision 变量定义")
            rev_mismatch += 1
            continue
        rev_value = rev_match.group(1)
        rev_num_match = REV_NUM_PATTERN.match(rev_value)
        if not rev_num_match or rev_num_match.group(1) != file_num:
            errors.append(f"  ❌ {f.name}: revision='{rev_value}' 与文件名前缀 {file_num} 不一致")
            rev_mismatch += 1

    if rev_mismatch == 0:
        print(f"  ✅ 全部 {len(py_files)} 个文件的 revision 与文件名一致")

    return errors


def main() -> int:
    versions_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("alembic/versions")

    errors = check_naming(versions_dir)

    print()
    if errors:
        print(f"❌ 发现 {len(errors)} 个命名问题")
        print()
        print("命名规范:")
        print("  - 文件名格式: NNN_description.py（3位数字前缀 + 下划线 + 小写描述）")
        print("  - 编号必须连续，不能跳号")
        print("  - revision 变量的数字前缀必须与文件名一致")
        return 1

    print("✅ 所有 migration 文件命名规范检查通过")
    return 0


if __name__ == "__main__":
    sys.exit(main())
