"""Monta os CSVs do NDB (projetos no Brasil e resumo anual) a partir dos brutos.

Uso:
    python3 scripts/montar_ndb.py

Lê dados/brutos/ndb/ (as páginas dos projetos do Brasil em ndb.int e os relatórios
anuais) e grava:
    dados/tratados/politica_externa_ndb_projetos.csv  um projeto por linha
    dados/tratados/politica_externa_ndb.csv           resumo anual (formato longo)

O anexo "List of Projects Approved by NDB" do Relatório Anual 2025 (p. 181-189 do PDF)
e os números lidos nos gráficos dos relatórios estão transcritos abaixo, com a página
de origem, porque o PDF não tem tabela legível por máquina. Os brutos foram baixados
em 5/10/2026; ver dados/brutos/ndb/manifesto.csv e dados/tratados/politica_externa_notas.md.
"""
import csv, datetime as dt, html, re

from comum import RAIZ, brutos_mais_recentes

BRUTOS = RAIZ / 'dados' / 'brutos' / 'ndb'
TRATADOS = RAIZ / 'dados' / 'tratados'
SITE = 'https://www.ndb.int'
_lista = brutos_mais_recentes('ndb', 'lista_projetos_brasil_p1')
DATA = _lista[0].stem.rsplit('_', 1)[1] if _lista else '2026-10-05'

slugs = []
for n in (1, 2, 3):
    s = (BRUTOS / f'lista_projetos_brasil_p{n}_{DATA}.html').read_text(encoding='utf-8', errors='replace')
    for m in re.finditer(r'project-card-title h5-style"><a class="card-link" href="([^"]+)"', s):
        slug = m.group(1).rstrip('/').rsplit('/', 1)[1]
        if slug not in slugs:
            slugs.append(slug)
assert len(slugs) == 36, len(slugs)


# ---------------------------------------------------------------- 2. páginas
def limpar(x):
    return re.sub(r'\s+', ' ', html.unescape(re.sub(r'<[^>]+>', ' ', x))).strip()


def ler_pagina(slug):
    s = (BRUTOS / f'projeto_{slug}_{DATA}.html').read_text(encoding='utf-8', errors='replace')
    d = {'slug': slug}
    d['nome'] = limpar(re.search(r'<h2 class="project-banner-title">(.*?)</h2>', s, re.S).group(1))
    for chave, pad in [('pais', r'project-banner-country text-large-bold">(.*?)</div>'),
                       ('situacao', r'project-banner-status-value text-large-bold">(.*?)</div>'),
                       ('setor', r'project-banner-key-area-focus-name text-large-bold">(.*?)</div>'),
                       ('tipo', r'project-banner-type-name text-large-bold">(.*?)</div>')]:
        d[chave] = limpar(re.search(pad, s, re.S).group(1))
    fatos = {limpar(a).lower(): limpar(b) for a, b in re.findall(
        r'info-title-card-info text-extra-small">(.*?)</div>\s*<div class="info-title-card-title text-extra-large-bold">(.*?)</div>', s, re.S)}
    d['fatos'] = fatos
    d['aprovacao'] = fatos.get('financing approval date')
    d['conceito'] = fatos.get('concept approval date')
    d['limite'] = fatos.get('current limit of ndb financing') or fatos.get('current limit of financing') or fatos.get('proposed limit of ndb financing')
    d['tomador'] = fatos.get('borrower') or fatos.get('fund manager')
    d['rotulo_tomador'] = 'Borrower' if fatos.get('borrower') else 'Fund Manager'
    return d


def valor_em_unidades(texto):
    """'USD 1.2 billion equivalent' -> ('USD', 1200000000.0)."""
    m = re.match(r'(USD|EUR|RMB)\s+([\d,.]+)\s+(million|billion)', texto)
    moeda, num, escala = m.group(1), float(m.group(2).replace(',', '')), m.group(3)
    return ('CNY' if moeda == 'RMB' else moeda), num * (1e9 if escala == 'billion' else 1e6)


def data_iso(texto):
    return dt.datetime.strptime(texto.strip(), '%d %B %Y').date().isoformat()


# ---------------------------------------------------------------- 3. anexo do Relatório Anual 2025
# (nº no anexo, ano, tipo, moeda, valor aprovado em milhões, nota do anexo, slug no site)
ANEXO = [
    (1, 2016, 'Non-sovereign', 'USD', 300, 'Fully repaid in 2022.', 'bndesbrazil'),
    (15, 2018, 'Sovereign', 'USD', 50, '', 'para-sustainable-municipalities-project-brazil'),
    (16, 2018, 'Sovereign', 'USD', 71, 'Cancelled in 2021.', 'maranhao-road-corridor-south-north-integration'),
    (17, 2018, 'Non-sovereign', 'USD', 200, 'Fully repaid in 2021.', 'environmental-protection-project-brazil'),
    (44, 2019, 'Sovereign', 'USD', 500, 'Restructured in 2021 into a USD 500 million loan to BNDES with sovereign guarantee for BNDES Clima.', 'bndes-clima-sustainable-financing-to-support-global-climate-change-mitigation-and-adaptation-in-brazil'),
    (46, 2019, 'Non-sovereign', 'USD', 300, 'Fully repaid in 2025. Includes a USD 50 million portion, which could be delivered in RMB.', 'brazil-north-region-transportation-infrastructure-improvement-project'),
    (50, 2019, 'Non-sovereign', 'USD', 100, '', 'brazil-patria-infrastructure-fund-iv'),
    (57, 2020, 'Sovereign', 'USD', 50, 'Cancelled in 2025.', 'brazil-teresina-educational-infrastructure-program'),
    (60, 2020, 'Sovereign', 'USD', 1000, '', 'brazil-emergency-assistance-program-combating-covid-19'),
    (66, 2020, 'Sovereign', 'USD', 1000, '', 'brazil-brazil-emergency-assistance-program-economic-recovery'),
    (68, 2020, 'Sovereign', 'USD', 1200, '', 'brazil-bndes-ndb-sustainable-infrastructure-project'),
    (69, 2020, 'Sovereign', 'EUR', 135, '', 'brazil-brde-sustainable-infrastructure-program'),
    (70, 2020, 'Sovereign', 'USD', 75, '', 'brazil-curitibas-bus-rapid-transit-rideability-improvement-project'),
    (76, 2021, 'Sovereign', 'USD', 153, 'Cancelled in 2022.', 'brazil-para-ii-transport-infrastructure-regional-development'),
    (79, 2021, 'Sovereign', 'USD', 40, '', 'sorocaba-mobility-and-urban-development-project'),
    (83, 2022, 'Sovereign', 'USD', 90, 'Cancelled in 2025.', 'desenvolve-sp-sustainable-infrastructure-project'),
    (84, 2022, 'Non-sovereign', 'USD', 50, 'Cancelled in 2025.', 'fonplata-sustainable-infrastructure-project'),
    (85, 2022, 'Non-sovereign', 'USD', 200, '', 'banco-do-brasil-sustainable-finance-project'),
    (86, 2022, 'Sovereign', 'USD', 300, 'Cancelled in 2024.', 'sabesp-investment-program'),
    (94, 2022, 'Sovereign', 'USD', 84, '', 'urban-and-sustainable-infrastructure-program-aracaju-city-of-the-future'),
    (95, 2022, 'Non-sovereign', 'USD', 80, 'Cancelled in 2023.', 'water-and-wastewater-services-expansion-project-in-manaus'),
    (97, 2023, 'Sovereign', 'USD', 202, '', 'pernambuco-water-and-sanitation-efficiency-and-expansion-project'),
    (99, 2023, 'Sovereign', 'USD', 120, '', 'aparecida-de-goiania-100-years-infrastructure-program'),
    (105, 2023, 'Sovereign', 'USD', 200, 'Cancelled in 2025.', 'bdmg-infrastructure-and-sustainable-development-financing-project'),
    (108, 2024, 'Sovereign', 'USD', 61, '', 'paraiba-water-supply-infrastructure-project'),
    (109, 2024, 'Sovereign', 'USD', 58, '', 'serra-urban-mobility-project-requalifica-serra'),
    (112, 2024, 'Non-sovereign', 'RMB', 1425, '', 'electricity-distribution-infrastructure-modernization-project'),
    (118, 2024, 'Sovereign', 'EUR', 94, '', 'brasilia-capital-of-solar-lighting-project'),
    (121, 2025, 'Sovereign', 'USD', 50, '', 'para-sanitation-development-project'),
    (125, 2025, 'Non-sovereign', 'RMB', 1400, '', 'serra-da-palmeira-wind-power-project'),
    (127, 2025, 'Non-sovereign', 'RMB', 2150, '', 'graca-aranha-silvania-energy-transmission-project'),
    (133, 2025, 'Sovereign', 'USD', 320, 'USD 320 million equivalent in USD or RMB, depending on the borrower before signing the loan agreement.', 'brazil-smart-hospital-project'),
    (134, 2025, 'Sovereign', 'USD', 150, '', 'integration-social-and-sustainable-development-program-of-maceio'),
    (135, 2025, 'Sovereign', 'USD', 500, '', 'regional-sustainable-infrastructure-development-program'),
]
FUNDO_CLIMA = 'bndes-clima-sustainable-financing-to-support-global-climate-change-mitigation-and-adaptation-in-brazil'

SETORES = {
    'Clean Energy and Energy Efficiency': 'Energia limpa e eficiência energética',
    'Transport Infrastructure': 'Infraestrutura de transporte',
    'Water and Sanitation': 'Água e saneamento',
    'Environmental Protection': 'Proteção ambiental',
    'Social Infrastructure': 'Infraestrutura social',
    'Digital Infrastructure': 'Infraestrutura digital',
    'Multiple Areas': 'Múltiplas áreas',
    'COVID-19 Emergency Assistance': 'Assistência emergencial contra a COVID-19',
}
SITUACOES = {'Approved': 'aprovado', 'Completed': 'concluído', 'Cancelled': 'cancelado', 'Proposed': 'proposto'}


def governo(data):
    d = dt.date.fromisoformat(data)
    with open(RAIZ / 'metodologia' / 'mandatos.csv', encoding='utf-8') as f:
        for m in csv.DictReader(f):
            if dt.date.fromisoformat(m['inicio']) <= d <= dt.date.fromisoformat(m['fim']):
                return m['id']
    raise ValueError(data)


def fmt(x):
    return str(int(round(x))) if abs(x - round(x)) < 1e-6 else f'{x:.2f}'


def main():
    paginas = {s: ler_pagina(s) for s in slugs}
    assert all(p['pais'] == 'Brazil' for p in paginas.values())
    anexo = {a[6]: a for a in ANEXO}
    assert len(anexo) == 34 and set(anexo) <= set(paginas)
    avisos = []
    linhas = []
    for slug, p in paginas.items():
        url = f'{SITE}/project/{slug}/'
        situacao = SITUACOES[p['situacao']]
        moeda_web, valor_web = valor_em_unidades(p['limite'])
        if slug in anexo:
            num, ano, tipo, moeda_a, mi, nota, _ = anexo[slug]
            moeda_a = 'CNY' if moeda_a == 'RMB' else moeda_a
            data = data_iso(p['aprovacao'])
            if slug == FUNDO_CLIMA:
                # Aprovado em 14/10/2019 (notícia do NDB de 24/10/2019); a data do site é a da reestruturação.
                assert data == '2021-06-29'
                data = '2019-10-14'
            if int(data[:4]) != ano:
                avisos.append(f'{slug}: ano no site {data[:4]} x anexo {ano}')
            if p['tipo'].lower() != tipo.lower():
                avisos.append(f'{slug}: tipo {p["tipo"]} x {tipo}')
            if moeda_web == moeda_a and abs(valor_web / 1e6 - mi) <= 0.5:
                valor = valor_web  # mesmo valor, com mais casas no site
            else:
                valor = mi * 1e6
                avisos.append(f'{slug}: limite atual no site {p["limite"]} x aprovado no anexo {moeda_a} {mi} milhões; usado o do anexo')
            m = re.search(r'Cancelled in (\d{4})', nota)
            if situacao == 'cancelado':
                situacao = f'cancelado em {m.group(1)}' if m else 'cancelado (ano não informado no Relatório Anual 2025)'
            elif m:
                avisos.append(f'{slug}: anexo diz cancelado, site diz {p["situacao"]}')
            nome = p['nome']
            tomador = p['tomador']
            if slug == FUNDO_CLIMA:
                nome = 'Fundo Clima – Brazil National Climate Fund Project (reestruturado em 2021 como BNDES Clima – Sustainable Financing to Support Global Climate Change Mitigation and Adaptation in Brazil)'
                tomador = 'Banco Nacional de Desenvolvimento Economico e Social (desde a reestruturação de 2021; na aprovação, The Federative Republic of Brazil)'
            linhas.append([nome, tomador, SETORES[p['setor']], data, fmt(valor), moeda_a, situacao, governo(data), url])
        else:
            assert p['situacao'] == 'Proposed' and p['aprovacao'] is None, slug
            linhas.append([p['nome'], p['tomador'], SETORES[p['setor']], '', fmt(valor_web), moeda_web,
                           f'proposto (conceito aprovado em {data_iso(p["conceito"])})', '', url])
    linhas.sort(key=lambda l: (l[3] or '9999', l[0]))
    with open(TRATADOS / 'politica_externa_ndb_projetos.csv', 'w', newline='', encoding='utf-8') as f:
        w = csv.writer(f)
        w.writerow(['projeto', 'tomador', 'setor', 'data_aprovacao', 'valor', 'moeda', 'situacao', 'governo_na_aprovacao', 'fonte_url'])
        w.writerows(linhas)

    # ------------------------------------------------------------ resumo anual
    ar = lambda ano, pg, quadro: f'NDB/Relatório Anual {ano} ({quadro}, p. {pg} do PDF)'
    fonte_anexo = 'NDB/Relatório Anual 2025 (anexo List of Projects Approved by NDB, p. 181-189 do PDF)'
    # Aprovações do ano no Brasil em US$ milhões, como publicadas pelo NDB nos anos com moeda além do dólar.
    usd_publicado = {
        2020: (3491, ar(2020, 55, 'gráfico Evolution of approvals by country')),
        2024: (412, ar(2024, 7, 'gráfico Approvals by country')),
        2025: (1528, ar(2025, 9, 'gráfico Approvals by country')),
    }
    # Conferência com os relatórios nos anos só em dólar.
    conferido = {2016: 300, 2018: 321, 2019: 900, 2023: 522}
    resumo = []
    un_n = 'número de projetos'
    un_usd = 'US$ milhões (equivalente calculado pelo NDB, câmbio de fim de ano)'
    for ano in range(2016, 2026):
        do_ano = [a for a in ANEXO if a[1] == ano]
        resumo.append(['ndb_projetos_aprovados_brasil_n', str(ano), str(len(do_ano)), un_n, fonte_anexo])
    for ano in range(2016, 2026):
        do_ano = [a for a in ANEXO if a[1] == ano]
        moedas = {a[3] for a in do_ano}
        if moedas <= {'USD'}:
            total = sum(a[4] for a in do_ano)
            if ano in conferido:
                assert conferido[ano] == total, (ano, total)
            resumo.append(['ndb_aprovacoes_brasil_usd', str(ano), str(total), un_usd, fonte_anexo + '; soma dos valores em USD'])
        else:
            v, fonte = usd_publicado[ano]
            resumo.append(['ndb_aprovacoes_brasil_usd', str(ano), str(v), un_usd, fonte])
    desembolsos = {
        2023: (1155, ar(2023, 9, 'gráfico Disbursements by country')),
        2024: (815, ar(2024, 10, 'gráfico Disbursements by country')),
        2025: (406, ar(2025, 12, 'gráfico Disbursements by country')),
    }
    for ano, (v, fonte) in desembolsos.items():
        resumo.append(['ndb_desembolsos_brasil_usd', str(ano), str(v), un_usd, fonte])
    with open(TRATADOS / 'politica_externa_ndb.csv', 'w', newline='', encoding='utf-8') as f:
        w = csv.writer(f)
        w.writerow(['indicador', 'data', 'valor', 'unidade', 'fonte'])
        w.writerows(resumo)
    print(len(linhas), 'projetos;', len(resumo), 'linhas no resumo')
    for a in avisos:
        print('AVISO', a)


if __name__ == '__main__':
    main()
