"""
Configuração do engine e session SQLAlchemy.

Prioridade de conexão:
  1. db_url em okgestor_config.json  → PostgreSQL (ou qualquer URL SQLAlchemy)
  2. Fallback automático             → SQLite local  (okgestor.db)

Variável pública MODO_CONEXAO: "remoto" | "local"
"""

import json
import sys as _sys
import shutil
from contextlib import contextmanager
from datetime import datetime
from pathlib import Path

from sqlalchemy import create_engine, event, text as _text
from sqlalchemy.orm import sessionmaker, DeclarativeBase

# ── Caminhos ──────────────────────────────────────────────────────────────────
PASTA_BASE   = (
    Path(_sys.executable).parent
    if getattr(_sys, "frozen", False)
    else Path(__file__).resolve().parent.parent
)
PASTA_BACKUP = PASTA_BASE / "backups" / "db"
DB_PATH      = PASTA_BASE / "okgestor.db"          # SQLite local (fallback)
_CONFIG_FILE = PASTA_BASE / "okgestor_config.json"


# ── Leitura de config ─────────────────────────────────────────────────────────
def _ler_config() -> dict:
    try:
        return json.loads(_CONFIG_FILE.read_text(encoding="utf-8"))
    except Exception:
        return {}


# ── Escolha da URL de conexão ─────────────────────────────────────────────────
def _resolver_url() -> tuple[str, str]:
    """
    Retorna (database_url, modo) onde modo é 'remoto' ou 'local'.
    Tenta PostgreSQL primeiro; cai para SQLite se falhar.
    """
    cfg     = _ler_config()
    url_rem = cfg.get("db_url", "").strip()

    if url_rem:
        try:
            eng_test = create_engine(url_rem, connect_args={}, pool_pre_ping=True)
            with eng_test.connect() as _c:
                pass
            eng_test.dispose()
            return url_rem, "remoto"
        except Exception as e:
            print(f"[database] PostgreSQL indisponível ({e}). Usando SQLite local.")

    return f"sqlite:///{DB_PATH}", "local"


DATABASE_URL, MODO_CONEXAO = _resolver_url()

# ── Engine ────────────────────────────────────────────────────────────────────
_connect_args = {"check_same_thread": False} if DATABASE_URL.startswith("sqlite") else {}

engine = create_engine(
    DATABASE_URL,
    connect_args=_connect_args,
    pool_pre_ping=True,
    echo=False,
)

# WAL mode apenas para SQLite
if DATABASE_URL.startswith("sqlite"):
    @event.listens_for(engine, "connect")
    def _set_wal_mode(conn, _):
        conn.execute("PRAGMA journal_mode=WAL")
        conn.execute("PRAGMA foreign_keys=ON")


Session = sessionmaker(bind=engine, autoflush=True, autocommit=False)


class Base(DeclarativeBase):
    pass


# ── Session context manager ───────────────────────────────────────────────────
@contextmanager
def get_session():
    """Context manager: with get_session() as s: ..."""
    session = Session()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()


# ── Backup (SQLite only) ──────────────────────────────────────────────────────
def fazer_backup(forcar: bool = False) -> Path | None:
    """Backup diário do SQLite local. Ignorado em modo remoto."""
    if MODO_CONEXAO == "remoto":
        return None
    if not DB_PATH.exists():
        return None

    PASTA_BACKUP.mkdir(parents=True, exist_ok=True)
    nome    = f"okgestor_{datetime.now().strftime('%Y%m%d')}.db"
    destino = PASTA_BACKUP / nome

    if destino.exists() and not forcar:
        return None

    import sqlite3
    src = sqlite3.connect(str(DB_PATH))
    dst = sqlite3.connect(str(destino))
    src.backup(dst)
    src.close()
    dst.close()

    todos = sorted(PASTA_BACKUP.glob("okgestor_*.db"))
    for antigo in todos[:-30]:
        antigo.unlink(missing_ok=True)

    return destino


# ── Criação / migração de tabelas ─────────────────────────────────────────────
def criar_tabelas():
    """Cria todas as tabelas e aplica colunas novas via ALTER TABLE seguro."""
    from db import models  # noqa
    import sqlalchemy as _sa

    Base.metadata.create_all(engine)

    _colunas_extras = [
        ("colaboradores",     "adiantamento",      "REAL DEFAULT 0.0"),
        ("colaboradores",     "chave_pix",          "TEXT"),
        ("colaboradores",     "uniformes",          "TEXT"),
        ("colaboradores",     "demissao",           "DATE"),
        ("colaboradores",     "apelido",            "TEXT"),
        ("titulos",           "centro_custo",       "TEXT"),
        ("titulos",           "forma_pagamento",    "TEXT"),
        ("titulos",           "conta_nome",         "TEXT"),
        ("config_empresa",    "fc_conta_dinheiro",  "TEXT"),
        ("config_empresa",    "fc_conta_moedas",    "TEXT"),
        ("fechamentos_caixa", "conta_moedas",       "TEXT"),
        ("fechamentos_caixa", "editado_em",         "DATETIME"),
        ("fechamentos_caixa", "log_edicao",         "TEXT"),
    ]

    if DATABASE_URL.startswith("sqlite"):
        # SQLite: ALTER TABLE ADD COLUMN simples
        with engine.connect() as conn:
            for tabela, coluna, definicao in _colunas_extras:
                try:
                    conn.execute(_sa.text(f"SELECT {coluna} FROM {tabela} LIMIT 1"))
                except Exception:
                    conn.execute(_sa.text(
                        f"ALTER TABLE {tabela} ADD COLUMN {coluna} {definicao}"
                    ))
                    conn.commit()
    else:
        # PostgreSQL: usa information_schema para verificar
        with engine.connect() as conn:
            for tabela, coluna, definicao in _colunas_extras:
                existe = conn.execute(_sa.text(
                    "SELECT 1 FROM information_schema.columns "
                    "WHERE table_name=:t AND column_name=:c"
                ), {"t": tabela, "c": coluna}).fetchone()
                if not existe:
                    # Adapta tipo para PostgreSQL
                    tipo_pg = (definicao
                               .replace("TEXT", "TEXT")
                               .replace("REAL", "DOUBLE PRECISION")
                               .replace("DATETIME", "TIMESTAMP")
                               .replace("DATE", "DATE"))
                    conn.execute(_sa.text(
                        f"ALTER TABLE {tabela} ADD COLUMN IF NOT EXISTS {coluna} {tipo_pg}"
                    ))
                    conn.commit()
