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

import argparse
import os
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from pathlib import Path

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

from nf_nacional_web.db import MysqlCli
from nf_nacional_web.sync import run_sync_full


@dataclass(frozen=True)
class MunicipioSyncTarget:
    id: int
    codigo_ibge: str
    codigo_siafi: str
    nome: str
    status: str
    lock_recente: str


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Executa sincronizacao full de todos os municipios ativos.")
    parser.add_argument(
        "--concurrency",
        type=int,
        default=3,
        help="Quantidade maxima de municipios sincronizados em paralelo. Padrao: 3.",
    )
    return parser.parse_args()


def list_targets(db: MysqlCli) -> list[MunicipioSyncTarget]:
    stale_minutes = int(os.environ.get("NFSE_SYNC_STALE_LOCK_MINUTES", "120"))
    rows = db.query_rows(
        f"""
SELECT
  m.id,
  m.codigo_ibge,
  m.codigo_siafi,
  m.nome,
  COALESCE(s.status, 'sem_estado'),
  CASE
    WHEN s.locked_at IS NOT NULL
     AND s.locked_at >= DATE_SUB(NOW(), INTERVAL {stale_minutes} MINUTE)
    THEN 1 ELSE 0
  END
FROM nfse_municipios m
JOIN nfse_certificados c ON c.municipio_id = m.id AND c.ativo = 1
LEFT JOIN nfse_sync_state s ON s.municipio_id = m.id
WHERE m.ativo = 1
ORDER BY m.codigo_siafi;
"""
    )
    return [
        MunicipioSyncTarget(id=int(row[0]), codigo_ibge=row[1], codigo_siafi=row[2], nome=row[3], status=row[4], lock_recente=row[5])
        for row in rows
    ]


def sync_target(database: str, target: MunicipioSyncTarget) -> tuple[MunicipioSyncTarget, str, str, str]:
    run_sync_full(database, target.id, locked_by="script-sync-full-all")
    db = MysqlCli(database=database)
    rows = db.query_rows(
        f"""
SELECT status, last_nsu, COALESCE(last_error_message, '')
FROM nfse_sync_state
WHERE municipio_id = {target.id}
LIMIT 1;
"""
    )
    if not rows:
        return target, "sem_estado", "", "Estado de sincronizacao nao encontrado."
    status, last_nsu, error = rows[0]
    return target, status, last_nsu, error


def main() -> int:
    args = parse_args()
    concurrency = max(1, args.concurrency)
    db = MysqlCli.from_env()
    targets = list_targets(db)
    if not targets:
        print("Nenhum municipio ativo com certificado ativo encontrado.")
        return 0

    skipped = [target for target in targets if target.status in ("pending", "running") and target.lock_recente == "1"]
    runnable = [target for target in targets if target not in skipped]
    for target in skipped:
        print(f"{target.codigo_siafi} {target.nome}: ignorado, status={target.status}.")
    if not runnable:
        return 0

    print(f"Sincronizando {len(runnable)} municipio(s) com concorrencia={concurrency}.")
    has_error = False
    with ThreadPoolExecutor(max_workers=concurrency) as executor:
        futures = {executor.submit(sync_target, db.database, target): target for target in runnable}
        for future in as_completed(futures):
            target = futures[future]
            try:
                resolved, status, last_nsu, error = future.result()
            except Exception as exc:
                has_error = True
                print(f"{target.codigo_siafi} {target.nome}: erro inesperado: {exc}")
                continue
            if status == "error":
                has_error = True
            print(f"{resolved.codigo_siafi} {resolved.nome}: status={status} last_nsu={last_nsu} erro={error}")

    return 1 if has_error else 0


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