#!/usr/bin/env python3
"""将全角/易混淆标点规范为半角 ASCII(注释, 文档, 配置模板).

不转换弯引号 “ ” ‘ ’,避免破坏 Python/JS 字符串字面量.
"""
from __future__ import annotations

import argparse
from pathlib import Path

REPO = Path(__file__).resolve().parents[1]

SKIP_DIRS = frozenset({
    ".git",
    ".venv",
    "node_modules",
    "__pycache__",
    ".cursor",
    "agent-transcripts",
})

SCAN_SUFFIXES = frozenset({
    ".py",
    ".js",
    ".html",
    ".md",
    ".sh",
    ".css",
    ".json",
    ".cjs",
    ".txt",
    ".yml",
    ".yaml",
    ".example",
})

AMBIGUOUS_CHARS = frozenset(
    "\uff08\uff09\uff1a\uff0c\uff1b\uff1f\uff01\u3002\u3001\u00a0"
)

TRANSLATION = str.maketrans(
    {
        "\uff08": "(",
        "\uff09": ")",
        "\uff1a": ":",
        "\uff0c": ",",
        "\uff1b": ";",
        "\uff1f": "?",
        "\uff01": "!",
        "\u3002": ".",
        "\u3001": ",",
        "\u00a0": " ",
    }
)


def should_scan(path: Path) -> bool:
    if not path.is_file():
        return False
    if any(part in SKIP_DIRS for part in path.parts):
        return False
    if path.name == ".env.example" or path.name.endswith(".env.example"):
        return True
    return path.suffix in SCAN_SUFFIXES


def normalize_text(text: str) -> tuple[str, int]:
    count = sum(1 for ch in text if ch in AMBIGUOUS_CHARS)
    if not count:
        return text, 0
    return text.translate(TRANSLATION), count


def read_text_strip_bom(path: Path) -> tuple[str, bool]:
    raw = path.read_text(encoding="utf-8")
    if raw.startswith("\ufeff"):
        return raw.lstrip("\ufeff"), True
    return raw, False


def iter_targets(root: Path) -> list[Path]:
    return sorted(p for p in root.rglob("*") if should_scan(p))


def main() -> int:
    parser = argparse.ArgumentParser(description="Normalize ambiguous Unicode punctuation")
    parser.add_argument("--dry-run", action="store_true")
    parser.add_argument("--root", default=str(REPO))
    args = parser.parse_args()

    root = Path(args.root)
    files_changed = 0
    chars_changed = 0

    for path in iter_targets(root):
        try:
            original, had_bom = read_text_strip_bom(path)
        except (OSError, UnicodeDecodeError):
            continue
        normalized, n = normalize_text(original)
        if not n and not had_bom:
            continue
        rel = path.relative_to(root)
        if args.dry_run:
            print(f"[dry-run] {rel}: {n} chars")
        else:
            # 保持原换行风格, 仅替换标点
            path.write_text(normalized, encoding="utf-8", newline="")
        files_changed += 1
        chars_changed += n

    print(f"done: {files_changed} files, {chars_changed} replacements")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
