"""用法: python -m flyway.migration --server-tz UTC [--resolutions r.csv] [--allow-pending] [--out DIR]
目标库（FLYWAY_DATABASE_URL）须已执行 alembic upgrade 且业务表为空；源库为 FLYWAY_LEGACY_DATABASE_URL。"""
import argparse
import csv
import json
import sys
from pathlib import Path
from zoneinfo import ZoneInfo

from flyway.config import settings
from flyway.db import get_session_factory
from flyway.migration.run import MigrationBlocked, migrate
from flyway.migration.source import SqlServerSource
from flyway.migration.sqlscript import SqlScriptSource
from flyway.migration.verify import verify


OUTPUT_FILES = ("pending-dates.csv", "duplicates.txt", "renamed.txt", "verify.json")


def main() -> int:
    p = argparse.ArgumentParser()
    p.add_argument("--server-tz", required=True, help="旧服务器时区，以服务器配置为准，如 UTC 或 Pacific/Auckland")
    p.add_argument("--resolutions", help="CSV：table,row_id,column,resolution = server | user_nz。可把 pending-dates.csv 另存（如 decisions.csv）后填 resolution 列；不能与本次输出文件同路径")
    p.add_argument("--allow-pending", action="store_true", help="仅试演：未判定的日期按新西兰时间暂存并标记 pending")
    p.add_argument("--out", default="../migration-output")
    p.add_argument("--sql-script", help="没有 SQL Server 时，改从 SSMS 导出的含数据 .sql 脚本读取旧库")
    p.add_argument("--merge-duplicate-batches", action="store_true",
                   help="合并只差尾部空格 / 大小写的重复批次，保留前导空格（生产迁移前须经业务确认）")
    p.add_argument("--rename-duplicates", action="store_true",
                   help="单号 / 装箱单号 / 条码只差首尾空格或大小写的不同记录：保留最早一条，其余加 -DUPn 后缀（须经业务确认）")
    a = p.parse_args()
    out = Path(a.out)
    if a.resolutions and Path(a.resolutions).resolve() in {(out / n).resolve() for n in OUTPUT_FILES}:
        # 业务填好的判定表不能被本次输出覆盖：先另存一份再传入
        print(f"--resolutions {a.resolutions} would be overwritten by this run's output in {out}. "
              "Copy it (e.g. to decisions.csv) or use another --out directory.")
        return 2
    out.mkdir(parents=True, exist_ok=True)
    resolutions = {}
    if a.resolutions:
        with open(a.resolutions, encoding="utf-8") as f:
            rows = list(csv.DictReader(f))
        bad = [r for r in rows if r["resolution"].strip() not in ("", "server", "user_nz")]
        if bad:
            print(f'Invalid resolution "{bad[0]["resolution"]}" for {bad[0]["table"]} {bad[0]["row_id"]}: '
                  "use server or user_nz (blank = undecided).")
            return 2
        resolutions = {(r["table"], r["row_id"], r["column"]): r["resolution"].strip() for r in rows
                       if r["resolution"].strip()}
    source = SqlScriptSource(a.sql_script) if a.sql_script else SqlServerSource(settings.legacy_database_url)
    with get_session_factory()() as s:
        try:
            result = migrate(s, source, server_tz=ZoneInfo(a.server_tz), resolutions=resolutions,
                             allow_pending=a.allow_pending, merge_duplicate_batches=a.merge_duplicate_batches,
                             rename_duplicates=a.rename_duplicates)
        except MigrationBlocked as e:
            s.rollback()
            _write_csv(out / "pending-dates.csv", e.pending)
            (out / "duplicates.txt").write_text("\n".join(e.duplicates), encoding="utf-8")
            print(f"BLOCKED: {e}. See {out}")
            return 1
        report = verify(s, source, batch_map=result.merged_batches, server_tz=ZoneInfo(a.server_tz))
        _write_csv(out / "pending-dates.csv", result.pending)
        (out / "renamed.txt").write_text("\n".join(result.renamed), encoding="utf-8")
        (out / "verify.json").write_text(json.dumps(report.__dict__, default=str, indent=2), encoding="utf-8")
        if not report.ok:
            s.rollback()
            print(f"VERIFY FAILED. See {out / 'verify.json'}")
            return 1
        s.commit()
    print(f"OK. {len(result.pending)} pending dates. Report in {out}")
    return 0


def _write_csv(path: Path, rows: list[dict]) -> None:
    with open(path, "w", newline="", encoding="utf-8") as f:
        if rows:
            w = csv.DictWriter(f, fieldnames=list(rows[0]))
            w.writeheader()
            w.writerows(rows)


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