"""Empacota os dados tratados de um tema para o site.

Uso:
    python3 scripts/exportar_site.py --tema economia

Grava site/dados/<tema>.js com window.DADOS = {...}. Um arquivo .js (e não .json)
permite abrir o site direto do disco, sem servidor.
"""

import argparse
import json
import re
import subprocess
from collections import Counter, defaultdict

from comum import HOJE, RAIZ, TRATADOS, ler_csv, ler_fichas, ler_mandatos


def secoes(caminho):
    """Seções `## Título` da ficha, em texto simples (listas viram itens)."""
    texto = caminho.read_text(encoding="utf-8").split("\n---\n", 1)[-1]
    texto = re.sub(r"<!--.*?-->", "", texto, flags=re.S)
    resultado = {}
    for bloco in re.split(r"\n## ", "\n" + texto)[1:]:
        titulo, _, corpo = bloco.partition("\n")
        itens, paragrafo = [], []
        for linha in corpo.strip().splitlines():
            linha = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", re.sub(r"\*\*|`", "", linha)).strip()
            if linha.startswith("- "):
                itens.append(linha[2:])
            elif linha and not linha.startswith("|"):
                paragrafo.append(linha)
            elif not linha and paragrafo:
                itens.append(" ".join(paragrafo))
                paragrafo = []
        if paragrafo:
            itens.append(" ".join(paragrafo))
        resultado[titulo.strip().lower()] = itens
    return resultado


# Arquivo bruto de cada série, para o link "Extraído em" do site. Primeiro as regras pelo id
# da série (fontes sem código no texto: INPE, MapBiomas, INEP, SIOP...); depois o código citado
# na coluna `fonte` (SGS, SIDRA, WEO...). A menção ao IPCA usado para deflacionar não conta.
BRUTOS_POR_ID = [
    # orçamento: RTN (a série sem covid usa também a edição de dezembro de 2022), SIOP e Receita Federal
    (r"^despesa_primaria_gc__sem_covid", r"^rtn_serie_historica_"),
    (r"^(receita_liquida_gc|despesa_primaria_gc|resultado_primario_gc|despesas_discricionarias|pessoal_uniao|beneficios_rgps|precatorios)__",
     r"^rtn_serie_historica_\d"),
    (r"^investimento_uniao__rtn", r"^rtn_serie_historica_\d"),
    (r"^(investimento_uniao__siop|despesa_funcao__|emendas_parlamentares__)", r"^orcamento_funcao_gnd_mod_rp_"),
    (r"^gastos_tributarios__", r"^dgt_bases_efetivas_"),
    (r"^desmatamento_amazonia__", r"^prodes_amazonia_legal_taxas_"),
    (r"^desmatamento_cerrado__", r"^prodes_cerrado_incrementos_"),
    (r"^alertas_deter__", r"^deter_amz_mensal_"),
    (r"^focos_queimadas__brasil", r"^historico_mensal_brasil_"),
    (r"^focos_queimadas__amazonia_legal", r"^historico_mensal_amazonia_legal_"),
    (r"^focos_queimadas__amazonia", r"^historico_mensal_amazonia_\d"),
    (r"^area_queimada__(\w+)$", r"^area_queimada_anual_{1}_\d"),
    (r"^emissoes_gee__mcti", r"^nir_2024_"),
    (r"^emissoes_gee__seeg", r"^dados_nacionais_"),
    (r"^autos_infracao_ibama__", r"^auto_infracao_csv_"),
    (r"^despesa_gestao_ambiental__", r"^despesa_funcao18_"),
    (r"^perda_floresta_primaria__", r"^global_country_stats_"),
    (r"^despesa_educacao_federal__", r"^despesa_funcao12_"),
    (r"^(ideb|saeb)__", r"^ideb_brasil_"),
    (r"^pisa__", r"^pisa20\d\d_vol1_anexo_b1"),
    (r"^matriculas_basica__", r"^sinopse_basica_"),
    (r"^matriculas_superior__", r"^sinopse_superior_"),
    (r"^bolsas_pos_pesquisa__capes", r"^geocapes_concessao_"),
    (r"^bolsas_pos_pesquisa__cnpq_pos", r"^tab_04_02_"),
    (r"^bolsas_pos_pesquisa__cnpq_total", r"^tab_04_01_"),
    (r"^gasto_publico_educacao_pib__investimento_direto", r"^investimento_pib_direto"),
    (r"^gasto_publico_educacao_pib__investimento_total", r"^investimento_pib_total"),
    (r"^gasto_publico_educacao_pib__", r"^wdi_SE\.XPD\.TOTL\.GD\.ZS"),
    (r"^(bm_gini|pobreza|pobreza_extrema)__", r"^wdi_pobreza_gini"),
    (r"^informalidade_pnad$", r"^DISOC_INFI_\d"),
    (r"^salario_minimo_(nominal|real)$", r"^MTE12_SALMIN12_\d"),
    (r"^ndb_", r"^lista_projetos_brasil_p"),
    # saúde
    (r"^(mortalidade_infantil__ibge_estimada|esperanca_vida__)", r"^projecoes_2024_tab4_indicadores_"),
    (r"^mortalidade_infantil__ms_corrigida$", r"^ficha_MRT-1-01_"),
    (r"^mortalidade_infantil__", r"^(sim_obitos_infantis_ano|sinasc_nascidos_vivos_ano)_"),
    (r"^mortalidade_materna__ms_corrigida$", r"^ficha_MRT-2-01_"),
    (r"^mortalidade_materna__", r"^(sim_obitos_maternos_ano|sinasc_nascidos_vivos_ano)_"),
    (r"^cobertura_vacinal__\w+_sipni$", r"^pni_coberturas_ano_"),
    (r"^cobertura_vacinal__\w+_rnds$", r"^cobertura_residencia_ano_imunobiologico_"),
    (r"^despesa_asps_uniao__", r"^(rreo_asps_uniao|rreo_uniao_dezembro)_"),
    (r"^leitos__", r"^cnes_leitos_internacao_dezembro_"),
    (r"^medicos__cnes_cbo1994", r"^cnes_medicos_cbo1994_dezembro_"),
    (r"^medicos__", r"^cnes_medicos_cbo2002_dezembro_"),
    (r"^cobertura_atencao_primaria__potencial_aps", r"^cobertura_aps_potencial_brasil_"),
    (r"^cobertura_atencao_primaria__cadastro_pns", r"^cobertura_pns_brasil_"),
    (r"^cobertura_atencao_primaria__", r"^cobertura_ab_brasil_"),
    (r"^bm_(mortalidade_infantil|mortalidade_materna|esperanca_vida|vacinacao|gasto_publico_saude)__", r"^wdi_saude_\d"),
    # social
    (r"^transferencia_renda_familias__(bolsa_escola|bolsa_alimentacao|cartao_alimentacao|auxilio_gas)_", r"^politicas_sociais_acompanhamento_analise_13_2007_"),
    (r"^transferencia_renda_(familias|beneficio_medio)__", r"^transferencia_renda_mensal_"),
    (r"^transferencia_renda_despesa__", r"^despesa_transferencia_renda_\d"),
    (r"^auxilio_emergencial__pessoas$", r"^auxilio_emergencial_mensal_"),
    (r"^auxilio_emergencial__", r"^despesa_transferencia_renda_202[01]_"),
    (r"^bpc__", r"^bpc_mensal_"),
    (r"^cadunico__", r"^cadunico_mensal_"),
    (r"^inseguranca_alimentar__", r"^t6665_v"),
    (r"^bm_(subalimentacao|inseguranca_alimentar)__", r"^wdi_seguranca_alimentar_"),
    (r"^habitacao_unidades__subsidiado", r"^mcmv_subsidiado_"),
    (r"^habitacao_unidades__financiado", r"^mcmv_financiado_sintetico_"),
    (r"^habitacao_unidades__total$", r"^mcmv_(subsidiado|financiado_sintetico)_"),
    (r"^deficit_habitacional__(pnad_2007_2015|censo_2010)", r"^serie_temporal_deficit_2007_2015_"),
    (r"^deficit_habitacional__pnadc", r"^deficit_(2016_2019_reponderado|2022|2023|2024)_"),
]
BRUTOS_POR_FONTE = [(r"SGS (\d+)", "^sgs{1}_"), (r"SIDRA (\d+)", "^t{1}_"), (r"WEO (\w+)", "^weo_{1}_"),
                    (r"PCPS", "^pcps_"), (r"FRED (\w+)", "^fred_{1}"), (r"Comex", "^exp_")]


def brutos_da_serie(serie, fontes, brutos):
    """Arquivos brutos de uma série, do mais recente para o mais antigo."""
    padrao = None
    for regra, arquivo in BRUTOS_POR_ID:
        m = re.search(regra, serie)
        if m:
            padrao = arquivo.replace("{1}", m.group(1) or "") if m.groups() else arquivo
            break
    if padrao is None:
        fonte = fontes.get(serie, "")
        derivada = re.match(r"cálculo do projeto sobre (\w+)", fonte)
        if derivada and derivada.group(1) in fontes and derivada.group(1) != serie:
            return brutos_da_serie(derivada.group(1), fontes, brutos)
        fonte = re.sub(r";? ?(reais de \d{4} pelo |e )?IPCA[^;]*\(SIDRA 1737\)", "", fonte)
        for regra, arquivo in BRUTOS_POR_FONTE:
            m = re.search(regra, fonte)
            if m:
                padrao = arquivo.replace("{1}", m.group(1) if m.groups() else "")
                break
    if padrao is None:
        return []
    achados = [a for a in brutos if re.search(padrao, a)]
    return sorted(achados, key=lambda a: (brutos[a]["data"][:10], a), reverse=True)


# Brutos acima deste tamanho ficam no repositório, mas não vão para o site (limite de envio do
# Vercel gratuito): o link "Extraído em" leva à URL de origem, e o manifesto guarda o SHA-256.
LIMITE_BRUTO_SITE = 2 * 1024 * 1024


def fora_do_git(caminhos):
    """Caminhos ignorados pelo git (arquivos grandes ou com dados pessoais): o site linka a URL de origem."""
    if not caminhos:
        return set()
    r = subprocess.run(["git", "check-ignore", "--stdin"], input="\n".join(caminhos), capture_output=True,
                       text=True, cwd=RAIZ)
    return set(r.stdout.split())


def segmentos(celula):
    """Célula de tabela markdown -> [[texto, url ou None], ...], sem negrito."""
    celula = celula.replace("**", "")
    partes, pos = [], 0
    for m in re.finditer(r"\[([^\]]+)\]\(([^)\s]+)\)", celula):
        if m.start() > pos:
            partes.append([celula[pos:m.start()], None])
        partes.append([m.group(1), m.group(2)])
        pos = m.end()
    if pos < len(celula):
        partes.append([celula[pos:], None])
    return partes


def tabela_md(texto, inicio_cabecalho):
    """Primeira tabela markdown cujo cabeçalho começa com o texto dado."""
    linhas = texto.splitlines()
    for i, linha in enumerate(linhas):
        if linha.startswith(inicio_cabecalho):
            cab = [c.strip() for c in linha.strip().strip("|").split("|")]
            corpo = []
            for outra in linhas[i + 2:]:
                if not outra.startswith("|"):
                    break
                celulas = [c.strip() for c in outra.strip().strip("|").split("|")]
                corpo.append({k: segmentos(v) for k, v in zip(cab, celulas)})
            return {"colunas": cab, "linhas": corpo}
    return {"colunas": [], "linhas": []}


def brics(caminho):
    """Tabelas do estudo de caso do BRICS (temas/politica-externa/brics.md)."""
    texto = caminho.read_text(encoding="utf-8")
    verificacao = tabela_md(texto, "| # | Afirmação")
    status = Counter("".join(t for t, _ in l.get("Status", [["", None]])).strip().split(" ")[0].lower()
                     for l in verificacao["linhas"])
    return {
        "comparacao": tabela_md(texto, "| Dimensão"),
        "cupulas": tabela_md(texto, "| Ano | Cúpula"),
        "votos_ag": tabela_md(texto, "| Data | Resolução"),
        "votos_cs": tabela_md(texto, "| Data | Documento"),
        "leituras": tabela_md(texto, "| Governo | Leitura favorável"),
        "verificacao_status": dict(status),
        "arquivo": str(caminho.relative_to(RAIZ)),
    }


def main():
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--tema", required=True)
    args = ap.parse_args()

    fichas = {}
    for f in ler_fichas(args.tema):
        s = secoes(f["_arquivo"])
        fichas[f["id"]] = {
            "nome": f["nome"], "unidade": f["unidade"], "camada": f.get("camada", "fato"),
            "fonte": f["fonte"], "link": f.get("link", ""), "codigo_serie": f.get("codigo_serie", ""),
            "media_mandato": f.get("media_mandato", ""), "par_internacional": f.get("par_internacional", ""),
            "arquivo": str(f["_arquivo"].relative_to(RAIZ)),
            "definicao": s.get("definição", []), "como_ler": s.get("como ler", []),
            "ressalvas": s.get("ressalvas", []),
        }

    # séries: todo CSV longo do tema (<tema>.csv e <tema>_*.csv); resumos: *_mandatos.csv
    series = defaultdict(list)
    unidades, fontes, resumo = {}, {}, []
    for caminho in sorted(TRATADOS.glob(f"{args.tema}*.csv")):
        if caminho.stem.endswith("_mandatos"):
            resumo += ler_csv(caminho)
            continue
        linhas = ler_csv(caminho)
        if not linhas or list(linhas[0]) != ["indicador", "data", "valor", "unidade", "fonte"]:
            continue
        for linha in linhas:
            series[linha["indicador"]].append([linha["data"], float(linha["valor"])])
            unidades[linha["indicador"]] = linha["unidade"]
            fontes[linha["indicador"]] = linha["fonte"]

    eventos = []
    arquivo_eventos = RAIZ / "eventos" / f"{args.tema}.csv"
    if arquivo_eventos.exists():
        eventos = ler_csv(arquivo_eventos)

    brutos = {}
    for pasta in (RAIZ / "dados" / "brutos").iterdir():
        manifesto = pasta / "manifesto.csv"
        if manifesto.exists():
            for linha in ler_csv(manifesto):
                brutos[linha["arquivo"]] = {"pasta": pasta.name, "url": linha["consulta_url"],
                                            "data": linha["data_extracao"]}
    brutos_serie = {serie: brutos_da_serie(serie, fontes, brutos) for serie in series}
    # só os brutos usados pelo tema vão para o site (antes ia o índice de todos os temas)
    usados = {a for lista in brutos_serie.values() for a in lista}
    ignorados = fora_do_git([f"dados/brutos/{brutos[a]['pasta']}/{a}" for a in sorted(usados)])
    def grande(a):
        caminho = RAIZ / "dados" / "brutos" / brutos[a]["pasta"] / a
        return caminho.exists() and caminho.stat().st_size > LIMITE_BRUTO_SITE
    brutos = {a: {**brutos[a], "fora_do_git": f"dados/brutos/{brutos[a]['pasta']}/{a}" in ignorados, "no_site": grande(a)}
              for a in sorted(usados)}

    mandatos = [{**m, "inicio": m["inicio"].isoformat(), "fim": m["fim"].isoformat()} for m in ler_mandatos()]
    extras = {}
    leituras = RAIZ / "temas" / args.tema.replace("_", "-") / "leituras.json"
    if leituras.exists():
        extras["leituras"] = json.loads(leituras.read_text(encoding="utf-8"))
    if args.tema == "politica_externa":
        extras["ndb_projetos"] = ler_csv(TRATADOS / "politica_externa_ndb_projetos.csv")
        extras["brics"] = brics(RAIZ / "temas" / "politica-externa" / "brics.md")
    dados = {
        "tema": args.tema, "gerado_em": HOJE.isoformat(), "mandatos": mandatos, "fichas": fichas,
        "series": series, "unidades": unidades, "fontes": fontes, "resumo": resumo,
        "eventos": eventos, "brutos": brutos, "brutos_serie": brutos_serie, "extras": extras,
    }
    destino = RAIZ / "site" / "dados" / f"{args.tema}.js"
    destino.parent.mkdir(parents=True, exist_ok=True)
    destino.write_text("window.DADOS = " + json.dumps(dados, ensure_ascii=False, separators=(",", ":")) + ";\n",
                       encoding="utf-8")
    sem_bruto = sorted(s for s, lista in brutos_serie.items() if not lista)
    print(f"{destino.relative_to(RAIZ)}: {len(series)} séries, {len(resumo)} linhas de resumo, "
          f"{len(eventos)} eventos, {destino.stat().st_size // 1024} KB")
    if sem_bruto:
        print(f"  séries sem arquivo bruto ligado ({len(sem_bruto)}): {', '.join(sem_bruto)}")


if __name__ == "__main__":
    main()
