#!/usr/bin/env python3
"""
Preenche o Componente B (PPG) na TB_RANKING_CONSULTOR usando Oracle remoto.
Usa .env para credenciais e busca em lotes (IN) para reduzir round-trips.
"""
import os
import sys
from datetime import datetime
from itertools import islice
from pathlib import Path

import cx_Oracle
from dotenv import load_dotenv

ROOT_DIR = Path(__file__).resolve().parents[1]
load_dotenv(ROOT_DIR / ".env")

ORACLE_LOCAL_USER = os.getenv("ORACLE_LOCAL_USER", "local123")
ORACLE_LOCAL_PASSWORD = os.getenv("ORACLE_LOCAL_PASSWORD", "local123")
ORACLE_LOCAL_DSN = os.getenv("ORACLE_LOCAL_DSN", "oracle18c:1521/XEPDB1")

ORACLE_REMOTE_USER = os.getenv("ORACLE_REMOTE_USER")
ORACLE_REMOTE_PASSWORD = os.getenv("ORACLE_REMOTE_PASSWORD")
ORACLE_REMOTE_DSN = os.getenv("ORACLE_REMOTE_DSN")

BATCH_IDS = 500
BATCH_UPDATES = 1000

QUERY_BASE = """
    SELECT
        c.ID_PESSOA,
        c.ID_PROGRAMA_SNPG,
        p.NM_PROGRAMA,
        p.CD_PROGRAMA_PPG,
        p.DS_CONCEITO,
        p.NM_PROGRAMA_MODALIDADE,
        aa.NM_AREA_AVALIACAO,
        c.DT_INICIO_VIGENCIA,
        c.DT_FIM_VIGENCIA
    FROM VM_COORDENADOR c
    INNER JOIN VM_PROGRAMA p ON c.ID_PROGRAMA_SNPG = p.ID_PROGRAMA
    LEFT JOIN VM_AREA_CONHECIMENTO ac ON p.ID_AREA_CONHECIMENTO_ATUAL = ac.ID_AREA_CONHECIMENTO
    LEFT JOIN VM_AREA_AVALIACAO aa ON ac.ID_AREA_AVALIACAO = aa.ID_AREA_AVALIACAO
    WHERE c.ID_PESSOA IN ({placeholders})
"""


def chunked(iterable, size):
    it = iter(iterable)
    while True:
        bloco = list(islice(it, size))
        if not bloco:
            break
        yield bloco


def calcular_componente_b(coordenacoes):
    if not coordenacoes:
        return 0

    base = 70
    anos_totais = 0
    for c in coordenacoes:
        inicio = c.get("DT_INICIO_VIGENCIA")
        fim = c.get("DT_FIM_VIGENCIA")
        if not inicio:
            continue
        if fim and fim < inicio:
            fim = None
        fim_ref = fim or datetime.now()
        anos_totais += (fim_ref - inicio).days // 365

    tempo = min(int(anos_totais * 5), 50)

    programas_distintos = len({c.get("ID_PROGRAMA_SNPG") for c in coordenacoes})
    extras = min((programas_distintos - 1) * 20, 40) if programas_distintos > 1 else 0

    maior_nota = 0
    for c in coordenacoes:
        nota_str = str(c.get("DS_CONCEITO") or "").strip()
        if nota_str in ("7", "6", "5", "4", "3"):
            maior_nota = max(maior_nota, int(nota_str))

    bonus_nota = {7: 20, 6: 15, 5: 10, 4: 5, 3: 0}.get(maior_nota, 0)

    return base + tempo + extras + bonus_nota


def fetch_ppg_por_lote(cursor_remote, ids):
    placeholders = ", ".join(f":id{i}" for i in range(len(ids)))
    params = {f"id{i}": val for i, val in enumerate(ids)}
    cursor_remote.execute(QUERY_BASE.format(placeholders=placeholders), params)
    cols = [col[0] for col in cursor_remote.description]
    return [dict(zip(cols, row)) for row in cursor_remote.fetchall()]


def main():
    if not ORACLE_REMOTE_USER or not ORACLE_REMOTE_PASSWORD or not ORACLE_REMOTE_DSN:
        print("ERRO: credenciais do Oracle REMOTO não encontradas (.env).")
        sys.exit(1)

    print("Conectando Oracle LOCAL (ranking)...")
    conn_local = cx_Oracle.connect(ORACLE_LOCAL_USER, ORACLE_LOCAL_PASSWORD, ORACLE_LOCAL_DSN)
    cursor_local = conn_local.cursor()

    print("Conectando Oracle REMOTO (SUCUPIRA_PAINEL)...")
    conn_remote = cx_Oracle.connect(ORACLE_REMOTE_USER, ORACLE_REMOTE_PASSWORD, ORACLE_REMOTE_DSN)
    cursor_remote = conn_remote.cursor()
    cursor_remote.arraysize = 2000

    print("Buscando IDs a atualizar (COMPONENTE_B = 0)...")
    cursor_local.execute("SELECT ID_PESSOA FROM TB_RANKING_CONSULTOR WHERE NVL(COMPONENTE_B,0) = 0")
    ids_pessoas = [row[0] for row in cursor_local.fetchall()]
    total_ids = len(ids_pessoas)
    print(f"Total: {total_ids} consultores pendentes")

    processados = 0
    com_ppg = 0
    batch_updates = []

    for lote_ids in chunked(ids_pessoas, BATCH_IDS):
        try:
            registros = fetch_ppg_por_lote(cursor_remote, lote_ids)
        except Exception as e:
            print(f"Erro ao buscar PPG para lote (IDs {lote_ids[:3]}...): {e}")
            continue

        coord_por_pessoa = {}
        for r in registros:
            coord_por_pessoa.setdefault(r["ID_PESSOA"], []).append(r)

        for id_pessoa in lote_ids:
            coordenacoes = coord_por_pessoa.get(id_pessoa, [])
            if not coordenacoes:
                processados += 1
                continue

            comp_b = calcular_componente_b(coordenacoes)
            batch_updates.append({"comp_b": comp_b, "id_pessoa": id_pessoa})
            com_ppg += 1
            processados += 1

            if len(batch_updates) >= BATCH_UPDATES:
                cursor_local.executemany(
                    """
                    UPDATE TB_RANKING_CONSULTOR
                    SET COMPONENTE_B = :comp_b,
                        PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D
                    WHERE ID_PESSOA = :id_pessoa
                    """,
                    batch_updates,
                )
                conn_local.commit()
                print(f"Processados: {processados}/{total_ids} | Com PPG: {com_ppg}")
                batch_updates = []

    if batch_updates:
        cursor_local.executemany(
            """
            UPDATE TB_RANKING_CONSULTOR
            SET COMPONENTE_B = :comp_b,
                PONTUACAO_TOTAL = COMPONENTE_A + :comp_b + COMPONENTE_C + COMPONENTE_D
            WHERE ID_PESSOA = :id_pessoa
            """,
            batch_updates,
        )
        conn_local.commit()

    print("\nAtualizando posições...")
    cursor_local.callproc("SP_ATUALIZAR_POSICOES")
    conn_local.commit()

    print("\nFinalizado!")
    print(f"Total processados: {processados}")
    print(f"Com PPG: {com_ppg}")

    cursor_local.close()
    cursor_remote.close()
    conn_local.close()
    conn_remote.close()


if __name__ == "__main__":
    main()
