#!/usr/bin/env python3
"""
Exporta/importa dados do MySQL entre máquinas.

O schema é criado por init_database.py; este script move os DADOS.

Uso na máquina de origem:
  python scripts/transferir_banco.py exportar --arquivo backup_omrcheck.json

Na máquina nova (após init_database.py):
  python scripts/transferir_banco.py importar --arquivo backup_omrcheck.json

Também copie a pasta processamentos/ para levar os lotes OMR (imagens e leituras).
"""

from __future__ import annotations

import argparse
import json
import sys
from datetime import date, datetime
from decimal import Decimal
from pathlib import Path

from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent.parent
if str(BASE_DIR) not in sys.path:
    sys.path.insert(0, str(BASE_DIR))

load_dotenv(BASE_DIR / ".env")

from sqlalchemy import inspect, text

from app.db import ENGINE, storage_usa_banco
from app.migrations import run_migrations

TABELAS_EXPORTAVEIS = (
    "kanban_colunas",
    "kanban_cards",
    "jobs",
    "sessoes_ativas",
    "tokens_lembre_me",
)


def _serializar_valor(valor):
    if isinstance(valor, (datetime, date)):
        return valor.isoformat(sep=" ", timespec="seconds")
    if isinstance(valor, Decimal):
        return str(valor)
    if isinstance(valor, (dict, list)):
        return valor
    return valor


def _serializar_linha(linha: dict) -> dict:
    return {chave: _serializar_valor(valor) for chave, valor in linha.items()}


def exportar_dados() -> dict:
    if ENGINE is None:
        raise RuntimeError("MySQL não está habilitado (APP_STORAGE_BACKEND deve ser mysql).")

    run_migrations(ENGINE)

    payload = {
        "versao": 1,
        "exportado_em": datetime.now().isoformat(timespec="seconds"),
        "tabelas": {},
    }

    insp = inspect(ENGINE)
    existentes = set(insp.get_table_names())

    with ENGINE.connect() as conn:
        for tabela in TABELAS_EXPORTAVEIS:
            if tabela not in existentes:
                payload["tabelas"][tabela] = []
                continue
            linhas = conn.execute(text(f"SELECT * FROM `{tabela}`")).mappings().all()
            payload["tabelas"][tabela] = [_serializar_linha(dict(linha)) for linha in linhas]

    return payload


def _limpar_tabela(conn, tabela: str) -> None:
    conn.execute(text(f"DELETE FROM `{tabela}`"))


def importar_dados(payload: dict, *, substituir: bool = True) -> dict:
    if ENGINE is None:
        raise RuntimeError("MySQL não está habilitado (APP_STORAGE_BACKEND deve ser mysql).")

    run_migrations(ENGINE)

    tabelas = payload.get("tabelas")
    if not isinstance(tabelas, dict):
        raise ValueError("Arquivo de backup inválido: chave 'tabelas' ausente.")

    resumo: dict[str, int] = {}
    ordem_importacao = list(TABELAS_EXPORTAVEIS)

    with ENGINE.begin() as conn:
        if substituir:
            for tabela in reversed(ordem_importacao):
                if tabela in tabelas and inspect(ENGINE).has_table(tabela):
                    _limpar_tabela(conn, tabela)

        for tabela in ordem_importacao:
            linhas = tabelas.get(tabela, [])
            if not isinstance(linhas, list) or not linhas:
                resumo[tabela] = 0
                continue

            if not inspect(ENGINE).has_table(tabela):
                resumo[tabela] = 0
                continue

            inseridas = 0
            for linha in linhas:
                if not isinstance(linha, dict) or not linha:
                    continue
                colunas = list(linha.keys())
                placeholders = ", ".join(f":{coluna}" for coluna in colunas)
                nomes = ", ".join(f"`{coluna}`" for coluna in colunas)
                conn.execute(
                    text(f"INSERT INTO `{tabela}` ({nomes}) VALUES ({placeholders})"),
                    linha,
                )
                inseridas += 1
            resumo[tabela] = inseridas

    return resumo


def main() -> int:
    parser = argparse.ArgumentParser(description="Exporta/importa dados MySQL do OMRCheck-Web")
    sub = parser.add_subparsers(dest="comando", required=True)

    exportar = sub.add_parser("exportar", help="Exporta dados para JSON")
    exportar.add_argument(
        "--arquivo",
        default=str(BASE_DIR / "backups" / "omrcheck_dados.json"),
        help="Caminho do arquivo de saída",
    )

    importar = sub.add_parser("importar", help="Importa dados de JSON")
    importar.add_argument("--arquivo", required=True, help="Arquivo JSON exportado")
    importar.add_argument(
        "--manter-existentes",
        action="store_true",
        help="Não apaga registros atuais antes de importar",
    )

    args = parser.parse_args()

    if not storage_usa_banco():
        print("[ERRO] APP_STORAGE_BACKEND não é mysql. Nada para exportar/importar.")
        return 1

    if args.comando == "exportar":
        dados = exportar_dados()
        destino = Path(args.arquivo)
        destino.parent.mkdir(parents=True, exist_ok=True)
        destino.write_text(json.dumps(dados, ensure_ascii=False, indent=2), encoding="utf-8")
        total = sum(len(v) for v in dados.get("tabelas", {}).values())
        print(f"[OK] Exportados {total} registro(s) em {destino}")
        for tabela in TABELAS_EXPORTAVEIS:
            qtd = len(dados.get("tabelas", {}).get(tabela, []))
            print(f"  - {tabela}: {qtd}")
        print("")
        print("Lembre de copiar também a pasta processamentos/ para os lotes OMR.")
        return 0

    caminho = Path(args.arquivo)
    if not caminho.exists():
        print(f"[ERRO] Arquivo não encontrado: {caminho}")
        return 1

    payload = json.loads(caminho.read_text(encoding="utf-8"))
    resumo = importar_dados(payload, substituir=not args.manter_existentes)
    print(f"[OK] Importação concluída a partir de {caminho}")
    for tabela, qtd in resumo.items():
        print(f"  - {tabela}: {qtd}")
    return 0


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