#!/usr/bin/env python3
"""Emprego e renda: coleta e tratamento do tema `emprego_renda`.

Executa o pré-registro de "Emprego e renda" (metodologia/indicadores_fixados.md): nove
indicadores e a comparação com pares. Só biblioteca padrão (D-004).

Uso:
    python3 scripts/coletar_emprego_renda.py              # coleta e trata
    python3 scripts/coletar_emprego_renda.py --so-tratar  # trata os brutos mais recentes, sem baixar
    python3 scripts/resumir.py --tema emprego_renda       # depois: resumo por mandato

Não rode `scripts/tratar.py --tema emprego_renda`: ele só sabe tratar a ficha do FMI e
sobrescreveria dados/tratados/emprego_renda.csv. Este tema é tratado aqui.

Brutos. Cada download vai intocado para dados/brutos/<fonte>/, com linha no manifesto da
pasta (comum.salvar_bruto). Se a mesma consulta já foi baixada hoje, o arquivo do dia é
reaproveitado e a fonte não é chamada de novo (o firewall do Banco Mundial bloqueia rajadas).

    ibge_sidra/  PNAD Contínua (tabelas 6381, 6390, 8529, 7435) e PME (2176), com os
                 metadados e a lista de períodos (data da última modificação) de cada tabela;
                 a tabela 4562 (taxa anual de desocupação do IBGE) só para conferência
    ipeadata/    catálogo inteiro (Metadados), metadados e valores de PAN12_TD12 (PME, só
                 conferência), DISOC_INFI e MTE12_SALMIN12
    bm_wdi/      Banco Mundial, WDI: SI.POV.DDAY, SI.POV.UMIC, SI.POV.GINI e os metadados
    bm_pip/      Banco Mundial, PIP: pesquisa, tipo de bem-estar e comparabilidade por ano
    dbnomics/    espelho do WDI, só se a API do Banco Mundial recusar o acesso
    fmi_weo/     WEO, taxa de desemprego (LUR), e a mesma consulta só para o Brasil com
                 todos os atributos, que traz a data de publicação da edição

Saídas:
    dados/tratados/emprego_renda.csv               indicador,data,valor,unidade,fonte
    dados/tratados/emprego_renda_pares_paises.csv  indicador,pais,data,valor,unidade,fonte
    seção "Extração" das fichas em fontes/fichas/emprego_renda/

Datas: AAAA (anual) e AAAA-MM (mensal). Trimestre móvel e trimestre fechado levam o último
mês do período (jan-fev-mar/2012 = 2012-03; 4º tri/2015 = 2015-12). O mês corrente fica
fora; dados do FMI a partir do ano corrente são projeções e ficam fora (D-017).

Valores reais (D-015): real = nominal × (média do IPCA número-índice em 2025 / índice do
período), com o índice `ipca_numero_indice` de dados/tratados/economia.csv. Para o trimestre
móvel, o índice do período é a média dos três meses do trimestre, o critério que reproduz a
série real do próprio IBGE (a conferência no fim do script mostra a diferença).
"""

import argparse
import gzip
import json
import re
import statistics
import sys
import time
import urllib.error
import urllib.parse
from collections import defaultdict

from comum import (BRUTOS, HOJE, RAIZ, TRATADOS, baixar, brutos_mais_recentes, escrever_csv,
                   ler_csv, ler_fichas, ler_json, ler_manifesto, salvar_bruto)
from coletar import SDMX_IMF, baixar_json, coletar_weo, nome_sidra, paises_pares
from tratar import Saida, ler_grupos, mediana_por_ano, mes_anterior, tratar_weo

TEMA = "emprego_renda"
ANO_INICIAL = "2000"  # mesmo corte das séries do tema economia
ANO_CORRENTE = HOJE.year
MES_CORRENTE = f"{HOJE.year:04d}-{HOJE.month:02d}"
ANO_BASE = "2025"  # D-015: reais a preços médios de 2025

# ---------------------------------------------------------------- consultas

SIDRA = {  # id da série -> caminho da API do SIDRA
    "desocupacao_pnadc": "/t/6381/n1/all/v/4099/p/all",
    "rendimento": "/t/6390/n1/all/v/5929,5933/p/all",  # 5929 nominal; 5933 real (só conferência)
    "informalidade_pnadc": "/t/8529/n1/all/v/12466/p/all",
    "gini": "/t/7435/n1/all/v/10681/p/all",
    "desocupacao_pme": "/t/2176/n110/all/v/1031/p/all/c2/6794",  # seis RMs, sexo = total
}
SIDRA_CONFERENCIA = {  # baixadas só para conferência; não viram série
    "desocupacao_anual": "/t/4562/n1/all/v/4099/p/all",  # "taxa anual" do IBGE (PNAD Contínua anual)
}
IBGE_AGREGADOS = "https://servicodados.ibge.gov.br/api/v3/agregados/{tabela}/{recurso}"

IPEA = "http://www.ipeadata.gov.br/api/odata4"
IPEA_SERIES = ["PAN12_TD12", "DISOC_INFI", "MTE12_SALMIN12"]

BM_INDICADORES = {"pobreza_extrema": "SI.POV.DDAY", "pobreza": "SI.POV.UMIC", "bm_gini": "SI.POV.GINI"}
PAUSA_BM = 15  # segundos entre consultas ao Banco Mundial
PIP_URL = ("https://api.worldbank.org/pip/v1/pip?country=all&year=all&povline=3&fill_gaps=false"
           "&welfare_type=all&reporting_level=all&ppp_version=2021&format=json")


def paises():
    return ["BRA"] + paises_pares()


def url_wdi():
    return (f"https://api.worldbank.org/v2/country/{';'.join(paises())}/indicator/"
            f"{';'.join(BM_INDICADORES.values())}?source=2&format=json&per_page=20000")


def url_wdi_metadados(codigo):
    # um indicador por consulta: com vários códigos separados por ";" a resposta vem vazia
    return f"https://api.worldbank.org/v2/indicator/{codigo}?format=json"


def url_dbnomics():
    dims = {"indicator": list(BM_INDICADORES.values()), "country": paises()}
    return "https://api.db.nomics.world/v22/series/WB/WDI?" + urllib.parse.urlencode(
        {"dimensions": json.dumps(dims, separators=(",", ":")), "observations": "1", "limit": "1000"})


def url_weo_atributos():
    return (SDMX_IMF.format(fluxo="WEO", chave="BRA.LUR.A")
            + "?dimensionAtObservation=TIME_PERIOD&attributes=all&measures=all")


# ---------------------------------------------------------------- coleta

def bruto_do_dia(fonte, nome, url):
    """Arquivo de hoje com a mesma consulta, se houver."""
    achados = [linha["arquivo"] for linha in ler_manifesto(fonte).values()
               if linha["consulta_url"] == url and linha["data_extracao"].startswith(HOJE.isoformat())
               and linha["arquivo"].startswith(f"{nome}_")]
    achados = [BRUTOS / fonte / a for a in achados if (BRUTOS / fonte / a).exists()]
    return sorted(achados)[-1] if achados else None


def obter(fonte, nome, url, validar, baixador=baixar, ext="json"):
    existente = bruto_do_dia(fonte, nome, url)
    if existente:
        print(f"  {existente.relative_to(RAIZ)} (já baixado hoje)")
        return existente
    conteudo = baixador(url)
    validar(conteudo)
    caminho = salvar_bruto(fonte, nome, conteudo, url, ext)
    print(f"  {caminho.relative_to(RAIZ)} ({len(conteudo)} bytes)")
    return caminho


def baixar_ibge_api(url):
    """servicodados.ibge.gov.br responde em gzip mesmo sem o cliente pedir. Grava-se o JSON
    descomprimido: a compressão é do transporte, não do conteúdo."""
    conteudo = baixar(url)
    if conteudo[:2] == b"\x1f\x8b":
        conteudo = gzip.decompress(conteudo)
    json.loads(conteudo)
    return conteudo


def json_bom(conteudo):
    """As respostas do Banco Mundial vêm com BOM (utf-8-sig)."""
    return json.loads(conteudo.decode("utf-8-sig"))


def validar_sidra(conteudo):
    d = json.loads(conteudo)
    if not (isinstance(d, list) and len(d) > 1 and "V" in d[0]):
        raise RuntimeError(f"resposta inesperada do SIDRA: {conteudo[:200]!r}")


def validar_json(conteudo):
    json.loads(conteudo)


def validar_ipea(conteudo):
    d = json.loads(conteudo)
    if not d.get("value"):
        raise RuntimeError(f"Ipeadata sem valores: {conteudo[:200]!r}")


def validar_wdi(conteudo):
    d = json_bom(conteudo)
    if not (isinstance(d, list) and len(d) == 2 and isinstance(d[1], list)):
        raise RuntimeError(f"resposta inesperada do Banco Mundial: {conteudo[:300]!r}")
    if d[0].get("total") != len(d[1]):
        raise RuntimeError(f"resposta paginada ({d[0]}); aumente per_page")


def validar_lista_bm(conteudo):
    d = json_bom(conteudo)
    if not isinstance(d, list) or not d:
        raise RuntimeError(f"resposta inesperada: {conteudo[:300]!r}")


def validar_dbnomics(conteudo):
    d = json.loads(conteudo)
    if d["series"]["num_found"] != len(d["series"]["docs"]):
        raise RuntimeError("resposta do DBnomics paginada")


def coletar():
    print("IBGE/SIDRA")
    tabelas = sorted({c.split("/")[2] for c in SIDRA.values()})
    for tabela in tabelas:
        for recurso in ("metadados", "periodos"):
            obter("ibge_sidra", f"t{tabela}_{recurso}", IBGE_AGREGADOS.format(tabela=tabela, recurso=recurso),
                  validar_json, baixador=baixar_ibge_api)
    for caminho in list(SIDRA.values()) + list(SIDRA_CONFERENCIA.values()):
        obter("ibge_sidra", nome_sidra(caminho), "https://apisidra.ibge.gov.br/values" + caminho,
              validar_sidra, baixador=baixar_json)

    print("Ipeadata")
    # O catálogo inteiro, para achar e documentar os códigos ($select quebra este endpoint)
    obter("ipeadata", "catalogo_metadados", f"{IPEA}/Metadados", validar_ipea)
    for codigo in IPEA_SERIES:
        obter("ipeadata", f"metadados_{codigo}", f"{IPEA}/Metadados('{codigo}')", validar_ipea)
        obter("ipeadata", codigo, f"{IPEA}/ValoresSerie(SERCODIGO='{codigo}')", validar_ipea)

    print("FMI/WEO")
    for arq in coletar_weo("LUR", []):  # salvar_bruto não duplica arquivo idêntico do mesmo dia
        print(f"  {arq.relative_to(RAIZ)}")
    obter("fmi_weo", "edicao_weo_BRA_LUR", url_weo_atributos(), validar_json, baixador=baixar_json)

    print("Banco Mundial (poucas consultas, com pausa: o firewall bloqueia acesso automatizado)")
    consultas_bm = [("bm_wdi", "wdi_pobreza_gini", url_wdi(), validar_wdi, True),
                    ("bm_pip", "pip_linha3_todos", PIP_URL, validar_lista_bm, False)]
    consultas_bm += [("bm_wdi", f"wdi_metadados_{c.replace('.', '-')}", url_wdi_metadados(c), validar_wdi, False)
                     for c in BM_INDICADORES.values()]
    for i, (fonte, nome, url, validar, essencial) in enumerate(consultas_bm):
        if i and not bruto_do_dia(fonte, nome, url):
            time.sleep(PAUSA_BM)
        try:
            obter(fonte, nome, url, validar)
        except (urllib.error.HTTPError, RuntimeError, ValueError) as e:
            print(f"  ! Banco Mundial recusou ou respondeu fora do formato ({e})", file=sys.stderr)
            if essencial:
                print("  usando o espelho do DBnomics (WB/WDI); registre a troca nas notas", file=sys.stderr)
                obter("dbnomics", "wdi_pobreza_gini", url_dbnomics(), validar_dbnomics)
            else:
                print(f"  {nome} fica de fora (não é essencial)", file=sys.stderr)


# ---------------------------------------------------------------- leitura dos brutos

def mais_recente(fonte, nome):
    arquivos = brutos_mais_recentes(fonte, nome)
    if not arquivos:
        raise SystemExit(f"Sem bruto '{nome}' em dados/brutos/{fonte}. Rode sem --so-tratar.")
    return arquivos[-1]


def data_sidra(codigo, tipo):
    if tipo == "Ano":
        return codigo
    if tipo == "Trimestre":  # 201504 = 4º trimestre de 2015 -> último mês, 2015-12
        return f"{codigo[:4]}-{int(codigo[4:]) * 3:02d}"
    return f"{codigo[:4]}-{codigo[4:]}"  # Mês e Trimestre Móvel (AAAAMM do último mês)


def ler_sidra(serie):
    """{(variável, data): valor} e o arquivo bruto."""
    arq = mais_recente("ibge_sidra", nome_sidra({**SIDRA, **SIDRA_CONFERENCIA}[serie]))
    linhas = ler_json(arq)
    cab = linhas[0]
    col_var = next(k[:-1] + "C" for k, v in cab.items() if v == "Variável")
    col_per, tipo = next((k[:-1] + "C", v) for k, v in cab.items()
                         if v in ("Ano", "Mês", "Trimestre", "Trimestre Móvel"))
    pontos = {}
    for linha in linhas[1:]:
        try:
            pontos[(linha[col_var], data_sidra(linha[col_per], tipo))] = float(linha["V"])
        except ValueError:  # "...", "-", "X": sem dado
            continue
    return pontos, arq


def variavel(pontos, var):
    return {d: v for (vv, d), v in pontos.items() if vv == var}


def ler_ipea(codigo, anual=False):
    arq = mais_recente("ipeadata", codigo)
    pontos = {}
    for v in ler_json(arq)["value"]:
        if v["VALVALOR"] is None or v.get("NIVNOME") not in ("", None, "Brasil"):
            continue
        pontos[v["VALDATA"][:4] if anual else v["VALDATA"][:7]] = float(v["VALVALOR"])
    return pontos, arq


def ler_bm():
    """{indicador: {iso3: {ano: valor}}}, nomes dos indicadores, arquivo e origem."""
    arqs = brutos_mais_recentes("bm_wdi", "wdi_pobreza_gini")
    por_ind, nomes = defaultdict(lambda: defaultdict(dict)), {}
    if arqs:
        cab, linhas = json_bom(arqs[-1].read_bytes())
        for r in linhas:
            nomes[r["indicator"]["id"]] = r["indicator"]["value"]
            if r["value"] is not None:
                por_ind[r["indicator"]["id"]][r["countryiso3code"]][r["date"]] = float(r["value"])
        return por_ind, nomes, arqs[-1], f"WDI, atualizado em {cab.get('lastupdated')}"
    arq = mais_recente("dbnomics", "wdi_pobreza_gini")
    for doc in ler_json(arq)["series"]["docs"]:
        ind, pais = doc["dimensions"]["indicator"], doc["dimensions"]["country"]
        nomes[ind] = doc["series_name"].split(" – ")[1]
        for ano, valor in zip(doc["period"], doc["value"]):
            if valor != "NA":
                por_ind[ind][pais][ano] = float(valor)
    return por_ind, nomes, arq, "espelho DBnomics do WDI"


def janela_anual(obs):
    return {a: v for a, v in obs.items() if ANO_INICIAL <= a < str(ANO_CORRENTE)}


def bruto_do_deflator():
    """Bruto do IPCA do qual economia.csv foi gerado, lido na seção de extração da ficha ipca_12m."""
    ficha = RAIZ / "fontes" / "fichas" / "economia" / "ipca_12m.md"
    m = re.search(r"`ipca_numero_indice` \| `(dados/brutos/[^`]+)`", ficha.read_text(encoding="utf-8"))
    return RAIZ / m.group(1) if m else None


def deflator():
    idx = {l["data"]: float(l["valor"]) for l in ler_csv(TRATADOS / "economia.csv")
           if l["indicador"] == "ipca_numero_indice"}
    meses = [f"{ANO_BASE}-{m:02d}" for m in range(1, 13)]
    if any(m not in idx for m in meses):
        raise SystemExit(f"economia.csv não tem os 12 meses de {ANO_BASE} do IPCA; rode o tema economia")
    return idx, sum(idx[m] for m in meses) / 12


def indice_trimestre(idx, mes):
    """Média do número-índice nos três meses do trimestre móvel terminado em `mes`."""
    meses = [mes_anterior(mes, k) for k in range(3)]
    return statistics.mean(idx[m] for m in meses) if all(m in idx for m in meses) else None


# ---------------------------------------------------------------- tratamento

def sem_mes_corrente(pontos):
    return {d: v for d, v in pontos.items() if ANO_INICIAL <= d[:4] and d < MES_CORRENTE}


def publicar_pares(saida, ficha, por_pais, fonte, arquivos, unidade_dif, pesquisa_brasil=None, arquivos_brasil=()):
    """Mesma convenção de tratar.tratar_weo: Brasil, mediana de cada grupo sem o Brasil,
    diferença Brasil menos mediana; valores por país e contagem de países em _pares_paises.

    `pesquisa_brasil` ({ano: texto}) acrescenta à coluna fonte das linhas do Brasil a pesquisa
    e o período comparável de cada ano, porque a série do Banco Mundial passa da PNAD anual
    para a PNAD Contínua em 2012."""
    base = ficha["id"]
    brasil = por_pais.get("BRA", {})
    inicio = len(saida.linhas)
    saida.add(ficha, f"{base}__brasil", brasil, fonte=fonte, arquivos=list(arquivos) + list(arquivos_brasil))
    for grupo, membros in ler_grupos().items():
        med, n = mediana_por_ano(por_pais, membros)
        saida.add(ficha, f"{base}__mediana_{grupo}", med, fonte=f"{fonte}, mediana de {grupo}", arquivos=arquivos)
        dif = {a: brasil[a] - med[a] for a in brasil if a in med}
        saida.add(ficha, f"{base}__brasil_menos_mediana_{grupo}", dif, unidade=unidade_dif,
                  fonte=f"{fonte}, Brasil menos mediana de {grupo}", arquivos=list(arquivos) + list(arquivos_brasil))
        for a in sorted(n):
            saida.paises.append([f"{base}__n_{grupo}", grupo, a, n[a], "países", fonte])
    for pais, obs in sorted(por_pais.items()):
        for a, v in sorted(obs.items()):
            saida.paises.append([base, pais, a, round(v, 4), ficha["unidade"], fonte])
    if pesquisa_brasil:
        for linha in saida.linhas[inicio:]:
            if linha[0].startswith(f"{base}__brasil") and linha[1] in pesquisa_brasil:
                linha[4] = f"{linha[4]}; Brasil: {pesquisa_brasil[linha[1]]}"
        for linha in saida.paises:
            if linha[0] == base and linha[1] == "BRA" and linha[2] in pesquisa_brasil:
                linha[5] = f"{linha[5]}; {pesquisa_brasil[linha[2]]}"


def pesquisas_pip(pais="BRA"):
    """{ano: 'pesquisa, período comparável ...'} do PIP (nível nacional), e o arquivo bruto."""
    arqs = brutos_mais_recentes("bm_pip", "pip_linha3_todos")
    if not arqs:
        return {}, None
    rotulos = {}
    for r in json_bom(arqs[-1].read_bytes()):
        if r.get("country_code") == pais and r.get("reporting_level") == "national":
            visita = " (5ª visita)" if str(r.get("survey_acronym", "")).endswith("-E5") else ""
            rotulos[str(r["reporting_year"])] = (f"{r['survey_acronym']}{visita}, período comparável "
                                                 f"{str(r['comparable_spell']).replace(' - ', '–')}")
    return rotulos, arqs[-1]


def tratar(fichas):
    saida = Saida()
    notas = defaultdict(list)  # id da ficha -> linhas extras na seção de extração
    idx, base = deflator()
    arq_ipca = bruto_do_deflator()
    nota_deflator = (f"Deflator: `ipca_numero_indice` de `dados/tratados/economia.csv` (IBGE/SIDRA 1737), "
                     f"média de {ANO_BASE} = {base:.4f} (dez/1993 = 100).")

    # 1. Desocupação, PNAD Contínua
    p, arq = ler_sidra("desocupacao_pnadc")
    saida.add(fichas["desocupacao_pnadc"], "desocupacao_pnadc", sem_mes_corrente(variavel(p, "4099")),
              fonte="IBGE/SIDRA 6381", arquivos=[arq])

    # 2. Desocupação, PME (SIDRA; o Ipeadata PAN12_TD12 entra só na conferência)
    p, arq = ler_sidra("desocupacao_pme")
    saida.add(fichas["desocupacao_pme"], "desocupacao_pme", sem_mes_corrente(variavel(p, "1031")),
              fonte="IBGE/SIDRA 2176 (PME)", arquivos=[arq])

    # 3. Informalidade, PNAD Contínua (trimestral)
    p, arq = ler_sidra("informalidade_pnadc")
    saida.add(fichas["informalidade_pnadc"], "informalidade_pnadc", sem_mes_corrente(variavel(p, "12466")),
              fonte="IBGE/SIDRA 8529", arquivos=[arq])

    # 4. Informalidade, PNAD anual (Ipea, definição I)
    p, arq = ler_ipea("DISOC_INFI", anual=True)
    saida.add(fichas["informalidade_pnad"], "informalidade_pnad", janela_anual(p),
              fonte="Ipea/Ipeadata DISOC_INFI (PNAD/IBGE)", arquivos=[arq])

    # 5. Rendimento médio habitual de todos os trabalhos, nominal (5929) -> real
    p, arq = ler_sidra("rendimento")
    nominal = sem_mes_corrente(variavel(p, "5929"))
    real = {m: v * base / indice_trimestre(idx, m) for m, v in nominal.items() if indice_trimestre(idx, m)}
    ficha = fichas["rendimento_medio_real"]
    saida.add(ficha, "rendimento_medio_real", real, arquivos=[arq] + ([arq_ipca] if arq_ipca else []),
              fonte="cálculo sobre IBGE/SIDRA 6390 (variável 5929) e IPCA (SIDRA 1737)", casas=2)
    saida.add(ficha, "rendimento_medio_nominal", nominal, unidade="R$ correntes",
              fonte="IBGE/SIDRA 6390 (variável 5929)", arquivos=[arq], casas=2)
    notas["rendimento_medio_real"].append(nota_deflator + " Trimestre móvel: média do índice dos três meses.")
    rendimento_ibge = variavel(p, "5933")

    # 6. Salário mínimo vigente, nominal -> real
    p, arq = ler_ipea("MTE12_SALMIN12")
    nominal_sm = sem_mes_corrente(p)
    real_sm = {m: v * base / idx[m] for m, v in nominal_sm.items() if m in idx}
    ficha = fichas["salario_minimo_real"]
    saida.add(ficha, "salario_minimo_real", real_sm, arquivos=[arq] + ([arq_ipca] if arq_ipca else []),
              fonte="cálculo sobre Ipeadata MTE12_SALMIN12 e IPCA (SIDRA 1737)", casas=2)
    saida.add(ficha, "salario_minimo_nominal", nominal_sm, unidade="R$ correntes",
              fonte="Ipeadata MTE12_SALMIN12", arquivos=[arq], casas=2)
    notas["salario_minimo_real"].append(nota_deflator)

    # 9. Gini, PNAD Contínua anual (série nacional)
    p, arq = ler_sidra("gini")
    saida.add(fichas["gini"], "gini", janela_anual(variavel(p, "10681")), fonte="IBGE/SIDRA 7435", arquivos=[arq])

    # 7, 8 e 9. Pobreza e Gini, Banco Mundial, Brasil e pares
    por_ind, nomes, arq_bm, origem = ler_bm()
    pesquisa_brasil, arq_pip = pesquisas_pip("BRA")
    for ficha_id, codigo in BM_INDICADORES.items():
        por_pais = {pais: janela_anual(obs) for pais, obs in por_ind[codigo].items()}
        por_pais = {pais: obs for pais, obs in por_pais.items() if obs}
        unidade_dif = "pontos do índice (0 a 100)" if codigo == "SI.POV.GINI" else "p.p."
        publicar_pares(saida, fichas[ficha_id], por_pais, f"Banco Mundial/WDI {codigo}", [arq_bm], unidade_dif,
                       pesquisa_brasil=pesquisa_brasil, arquivos_brasil=[arq_pip] if arq_pip else [])
        sem_dado = [p for p in paises() if p not in por_pais]
        notas[ficha_id].append(f"Indicador na fonte ({origem}): {nomes.get(codigo, '?')}. "
                               f"Países sem nenhum dado de {ANO_INICIAL} a {ANO_CORRENTE - 1}: "
                               f"{', '.join(sem_dado) or 'nenhum'}. Contagem de países por ano: "
                               f"`{ficha_id}__n_<grupo>` em `dados/tratados/{TEMA}_pares_paises.csv`. "
                               f"A pesquisa e o período comparável de cada ano do Brasil vêm do PIP "
                               f"e estão na coluna `fonte` das séries `{ficha_id}__brasil*`.")

    # Comparação com pares, FMI/WEO (mesma função do tema economia)
    tratar_weo(fichas["fmi_desocupacao"], saida)
    arq_attr = brutos_mais_recentes("fmi_weo", "edicao_weo_BRA_LUR")
    if arq_attr:
        notas["fmi_desocupacao"].append(f"Edição do WEO: {edicao_weo(arq_attr[-1])} "
                                        f"(`{arq_attr[-1].relative_to(RAIZ)}`).")
    notas["fmi_desocupacao"].append(f"Contagem de países por ano: `fmi_desocupacao__n_<grupo>` em "
                                    f"`dados/tratados/{TEMA}_pares_paises.csv`.")

    citar_auxiliares(notas)
    return saida, notas, {"rendimento_ibge": rendimento_ibge, "idx": idx, "nominal": nominal}


def citar_auxiliares(notas):
    """Cita na ficha os brutos que documentam a série (metadados, datas de modificação,
    catálogo, série de conferência), para que consolidar_fontes.py os ligue ao tema."""
    def caminhos(*pares):
        # prefixo exato: "wdi_metadados" não pode pegar "wdi_metadados_SI-POV-DDAY"
        achados = [[p for p in brutos_mais_recentes(fonte, nome) if p.stem.rsplit("_", 1)[0] == nome]
                   for fonte, nome in pares]
        return ", ".join(f"`{a[-1].relative_to(RAIZ)}`" for a in achados if a)

    for ficha_id, tabela in (("desocupacao_pnadc", "6381"), ("desocupacao_pme", "2176"),
                             ("informalidade_pnadc", "8529"), ("rendimento_medio_real", "6390"), ("gini", "7435")):
        lista = caminhos(("ibge_sidra", f"t{tabela}_metadados"), ("ibge_sidra", f"t{tabela}_periodos"))
        if lista:
            notas[ficha_id].append(f"Metadados da tabela e data da última modificação de cada período: {lista}.")
    lista = caminhos(("ibge_sidra", nome_sidra(SIDRA_CONFERENCIA["desocupacao_anual"])))
    if lista:
        notas["desocupacao_pnadc"].append(f"Taxa anual do IBGE (PNAD Contínua anual, SIDRA 4562), baixada só para "
                                          f"conferência: {lista}.")
    lista = caminhos(("ipeadata", "PAN12_TD12"), ("ipeadata", "metadados_PAN12_TD12"))
    if lista:
        notas["desocupacao_pme"].append(f"Conferência com o Ipeadata (PAN12_TD12, mesma pesquisa): {lista}.")
    for ficha_id, codigo in (("informalidade_pnad", "DISOC_INFI"), ("salario_minimo_real", "MTE12_SALMIN12")):
        lista = caminhos(("ipeadata", f"metadados_{codigo}"), ("ipeadata", "catalogo_metadados"))
        if lista:
            notas[ficha_id].append(f"Metadados da série e catálogo do Ipeadata usado para achar o código: {lista}.")
    for ficha_id, codigo in BM_INDICADORES.items():
        lista = caminhos(("bm_wdi", f"wdi_metadados_{codigo.replace('.', '-')}"))
        if lista:
            notas[ficha_id].append(f"Definição do indicador no WDI: {lista}.")
    lista = caminhos(("bm_wdi", "wdi_metadados"))
    if lista:
        notas["pobreza_extrema"].append(f"A consulta conjunta dos metadados dos três indicadores voltou vazia ({lista}); "
                                        f"as definições vêm das consultas por indicador.")


def edicao_weo(arq):
    d = ler_json(arq)
    attrs = d["data"]["structures"][0]["attributes"].get("dataSet", [])
    valores = d["data"]["dataSets"][0].get("attributes", [])
    lidos = {a["id"]: v for a, v in zip(attrs, valores)}
    pub, atu = lidos.get("PUBLICATION_DATE"), lidos.get("UPDATE_DATE")
    return f"publicada em {pub[0][:10] if pub else '?'}, atualizada em {atu[0][:10] if atu else '?'}"


# ---------------------------------------------------------------- fichas

def atualizar_extracao(ficha, itens, notas=()):
    """Mesma tabela de tratar.atualizar_extracao, com notas opcionais depois da tabela."""
    manifestos = {}
    linhas = ["| Série | Arquivo bruto | Extraído em | Observações | Primeira | Última |",
              "|---|---|---|---|---|---|"]
    for serie_id, arquivos, dados in itens:
        nomes, datas = [], []
        for arq in arquivos:
            fonte = arq.parent.name
            manifestos.setdefault(fonte, ler_manifesto(fonte))
            nomes.append(f"`{arq.relative_to(RAIZ)}`")
            datas.append(manifestos[fonte].get(arq.name, {}).get("data_extracao", "?")[:16].replace("T", " "))
        datas_txt = ", ".join(sorted(set(datas))) or "(derivada)"
        nomes_txt = "<br>".join(nomes) or "(derivada)"
        if dados:
            linhas.append(f"| `{serie_id}` | {nomes_txt} | {datas_txt} | {len(dados)} | {min(dados)} | {max(dados)} |")
        else:
            linhas.append(f"| `{serie_id}` | {nomes_txt} | {datas_txt} | 0 | | |")
    bloco = "\n".join(linhas)
    if notas:
        bloco += "\n\n" + "\n\n".join(notas)
    caminho = ficha["_arquivo"]
    texto = caminho.read_text(encoding="utf-8")
    novo = re.sub(r"(<!-- extracao:inicio -->\n).*?(\n<!-- extracao:fim -->)",
                  lambda m: m.group(1) + bloco + m.group(2), texto, flags=re.S)
    caminho.write_text(novo, encoding="utf-8")


# ---------------------------------------------------------------- conferência

def conferir(saida, extra):
    """Recalcula amostras a partir dos brutos e compara fontes entre si."""
    series = defaultdict(dict)
    for ind, data, valor, _, _ in saida.linhas:
        series[ind][data] = valor
    print("\nConferência")

    # PME: SIDRA 2176 contra Ipeadata PAN12_TD12
    ipea, _ = ler_ipea("PAN12_TD12")
    pme = series["desocupacao_pme"]
    comuns = sorted(set(ipea) & set(pme))
    difs = [abs(ipea[m] - pme[m]) for m in comuns]
    print(f"  PME, SIDRA 2176 x Ipeadata PAN12_TD12: {len(comuns)} meses em comum, maior diferença "
          f"{max(difs):.2f} p.p.; só no SIDRA: {', '.join(sorted(set(pme) - set(ipea))) or 'nenhum'}; "
          f"só no Ipeadata: {', '.join(sorted(set(ipea) - set(pme))) or 'nenhum'}")

    # Rendimento: deflação do projeto contra a série real do IBGE (preços do último trimestre)
    ibge, idx, nominal = extra["rendimento_ibge"], extra["idx"], extra["nominal"]
    ultimo = max(m for m in nominal if indice_trimestre(idx, m))
    for rotulo, indice in (("média dos 3 meses", lambda m: indice_trimestre(idx, m)),
                           ("só o último mês", lambda m: idx.get(m))):
        razoes = [ibge[m] / (nominal[m] * indice(ultimo) / indice(m))
                  for m in nominal if m in ibge and indice(m)]
        print(f"  Rendimento real do IBGE (5933) / nominal deflacionado pelo índice {rotulo}: "
              f"de {min(razoes):.4f} a {max(razoes):.4f}")

    # FMI: Brasil no WEO contra a média anual da PNAD Contínua (quatro trimestres fechados)
    weo = series["fmi_desocupacao__brasil"]
    pnadc = series["desocupacao_pnadc"]
    linha = []
    for a in sorted(weo):
        tri = [pnadc.get(f"{a}-{m}") for m in ("03", "06", "09", "12")]
        media = statistics.mean(tri) if all(t is not None for t in tri) else None
        linha.append(f"{a}: {weo[a]:.2f}" + (f" x {media:.2f}" if media is not None else ""))
    print("  Desemprego do Brasil, WEO x média dos 4 trimestres da PNAD Contínua: " + "; ".join(linha))

    # "Taxa anual" do IBGE (PNAD Contínua anual, SIDRA 4562) x média dos 4 trimestres da 6381
    if brutos_mais_recentes("ibge_sidra", nome_sidra(SIDRA_CONFERENCIA["desocupacao_anual"])):
        anual = variavel(ler_sidra("desocupacao_anual")[0], "4099")
        linha = []
        for a in sorted(anual):
            tri = [pnadc.get(f"{a}-{m}") for m in ("03", "06", "09", "12")]
            if all(t is not None for t in tri):
                linha.append(f"{a}: {anual[a]:.1f} x {statistics.mean(tri):.2f}")
        print("  Taxa anual do IBGE (SIDRA 4562) x média dos 4 trimestres (6381): " + "; ".join(linha))

    # Salário mínimo: meses em que o valor nominal muda
    sm = series["salario_minimo_nominal"]
    mudancas, anterior = [], None
    for m in sorted(sm):
        if sm[m] != anterior and m >= "2002":
            mudancas.append(f"{m} R$ {sm[m]:.2f}")
        anterior = sm[m]
    print("  Salário mínimo, reajustes: " + "; ".join(mudancas))

    # Banco Mundial: WDI contra PIP para o Brasil (pobreza a US$ 3,00 e Gini)
    arqs_pip = brutos_mais_recentes("bm_pip", "pip_linha3_todos")
    if arqs_pip:
        pip = [r for r in json_bom(arqs_pip[-1].read_bytes())
               if r.get("country_code") == "BRA" and r.get("reporting_level") == "national"]
        difs_h = [abs(r["headcount"] * 100 - series["pobreza_extrema__brasil"][str(r["reporting_year"])])
                  for r in pip if str(r["reporting_year"]) in series["pobreza_extrema__brasil"]]
        difs_g = [abs(r["gini"] * 100 - series["bm_gini__brasil"][str(r["reporting_year"])])
                  for r in pip if str(r["reporting_year"]) in series["bm_gini__brasil"]]
        if difs_h:
            print(f"  Brasil, WDI x PIP: {len(difs_h)} anos; maior diferença na pobreza a US$ 3,00: "
                  f"{max(difs_h):.2f} p.p.; no Gini: {max(difs_g):.2f} ponto")
        print("  Brasil no PIP (ano: pesquisa, bem-estar, período comparável): " + "; ".join(
            f"{r['reporting_year']}: {r.get('survey_acronym')}, {r.get('welfare_type')}, {r.get('comparable_spell')}"
            for r in sorted(pip, key=lambda r: r["reporting_year"]) if r["reporting_year"] >= 2000))


def resumo_pip():
    """Tipo de bem-estar, cobertura e anos de pesquisa de cada país, para as fichas e notas."""
    arqs = brutos_mais_recentes("bm_pip", "pip_linha3_todos")
    if not arqs:
        return
    linhas = json_bom(arqs[-1].read_bytes())
    print("\nPIP, por país (2000 em diante): bem-estar | cobertura | anos")
    for pais in paises():
        rs = [r for r in linhas if r.get("country_code") == pais and r.get("reporting_year", 0) >= 2000]
        if not rs:
            print(f"  {pais}: sem pesquisa no PIP")
            continue
        bem = sorted({r.get("welfare_type") for r in rs})
        cob = sorted({r.get("reporting_level") for r in rs})
        anos = sorted({r["reporting_year"] for r in rs})
        print(f"  {pais}: {', '.join(bem)} | {', '.join(cob)} | {', '.join(map(str, anos))}")


# ---------------------------------------------------------------- main

def main():
    ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
    ap.add_argument("--so-tratar", action="store_true", help="não baixa nada; trata os brutos mais recentes")
    args = ap.parse_args()

    if not args.so_tratar:
        coletar()

    fichas = {f["id"]: f for f in ler_fichas(TEMA)}
    saida, notas, extra = tratar(fichas)
    saida.linhas.sort(key=lambda l: (l[0], l[1]))
    escrever_csv(TRATADOS / f"{TEMA}.csv", ["indicador", "data", "valor", "unidade", "fonte"], saida.linhas)
    escrever_csv(TRATADOS / f"{TEMA}_pares_paises.csv", ["indicador", "pais", "data", "valor", "unidade", "fonte"],
                 saida.paises)
    for ficha_id, itens in saida.extracao.items():
        atualizar_extracao(fichas[ficha_id], itens, notas.get(ficha_id, ()))

    series = sorted({l[0] for l in saida.linhas})
    print(f"\n{len(saida.linhas)} linhas, {len(series)} séries -> dados/tratados/{TEMA}.csv")
    for s in series:
        datas = [l[1] for l in saida.linhas if l[0] == s]
        print(f"  {s:60s} {min(datas)} a {max(datas)} ({len(datas)})")
    conferir(saida, extra)
    resumo_pip()


if __name__ == "__main__":
    main()
