#!/usr/bin/env python3
from __future__ import annotations

import argparse
import hashlib
import os
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))

from nf_nacional_web.db import MysqlCli, sql_quote

MIGRATION_PATTERN = re.compile(r"^(?P<version>\d{3})_[a-z0-9_]+\.sql$")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Aplica migrations pendentes com lock e validacao de checksum."
    )
    parser.add_argument(
        "--check",
        action="store_true",
        help="Somente verifica se existem migrations pendentes.",
    )
    return parser.parse_args()


def migration_files() -> list[tuple[str, Path]]:
    migrations: list[tuple[str, Path]] = []
    versions: set[str] = set()
    for path in sorted((ROOT / "migrations").glob("*.sql")):
        match = MIGRATION_PATTERN.fullmatch(path.name)
        if not match:
            continue
        version = match.group("version")
        if version in versions:
            raise RuntimeError(f"Versao de migration duplicada: {version}")
        versions.add(version)
        migrations.append((version, path))
    return migrations


def checksum(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


def ensure_registry(db: MysqlCli) -> None:
    db.execute(
        """
CREATE TABLE IF NOT EXISTS nfse_schema_migrations (
  version VARCHAR(20) NOT NULL,
  filename VARCHAR(255) NOT NULL,
  checksum CHAR(64) NOT NULL,
  applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
"""
    )


def applied_migrations(db: MysqlCli) -> dict[str, tuple[str, str]]:
    rows = db.query_rows(
        "SELECT version, filename, checksum FROM nfse_schema_migrations ORDER BY version;"
    )
    return {row[0]: (row[1], row[2]) for row in rows}


def main() -> int:
    args = parse_args()
    db = MysqlCli.from_env()
    db.require_persistent_connection()

    lock_name = f"maspernf:migrations:{db.database}"
    timeout = max(1, int(os.environ.get("NFSE_MIGRATION_LOCK_TIMEOUT_SECONDS", "60")))
    lock_rows = db.query_rows(
        f"SELECT GET_LOCK({sql_quote(lock_name)}, {timeout});"
    )
    if not lock_rows or lock_rows[0][0] != "1":
        raise RuntimeError("Nao foi possivel obter o lock global de migrations.")

    try:
        ensure_registry(db)
        applied = applied_migrations(db)
        pending: list[tuple[str, Path, str]] = []

        for version, path in migration_files():
            current_checksum = checksum(path)
            previous = applied.get(version)
            if previous:
                previous_filename, previous_checksum = previous
                if previous_filename != path.name or previous_checksum != current_checksum:
                    raise RuntimeError(
                        f"Migration {version} foi alterada depois de aplicada: {path.name}"
                    )
                continue
            pending.append((version, path, current_checksum))

        if args.check:
            if pending:
                print("pendentes=" + ",".join(path.name for _, path, _ in pending))
                return 1
            print("migrations=ok")
            return 0

        if not pending:
            print("migrations=ok nenhuma_pendente")
            return 0

        for version, path, current_checksum in pending:
            print(f"aplicando={path.name}", flush=True)
            db.execute(path.read_text(encoding="utf-8"))
            db.execute(
                f"""
INSERT INTO nfse_schema_migrations (version, filename, checksum)
VALUES ({sql_quote(version)}, {sql_quote(path.name)}, {sql_quote(current_checksum)});
"""
            )
            print(f"aplicada={path.name}", flush=True)

        print(f"migrations=ok aplicadas={len(pending)}")
        return 0
    finally:
        db.execute(f"SELECT RELEASE_LOCK({sql_quote(lock_name)});")


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