<?php defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * patrimonial_helper.php
 * Tokens e configurações lidos do banco (tabela pat_configuracoes)
 * em vez de hardcoded — facilitando atualização sem editar arquivos.
 */

// ═══════════════════════════════════════════════════════════════
// CARREGA TOKENS DO BANCO (fallback para os valores hardcoded
// caso o banco ainda não tenha a tabela criada)
// ═══════════════════════════════════════════════════════════════
function pat_config($chave, $padrao = '') {
    static $cache = null;
    if ($cache === null) {
        $cache = [];
        try {
            $CI =& get_instance();
            $rows = $CI->db->where('chave', $chave)->get('pat_configuracoes')->result();
            // Se chegou aqui sem erro, carrega tudo
            $all = $CI->db->get('pat_configuracoes')->result();
            foreach ($all as $row) $cache[$row->chave] = $row->valor;
        } catch (\Throwable $e) {
            // Tabela ainda não existe — usa padrões hardcoded
        }
    }
    return $cache[$chave] ?? $padrao;
}

// Constantes para retrocompatibilidade (caso algum código use diretamente)
if (!defined('PAT_TOKEN_TRANSPARENCIA')) {
    define('PAT_TOKEN_TRANSPARENCIA', pat_config('transparencia_token', '524bb26d14a015fb4ada75298fa5a7a3'));
}
if (!defined('PAT_DATAJUD_APIKEY')) {
    define('PAT_DATAJUD_APIKEY', pat_config('datajud_apikey', 'cDZHYzlZa0JadVREZDJCendQbXY6SkJlTzNjLV9TRENyQk1RdnFKZGRQdw=='));
}

// ═══════════════════════════════════════════════════════════════
// 1. CNPJ — BrasilAPI v3: EXTRAÇÃO MÁXIMA
// Antes: só razão social, situação, capital, 1 atividade, sócios básicos.
// Agora: TODOS os CNAEs secundários, TODAS as filiais com endereço completo,
//        regime tributário (Simples/MEI), opção/exclusão do Simples,
//        natureza jurídica, data de situação cadastral, motivo, porte completo.
// ═══════════════════════════════════════════════════════════════
function pat_brasilapi_cnpj($cnpj) {
    $cnpj = preg_replace('/\D/', '', $cnpj);
    if (strlen($cnpj) !== 14) return ['erro' => 'CNPJ inválido'];
    $r = pat_http_get("https://brasilapi.com.br/api/cnpj/v1/{$cnpj}");
    if (!$r) return ['erro' => 'Sem resposta BrasilAPI'];

    // CNAEs secundários — atividades não declaradas que podem revelar negócio oculto
    $cnaes_secundarios = [];
    foreach (($r['cnaes_secundarios'] ?? []) as $c) {
        $cnaes_secundarios[] = ($c['codigo'] ?? '') . ' — ' . ($c['descricao'] ?? '');
    }

    // Filiais — endereços onde o devedor pode ter bens/estoque
    $filiais = [];
    foreach (($r['filiais'] ?? []) as $f) {
        $filiais[] = [
            'cnpj'      => $f['cnpj'] ?? '',
            'nome'      => $f['nome_fantasia'] ?? '',
            'situacao'  => $f['descricao_situacao_cadastral'] ?? '',
            'municipio' => ($f['municipio'] ?? '') . '/' . ($f['uf'] ?? ''),
        ];
    }

    return [
        'razao_social'    => $r['razao_social'] ?? '',
        'nome_fantasia'   => $r['nome_fantasia'] ?? '',
        'situacao'        => $r['descricao_situacao_cadastral'] ?? '',
        'data_situacao'   => $r['data_situacao_cadastral'] ?? '',
        'motivo_situacao' => $r['descricao_motivo_situacao_cadastral'] ?? '',
        'abertura'        => $r['data_inicio_atividade'] ?? '',
        'capital'         => $r['capital_social'] ?? 0,
        'municipio'       => ($r['municipio'] ?? '') . '/' . ($r['uf'] ?? ''),
        'endereco_completo'=> trim(($r['descricao_tipo_de_logradouro'] ?? '') . ' ' . ($r['logradouro'] ?? '') . ', ' . ($r['numero'] ?? '') . ' - ' . ($r['bairro'] ?? '')),
        'cep'             => $r['cep'] ?? '',
        'email'           => $r['email'] ?? '',
        'telefone'        => $r['ddd_telefone_1'] ?? '',
        'telefone2'       => $r['ddd_telefone_2'] ?? '',
        'atividade'       => $r['cnae_fiscal_descricao'] ?? '',
        'cnaes_secundarios'=> $cnaes_secundarios,
        'porte'           => $r['porte'] ?? '',
        'natureza'        => $r['descricao_natureza_juridica'] ?? '',
        // Regime tributário — empresa que alega pobreza mas é Lucro Real é suspeito
        'opcao_simples'   => $r['opcao_pelo_simples'] ? 'Optante pelo Simples Nacional' : 'NÃO optante (Lucro Real/Presumido)',
        'data_opcao_simples'=> $r['data_opcao_pelo_simples'] ?? '',
        'opcao_mei'       => $r['opcao_pelo_mei'] ? 'Sim' : 'Não',
        'filiais'         => $filiais,
        'total_filiais'   => count($filiais),
        'socios'          => array_map(function($s) {
            return [
                'nome'  => $s['nome_socio'] ?? '',
                'qual'  => $s['qualificacao_socio'] ?? '',
                'cpf'   => $s['cnpj_cpf_do_socio'] ?? '',
                'desde' => $s['data_entrada_sociedade'] ?? '',
                'pais'  => $s['pais'] ?? 'Brasil',
                'faixa_etaria' => $s['faixa_etaria'] ?? '',
            ];
        }, $r['qsa'] ?? []),
        'fonte' => 'brasilapi'
    ];
}

// ═══════════════════════════════════════════════════════════════
// 2. NOVO — RegistroBR / WHOIS de domínio .br (RDAP, gratuito)
// Revela o titular real (CPF/CNPJ) por trás de um site/e-commerce
// ═══════════════════════════════════════════════════════════════
function pat_registrobr_dominio($dominio) {
    $dominio = strtolower(trim($dominio));
    $dominio = preg_replace('#^https?://#', '', $dominio);
    $dominio = preg_replace('#^www\.#', '', $dominio);
    $dominio = explode('/', $dominio)[0];

    $r = pat_http_get("https://brasilapi.com.br/api/registrobr/v1/{$dominio}");
    if (!$r) return ['erro' => 'Domínio não encontrado ou fora do .br'];

    return [
        'dominio'      => $r['fqdn'] ?? $dominio,
        'status'       => implode(', ', $r['status'] ?? []),
        'criado_em'    => $r['created_at'] ?? '',
        'expira_em'    => $r['expires_at'] ?? '',
        'titular'      => $r['owner'] ?? '',
        'titular_doc'  => $r['document'] ?? '',
        'publication_status' => $r['publication_status'] ?? '',
        'dns'          => $r['hosts'] ?? [],
        'fonte'        => 'brasilapi_registrobr'
    ];
}

// ═══════════════════════════════════════════════════════════════
// 3. NOVO — CVM: Fundos de Investimento por CNPJ
// Cotas de fundos são penhoráveis (Art. 835 IX CPC)
// ═══════════════════════════════════════════════════════════════
function pat_cvm_fundo($cnpj_ou_nome) {
    $doc = preg_replace('/\D/', '', $cnpj_ou_nome);
    $param = strlen($doc) === 14 ? $doc : urlencode($cnpj_ou_nome);
    // Portal de dados abertos CVM — busca de fundos registrados
    $r = pat_http_get("https://dados.cvm.gov.br/dados/FI/CAD/DADOS/cad_fi.json?cnpj={$param}");
    if (!$r || empty($r)) return ['encontrado' => false, 'fundos' => []];
    $fundos = [];
    foreach ((array)$r as $f) {
        $fundos[] = [
            'cnpj'        => $f['CNPJ_FUNDO'] ?? '',
            'nome'        => $f['DENOM_SOCIAL'] ?? '',
            'situacao'    => $f['SIT'] ?? '',
            'classe'      => $f['CLASSE'] ?? '',
            'administrador'=> $f['ADMIN'] ?? '',
            'patrimonio_liquido' => $f['VL_PATRIM_LIQ'] ?? 0,
        ];
    }
    return ['encontrado' => count($fundos) > 0, 'fundos' => $fundos];
}

// ═══════════════════════════════════════════════════════════════
// 4. NOVO — CVM: Lista de Corretoras (cruzamento de intermediários)
// ═══════════════════════════════════════════════════════════════
function pat_cvm_corretora_por_cnpj($cnpj) {
    $cnpj = preg_replace('/\D/', '', $cnpj);
    $r = pat_http_get('https://brasilapi.com.br/api/cvm/corretoras/v1');
    if (!$r) return null;
    foreach ($r as $corretora) {
        if (preg_replace('/\D/', '', $corretora['cnpj'] ?? '') === $cnpj) {
            return [
                'nome_social'   => $corretora['nome_social']    ?? '',
                'nome_comercial'=> $corretora['nome_comercial'] ?? '',
                'status'        => $corretora['status']         ?? '',
                'codigo_cvm'    => $corretora['codigo_cvm']     ?? '',
            ];
        }
    }
    return null;
}

// ═══════════════════════════════════════════════════════════════
// 5. NOVO — Bancos (BrasilAPI) — para montar ofícios corretos
// ═══════════════════════════════════════════════════════════════
function pat_lista_bancos() {
    return pat_http_get('https://brasilapi.com.br/api/banks/v1') ?: [];
}

function pat_banco_por_codigo($codigo) {
    return pat_http_get("https://brasilapi.com.br/api/banks/v1/{$codigo}");
}

// ═══════════════════════════════════════════════════════════════
// 6. NOVO — Câmbio (BrasilAPI) — valores em moeda estrangeira
// ═══════════════════════════════════════════════════════════════
function pat_cotacao_moeda($moeda = 'USD', $data = null) {
    $data = $data ?: date('m-d-Y');
    $r = pat_http_get("https://brasilapi.com.br/api/cambio/v1/cotacao/{$moeda}/{$data}");
    if (!$r) return null;
    return [
        'moeda'     => $moeda,
        'compra'    => $r['cotacoes'][0]['compra'] ?? 0,
        'venda'     => $r['cotacoes'][0]['venda']  ?? 0,
        'data'      => $r['cotacoes'][0]['data_hora_cotacao'] ?? '',
    ];
}

// ═══════════════════════════════════════════════════════════════
// 7. CNPJ — OpenCNPJ (mantido)
// ═══════════════════════════════════════════════════════════════
function pat_opencnpj($cnpj) {
    $cnpj = preg_replace('/\D/', '', $cnpj);
    if (strlen($cnpj) !== 14) return ['erro' => 'CNPJ inválido'];
    $r = pat_http_get("https://api.opencnpj.org/{$cnpj}");
    if (!$r || isset($r['message'])) return ['erro' => 'OpenCNPJ sem resultado'];
    // Schema real confirmado em Jun/2026:
    // razao_social, situacao_cadastral, data_inicio_atividade, capital_social,
    // municipio, uf, email, QSA[]{nome_socio, cnpj_cpf_socio, qualificacao_socio}
    $socios = array_map(function($s) {
        return [
            'nome' => $s['nome_socio']        ?? '',
            'qual' => $s['qualificacao_socio'] ?? '',
            'cpf'  => $s['cnpj_cpf_socio']    ?? '',
        ];
    }, $r['QSA'] ?? []);
    return [
        'razao_social' => $r['razao_social']         ?? '',
        'situacao'     => $r['situacao_cadastral']    ?? '',
        'abertura'     => $r['data_inicio_atividade'] ?? '',
        'capital'      => $r['capital_social']        ?? 0,
        'municipio'    => ($r['municipio'] ?? '') . '/' . ($r['uf'] ?? ''),
        'email'        => $r['email']                 ?? '',
        'socios'       => $socios,
        'fonte'        => 'opencnpj',
    ];
}

// ═══════════════════════════════════════════════════════════════
// 8. CNPJ consolidado — cascata com fallback
// ═══════════════════════════════════════════════════════════════
function pat_consultar_cnpj($cnpj) {
    $cnpj = preg_replace('/\D/', '', $cnpj);
    $r = pat_brasilapi_cnpj($cnpj);
    if (!isset($r['erro'])) return $r;
    $r2 = pat_http_get("https://api.cnpja.com/office/{$cnpj}?strategy=CACHE_IF_FRESH");
    if ($r2 && isset($r2['company'])) {
        $socios = [];
        foreach (($r2['members'] ?? []) as $m) {
            $socios[] = [
                'nome'  => $m['person']['name'] ?? ($m['company']['name'] ?? ''),
                'qual'  => $m['role']['text'] ?? '',
                'cpf'   => $m['person']['taxId'] ?? '',
                'desde' => $m['since'] ?? '',
            ];
        }
        return [
            'razao_social' => $r2['company']['name'] ?? '',
            'situacao'     => $r2['status']['text'] ?? '',
            'abertura'     => $r2['founded'] ?? '',
            'capital'      => $r2['company']['equity'] ?? 0,
            'municipio'    => ($r2['address']['municipality'] ?? '') . '/' . ($r2['address']['state'] ?? ''),
            'email'        => $r2['emails'][0]['address'] ?? '',
            'porte'        => $r2['company']['size']['text'] ?? '',
            'socios'       => $socios,
            'filiais'      => array_map(fn($b) => ['nome'=>$b['alias']??'','municipio'=>($b['address']['municipality']??'')], $r2['branches'] ?? []),
            'total_filiais'=> count($r2['branches'] ?? []),
            'fonte'        => 'cnpja'
        ];
    }
    return pat_receitaws_cnpj($cnpj);
}

function pat_receitaws_cnpj($cnpj) {
    $cnpj = preg_replace('/\D/', '', $cnpj);
    $r = pat_http_get("https://www.receitaws.com.br/v1/cnpj/{$cnpj}");
    if (!$r || (isset($r['status']) && $r['status'] === 'ERROR'))
        return ['erro' => 'Nenhuma API retornou dados para este CNPJ'];
    return [
        'razao_social' => $r['nome'] ?? '',
        'situacao'     => $r['situacao'] ?? '',
        'abertura'     => $r['abertura'] ?? '',
        'capital'      => $r['capital_social'] ?? 0,
        'municipio'    => ($r['municipio'] ?? '') . '/' . ($r['uf'] ?? ''),
        'email'        => $r['email'] ?? '',
        'socios'       => array_map(function($s) {
            return ['nome' => $s['nome'] ?? '', 'qual' => $s['qual'] ?? '', 'cpf' => $s['cpf_rep_legal'] ?? ''];
        }, $r['qsa'] ?? []),
        'fonte' => 'receitaws'
    ];
}

// ═══════════════════════════════════════════════════════════════
// 9. DataJud CNJ — mantido + extração de movimentos recentes
// ═══════════════════════════════════════════════════════════════
// ─── Mapa completo de tribunais disponíveis no DataJud ───────
function pat_datajud_tribunais() {
    // Fonte: datajud-wiki.cnj.jus.br/api-publica/endpoints/
    // NOTA: STF NÃO está disponível no DataJud público.
    return [
        // ── Pernambuco (padrão) ────────────────────────────────
        'tjpe'  => ['nome' => 'TJPE',      'tier' => 1, 'tipo' => 'estadual'],
        'trt6'  => ['nome' => 'TRT 6ª',    'tier' => 1, 'tipo' => 'trabalhista'],
        // ── Nordeste — TJs ─────────────────────────────────────
        'tjpb'  => ['nome' => 'TJPB',      'tier' => 2, 'tipo' => 'estadual'],
        'tjal'  => ['nome' => 'TJAL',      'tier' => 2, 'tipo' => 'estadual'],
        'tjce'  => ['nome' => 'TJCE',      'tier' => 2, 'tipo' => 'estadual'],
        'tjrn'  => ['nome' => 'TJRN',      'tier' => 2, 'tipo' => 'estadual'],
        'tjma'  => ['nome' => 'TJMA',      'tier' => 2, 'tipo' => 'estadual'],
        'tjpi'  => ['nome' => 'TJPI',      'tier' => 2, 'tipo' => 'estadual'],
        'tjba'  => ['nome' => 'TJBA',      'tier' => 2, 'tipo' => 'estadual'],
        'tjse'  => ['nome' => 'TJSE',      'tier' => 2, 'tipo' => 'estadual'],
        // ── Nordeste — TRTs ────────────────────────────────────
        'trt7'  => ['nome' => 'TRT 7ª',    'tier' => 2, 'tipo' => 'trabalhista'], // CE
        'trt13' => ['nome' => 'TRT 13ª',   'tier' => 2, 'tipo' => 'trabalhista'], // PB
        'trt16' => ['nome' => 'TRT 16ª',   'tier' => 2, 'tipo' => 'trabalhista'], // MA
        'trt19' => ['nome' => 'TRT 19ª',   'tier' => 2, 'tipo' => 'trabalhista'], // AL
        'trt20' => ['nome' => 'TRT 20ª',   'tier' => 2, 'tipo' => 'trabalhista'], // SE
        'trt21' => ['nome' => 'TRT 21ª',   'tier' => 2, 'tipo' => 'trabalhista'], // RN
        'trt22' => ['nome' => 'TRT 22ª',   'tier' => 2, 'tipo' => 'trabalhista'], // PI
        // ── Federal ────────────────────────────────────────────
        'trf1'  => ['nome' => 'TRF 1ª',    'tier' => 2, 'tipo' => 'federal'],
        'trf2'  => ['nome' => 'TRF 2ª',    'tier' => 3, 'tipo' => 'federal'],
        'trf3'  => ['nome' => 'TRF 3ª',    'tier' => 3, 'tipo' => 'federal'],
        'trf4'  => ['nome' => 'TRF 4ª',    'tier' => 3, 'tipo' => 'federal'],
        'trf5'  => ['nome' => 'TRF 5ª',    'tier' => 2, 'tipo' => 'federal'],
        'trf6'  => ['nome' => 'TRF 6ª',    'tier' => 3, 'tipo' => 'federal'],
        // ── Superiores (STF NÃO disponível no DataJud) ─────────
        'stj'   => ['nome' => 'STJ',        'tier' => 2, 'tipo' => 'superior'],
        'tst'   => ['nome' => 'TST',        'tier' => 2, 'tipo' => 'superior'],
        // ── Sudeste — TJs ──────────────────────────────────────
        'tjsp'  => ['nome' => 'TJSP',       'tier' => 3, 'tipo' => 'estadual'],
        'tjrj'  => ['nome' => 'TJRJ',       'tier' => 3, 'tipo' => 'estadual'],
        'tjmg'  => ['nome' => 'TJMG',       'tier' => 3, 'tipo' => 'estadual'],
        'tjes'  => ['nome' => 'TJES',       'tier' => 3, 'tipo' => 'estadual'],
        // ── Sudeste — TRTs ─────────────────────────────────────
        'trt1'  => ['nome' => 'TRT 1ª',     'tier' => 3, 'tipo' => 'trabalhista'], // RJ
        'trt2'  => ['nome' => 'TRT 2ª',     'tier' => 3, 'tipo' => 'trabalhista'], // SP
        'trt3'  => ['nome' => 'TRT 3ª',     'tier' => 3, 'tipo' => 'trabalhista'], // MG
        'trt10' => ['nome' => 'TRT 10ª',    'tier' => 3, 'tipo' => 'trabalhista'], // DF/TO
        'trt15' => ['nome' => 'TRT 15ª',    'tier' => 3, 'tipo' => 'trabalhista'], // Campinas
        'trt17' => ['nome' => 'TRT 17ª',    'tier' => 3, 'tipo' => 'trabalhista'], // ES
        // ── Sul ────────────────────────────────────────────────
        'tjrs'  => ['nome' => 'TJRS',       'tier' => 3, 'tipo' => 'estadual'],
        'tjsc'  => ['nome' => 'TJSC',       'tier' => 3, 'tipo' => 'estadual'],
        'tjpr'  => ['nome' => 'TJPR',       'tier' => 3, 'tipo' => 'estadual'],
        'trt4'  => ['nome' => 'TRT 4ª',     'tier' => 3, 'tipo' => 'trabalhista'], // RS
        'trt9'  => ['nome' => 'TRT 9ª',     'tier' => 3, 'tipo' => 'trabalhista'], // PR
        'trt12' => ['nome' => 'TRT 12ª',    'tier' => 3, 'tipo' => 'trabalhista'], // SC
        // ── Centro-Oeste ───────────────────────────────────────
        'tjdft' => ['nome' => 'TJDFT',      'tier' => 3, 'tipo' => 'estadual'],
        'tjgo'  => ['nome' => 'TJGO',       'tier' => 3, 'tipo' => 'estadual'],
        'tjmt'  => ['nome' => 'TJMT',       'tier' => 3, 'tipo' => 'estadual'],
        'tjms'  => ['nome' => 'TJMS',       'tier' => 3, 'tipo' => 'estadual'],
        'trt18' => ['nome' => 'TRT 18ª',    'tier' => 3, 'tipo' => 'trabalhista'], // GO
        'trt23' => ['nome' => 'TRT 23ª',    'tier' => 3, 'tipo' => 'trabalhista'], // MT
        'trt24' => ['nome' => 'TRT 24ª',    'tier' => 3, 'tipo' => 'trabalhista'], // MS
        // ── Norte ──────────────────────────────────────────────
        'tjam'  => ['nome' => 'TJAM',       'tier' => 3, 'tipo' => 'estadual'],
        'tjpa'  => ['nome' => 'TJPA',       'tier' => 3, 'tipo' => 'estadual'],
        'tjro'  => ['nome' => 'TJRO',       'tier' => 3, 'tipo' => 'estadual'],
        'tjto'  => ['nome' => 'TJTO',       'tier' => 3, 'tipo' => 'estadual'],
        'tjac'  => ['nome' => 'TJAC',       'tier' => 3, 'tipo' => 'estadual'],
        'tjap'  => ['nome' => 'TJAP',       'tier' => 3, 'tipo' => 'estadual'],
        'tjrr'  => ['nome' => 'TJRR',       'tier' => 3, 'tipo' => 'estadual'],
        'trt8'  => ['nome' => 'TRT 8ª',     'tier' => 3, 'tipo' => 'trabalhista'], // PA/AP
        'trt11' => ['nome' => 'TRT 11ª',    'tier' => 3, 'tipo' => 'trabalhista'], // AM/RR
        'trt14' => ['nome' => 'TRT 14ª',    'tier' => 3, 'tipo' => 'trabalhista'], // RO/AC
    ];
}

/**
 * Consulta múltiplos tribunais em PARALELO via cURL multi-handle.
 * $tribunais_alias = array de aliases (ex: ['tjpe','trt6','stj'])
 *                    se null, consulta só Tier 1 (tjpe + trt6)
 */
function pat_consultar_datajud($documento, $tribunais_alias = null, $nome_parte = null) {
    $doc    = preg_replace('/\D/', '', $documento);
    $apikey = pat_config('datajud_apikey', 'cDZHYzlZa0JadVREZDJCendQbXY6SkJlTzNjLV9TRENyQk1RdnFKZGRQdw==');

    if ($tribunais_alias === null) {
        $trib_conf       = pat_config('datajud_tribunais_padrao', 'tjpe,trt6');
        $tribunais_alias = array_filter(array_map('trim', explode(',', $trib_conf)));
        if (empty($tribunais_alias)) $tribunais_alias = ['tjpe', 'trt6'];
    }

    $mapa = pat_datajud_tribunais();

    // Múltiplas estratégias de busca para máximo recall
    $should = [];

    // 1. Por documento
    if ($doc) {
        $should[] = ['match' => ['partes.documento' => $doc]];
        if (strlen($doc) === 14) {
            $fmt = preg_replace('/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/', '$1.$2.$3/$4-$5', $doc);
            $should[] = ['match' => ['partes.documento' => $fmt]];
        }
    }

    // 2. Por nome com variações (tolera pontuação diferente)
    if ($nome_parte) {
        $nome = trim($nome_parte);
        $variacoes = array_unique([
            $nome,
            rtrim($nome, '.'),
            str_replace(['S.A.','S.A','S/A','LTDA.','LTDA','ME.','ME','EPP.','EPP'],
                        ['SA','SA','SA','LTDA','LTDA','ME','ME','EPP','EPP'], $nome),
            preg_replace('/[\.\/]/', ' ', $nome),
            trim(preg_replace('/\s+/', ' ',
                preg_replace('/[^A-Z0-9\s]/u', ' ', mb_strtoupper($nome)))),
        ]);
        foreach ($variacoes as $v) {
            $v = trim(preg_replace('/\s+/', ' ', $v));
            if (strlen($v) < 3) continue;
            $should[] = ['match_phrase' => ['partes.nome' => $v]];
        }
        $should[] = ['match' => ['partes.nome' => [
            'query'    => $nome,
            'operator' => 'and',
        ]]];
        // Fuzzy na palavra mais longa (tolera 1 diferença: ponto, acento)
        $stopwords = ['SA','SAS','LTDA','ME','EPP','DE','DO','DA','DOS','DAS','E','S','A'];
        $principal = '';
        foreach (explode(' ', $nome) as $p) {
            $pu = preg_replace('/[^A-Z0-9]/u', '', mb_strtoupper($p));
            if (strlen($pu) > strlen($principal) && !in_array($pu, $stopwords))
                $principal = $pu;
        }
        if (strlen($principal) >= 5)
            $should[] = ['fuzzy' => ['partes.nome' => [
                'value'     => $principal,
                'fuzziness' => 1,
            ]]];
    }

    if (empty($should)) return [
        'processos' => [], 'total' => 0, 'como_credor' => 0,
        'tribunais_ok' => 0, 'tribunais_erro' => [],
    ];

    $body = json_encode([
        'size'    => 30,
        'query'   => ['bool' => ['should' => $should, 'minimum_should_match' => 1]],
        '_source' => ['numeroProcesso','tribunal','classe','assuntos',
                      'dataAjuizamento','valorCausa','partes','movimentos',
                      'grau','orgaoJulgador'],
    ]);

    $headers = [
        'Content-Type: application/json',
        'Authorization: ApiKey ' . $apikey,
    ];

    // ── cURL multi-handle: dispara todas as requisições em paralelo ──
    $multi  = curl_multi_init();
    $curls  = [];

    foreach ($tribunais_alias as $alias) {
        $url = "https://api-publica.datajud.cnj.jus.br/api_publica_{$alias}/_search";
        $ch  = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $body,
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_USERAGENT      => 'Breno Paiva Advocacia/4.0',
        ]);
        curl_multi_add_handle($multi, $ch);
        $curls[$alias] = $ch;
    }

    $running = null;
    do { curl_multi_exec($multi, $running); curl_multi_select($multi); }
    while ($running > 0);

    $processos   = [];
    $total_geral = 0;
    $erros       = [];

    foreach ($curls as $alias => $ch) {
        $resp = curl_multi_getcontent($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_multi_remove_handle($multi, $ch);
        curl_close($ch);

        if (!$resp || $code !== 200) {
            $erros[] = strtoupper($alias) . ' (HTTP ' . $code . ')';
            continue;
        }

        $r = json_decode($resp, true);
        if (empty($r['hits']['hits'])) continue;

        $total_geral += (int)($r['hits']['total']['value'] ?? 0);
        $nome_tribunal = $mapa[$alias]['nome'] ?? strtoupper($alias);
        $tipo_tribunal = $mapa[$alias]['tipo'] ?? 'outro';

        foreach ($r['hits']['hits'] as $hit) {
            $s    = $hit['_source'];
            $polo = '';
            if ($nome_parte) {
                $primeiro = mb_strtolower(explode(' ', $nome_parte)[0]);
                foreach (($s['partes'] ?? []) as $p) {
                    if (strpos(mb_strtolower($p['nome'] ?? ''), $primeiro) !== false) {
                        $polo = $p['polo'] ?? '';
                        break;
                    }
                }
            }
            $ultimo_mov = '';
            if (!empty($s['movimentos'])) {
                $mov = end($s['movimentos']);
                $ultimo_mov = ($mov['nome'] ?? '') . ' em ' . substr($mov['dataHora'] ?? '', 0, 10);
            }
            $processos[] = [
                'numero'           => $s['numeroProcesso']      ?? '',
                'tribunal'         => $nome_tribunal,
                'tribunal_alias'   => $alias,
                'tribunal_tipo'    => $tipo_tribunal,
                'classe'           => $s['classe']['nome']       ?? '',
                'assunto'          => $s['assuntos'][0]['nome']  ?? '',
                'data'             => substr($s['dataAjuizamento'] ?? '', 0, 10),
                'valor'            => $s['valorCausa']           ?? 0,
                'grau'             => $s['grau']                 ?? '',
                'orgao'            => $s['orgaoJulgador']['nome'] ?? '',
                'polo'             => $polo,
                'como_autor'       => ($polo === 'AT'),
                'ultimo_movimento' => $ultimo_mov,
            ];
        }
    }

    curl_multi_close($multi);
    usort($processos, fn($a, $b) => strcmp($b['data'], $a['data']));

    return [
        'processos'      => $processos,
        'total'          => $total_geral,
        'como_credor'    => count(array_filter($processos, fn($p) => $p['como_autor'])),
        'tribunais_ok'   => count($curls) - count($erros),
        'tribunais_erro' => $erros,
        'por_tipo'       => array_count_values(array_column($processos, 'tribunal_tipo')),
    ];
}

function pat_transparencia_contratos($cnpj_cpf) {
    $doc = preg_replace('/\D/', '', $cnpj_cpf);
    $url = "https://api.portaldatransparencia.gov.br/api-de-dados/contratos?cnpjContratado={$doc}&pagina=1";
    $r   = pat_http_get($url, ['chave-api-dados: ' . PAT_TOKEN_TRANSPARENCIA]);
    if (!$r) return ['contratos' => [], 'total' => 0];
    $contratos = [];
    foreach ((array)$r as $c) {
        $contratos[] = [
            'numero'   => $c['numero']        ?? '',
            'objeto'   => $c['objeto']        ?? '',
            'valor'    => $c['valorInicial']  ?? 0,
            'orgao'    => $c['unidadeGestora']['orgaoVinculado']['nome'] ?? '',
            'vigencia' => $c['dataFimVigencia'] ?? '',
        ];
    }
    return ['contratos' => $contratos, 'total' => count($contratos)];
}

function pat_transparencia_sancoes($cnpj_cpf) {
    $doc = preg_replace('/\D/', '', $cnpj_cpf);
    $r   = pat_http_get(
        "https://api.portaldatransparencia.gov.br/api-de-dados/ceis?codigoSancionado={$doc}&pagina=1",
        ['chave-api-dados: ' . PAT_TOKEN_TRANSPARENCIA]
    );
    if (!$r) return ['sancoes' => [], 'total' => 0];
    $sancoes = [];
    foreach ((array)$r as $s) {
        $sancoes[] = [
            'tipo'       => $s['tipoSancao']['descricaoResumida'] ?? '',
            'orgao'      => $s['orgaoSancionador']['nome']        ?? '',
            'dataInicio' => $s['dataInicioSancao']                ?? '',
            'dataFim'    => $s['dataFimSancao']                   ?? '',
        ];
    }
    return ['sancoes' => $sancoes, 'total' => count($sancoes)];
}

// Alias: pat_consultar_dominio → pat_registrobr_dominio
function pat_consultar_dominio($dominio) {
    return pat_registrobr_dominio($dominio);
}

// Links de consulta manual orientada
// (fontes sem API REST gratuita — gera link já com dados do devedor)
function pat_links_consulta_manual($nome, $documento) {
    $doc      = preg_replace('/\D/', '', $documento);
    $nome_enc = urlencode($nome);
    return [
        [
            'fonte'     => 'INPI — Marcas e Patentes',
            'motivo'    => 'Sem API REST gratuita. Consulta manual por titular.',
            'url'       => 'https://busca.inpi.gov.br/pePI/servlet/MarcasServletController',
            'instrucao' => 'Buscar por Titular → CPF/CNPJ: ' . $documento,
        ],
        [
            'fonte'     => 'TSE — Bens de Candidatos',
            'motivo'    => 'CPF removido da base pública desde 2024. Busca apenas por nome.',
            'url'       => "https://divulgacandcontas.tse.jus.br/divulga/#/candidato/buscar?nome={$nome_enc}",
            'instrucao' => 'Buscar candidaturas em nome de: ' . $nome,
        ],
        [
            'fonte'     => 'CVM — Cotistas de Fundos (FIP/FIDC)',
            'motivo'    => 'Apenas dataset CSV mensal, sem busca individual por CPF.',
            'url'       => 'https://dados.cvm.gov.br/dataset?groups=fundos-de-investimento',
            'instrucao' => 'Baixar dataset e cruzar manualmente com: ' . $documento,
        ],
        [
            'fonte'     => 'ONR — Certidão Nacional de Imóveis',
            'motivo'    => 'Busca paga por certidão, sem API gratuita.',
            'url'       => 'https://cer.oni.org.br/',
            'instrucao' => 'Solicitar certidão de imóveis em nome de: ' . $nome,
        ],
        [
            'fonte'     => 'CENSEC — Central Notarial',
            'motivo'    => 'Acesso mediante ordem judicial ou cadastro OAB verificado.',
            'url'       => 'https://www.censec.org.br/',
            'instrucao' => 'Consultar escrituras/procurações em nome de: ' . $nome,
        ],
    ];
}


function pat_consultar_fipe_marca($tipo = 'carros') {
    return pat_http_get("https://parallelum.com.br/fipe/api/v1/{$tipo}/marcas") ?: [];
}
function pat_consultar_fipe_modelos($tipo, $marca_id) {
    $r = pat_http_get("https://parallelum.com.br/fipe/api/v1/{$tipo}/marcas/{$marca_id}/modelos");
    return $r['modelos'] ?? [];
}
function pat_consultar_fipe_anos($tipo, $marca_id, $modelo_id) {
    return pat_http_get("https://parallelum.com.br/fipe/api/v1/{$tipo}/marcas/{$marca_id}/modelos/{$modelo_id}/anos") ?: [];
}
function pat_consultar_fipe_valor($tipo, $marca_id, $modelo_id, $ano_id) {
    $r = pat_http_get("https://parallelum.com.br/fipe/api/v1/{$tipo}/marcas/{$marca_id}/modelos/{$modelo_id}/anos/{$ano_id}");
    if (!$r) return null;
    return [
        'valor' => $r['Valor'] ?? '', 'marca' => $r['Marca'] ?? '', 'modelo' => $r['Modelo'] ?? '',
        'ano'   => $r['AnoModelo'] ?? '', 'comb' => $r['Combustivel'] ?? '', 'codigo' => $r['CodigoFipe'] ?? '',
        'valor_num' => (float) preg_replace('/[^\d,]/', '', str_replace('.', '', $r['Valor'] ?? '0')) / 100
    ];
}

// ═══════════════════════════════════════════════════════════════
// 12. BCB Selic/IPCA (mantido)
// ═══════════════════════════════════════════════════════════════
function pat_taxa_selic() {
    $r = pat_http_get('https://brasilapi.com.br/api/taxas/v1');
    if (!$r) return null;
    foreach ($r as $t) if (strtolower($t['nome'] ?? '') === 'selic') return $t['valor'];
    return null;
}
function pat_todas_taxas() {
    return pat_http_get('https://brasilapi.com.br/api/taxas/v1') ?: [];
}
function pat_ipca_mensal() {
    $r = pat_http_get('https://brasilapi.com.br/api/ipca/v1');
    return is_array($r) ? array_slice($r, 0, 12) : [];
}

// ═══════════════════════════════════════════════════════════════
// 13. ViaCEP (mantido)
// ═══════════════════════════════════════════════════════════════
function pat_consultar_cep($cep) {
    $cep = preg_replace('/\D/', '', $cep);
    if (strlen($cep) !== 8) return null;
    return pat_http_get("https://viacep.com.br/ws/{$cep}/json/");
}

// ═══════════════════════════════════════════════════════════════
// 14. Blockchain (mantido)
// ═══════════════════════════════════════════════════════════════
function pat_blockchain_btc($address) {
    if (empty($address)) return null;
    $r = pat_http_get("https://blockchain.info/rawaddr/{$address}?limit=5");
    if (!$r) return null;
    return [
        'saldo_btc' => ($r['final_balance'] ?? 0) / 100000000,
        'total_recebido' => ($r['total_received'] ?? 0) / 100000000,
        'transacoes' => $r['n_tx'] ?? 0,
    ];
}
function pat_blockchain_eth($address) {
    if (empty($address)) return null;
    $r = pat_http_get("https://api.etherscan.io/api?module=account&action=balance&address={$address}&tag=latest");
    if (!$r || $r['status'] !== '1') return null;
    return ['saldo_eth' => ($r['result'] ?? 0) / 1e18];
}

// ═══════════════════════════════════════════════════════════════
// 15. ICIJ Offshore Leaks — API de Reconciliação oficial
// (endpoint antigo /search?json=true não é mais válido; o ICIJ
// publicou uma API REST de Reconciliação documentada em
// https://offshoreleaks.icij.org/docs/reconciliation)
// ═══════════════════════════════════════════════════════════════
function pat_offshore_leaks($nome) {
    if (empty($nome)) return ['total' => 0, 'entidades' => []];

    $body = json_encode([
        'query' => $nome,
        'type'  => 'Entity', // also: Officer, Address, Intermediary
    ]);

    $r = pat_http_post('https://offshoreleaks.icij.org/api/v1/reconcile', $body, [
        'Content-Type: application/json'
    ]);

    if (!$r || empty($r['result'])) return ['total' => 0, 'entidades' => []];

    $entidades = [];
    foreach (array_slice($r['result'], 0, 10) as $item) {
        $entidades[] = [
            'nome'    => $item['name']  ?? '',
            'tipo'    => $item['type']['name'] ?? ($item['type'] ?? ''),
            'pais'    => implode(', ', array_column($item['type']['features'] ?? [], 'value')) ?: '',
            'score'   => $item['score'] ?? null,
            'match'   => $item['match'] ?? false,
        ];
    }
    return ['total' => count($r['result']), 'entidades' => $entidades];
}

// ═══════════════════════════════════════════════════════════════
// MOTOR DE CORRELAÇÃO v3 — mais alertas, mais fontes
// ═══════════════════════════════════════════════════════════════
function pat_motor_correlacao($dados) {
    $alertas = [];
    $cnpj     = $dados['cnpj']          ?? null;
    $datajud  = $dados['datajud']        ?? null;
    $transp   = $dados['transparencia']  ?? null;
    $offshore = $dados['offshore']       ?? null;
    $registrobr = $dados['registrobr']   ?? null;
    $cvm_fundo  = $dados['cvm_fundo']    ?? null;

    if ($cnpj && !isset($cnpj['erro'])) {
        $sit = strtolower($cnpj['situacao'] ?? '');

        // 1. Empresa irregular + contratos públicos ativos
        if ((strpos($sit,'irregular')!==false || strpos($sit,'baixada')!==false || strpos($sit,'suspensa')!==false || strpos($sit,'inapta')!==false)
            && $transp && ($transp['total'] ?? 0) > 0) {
            $alertas[] = ['tipo'=>'faturamento_oculto','relevancia'=>'alta',
                'descricao'=>"INCONSISTÊNCIA: Empresa com situação \"{$cnpj['situacao']}\" na Receita Federal, mas possui {$transp['total']} contrato(s) ativo(s) com o poder público.",
                'fontes'=>'BrasilAPI CNPJ + Portal da Transparência',
                'tese'=>'Indício de faturamento oculto. Requer penhora no rosto dos créditos junto ao órgão público contratante (Art. 860 CPC).'];
        }

        // 2. NOVO — Não optante do Simples mas alega insolvência
        if (strpos($cnpj['opcao_simples'] ?? '', 'NÃO optante') !== false) {
            $alertas[] = ['tipo'=>'inconsistencia_renda','relevancia'=>'media',
                'descricao'=>"Empresa tributada por Lucro Real ou Presumido (não optante do Simples Nacional) — regime tipicamente usado por empresas de maior faturamento.",
                'fontes'=>'BrasilAPI CNPJ',
                'tese'=>'Regime tributário incompatível com alegação de pequeno porte/insolvência. Fundamenta aprofundamento da investigação patrimonial.'];
        }

        // 3. NOVO — Múltiplas filiais ativas
        if (($cnpj['total_filiais'] ?? 0) >= 2) {
            $alertas[] = ['tipo'=>'patrimonio_rural','relevancia'=>'media',
                'descricao'=>"Empresa possui {$cnpj['total_filiais']} filial(is) registrada(s) em endereços distintos — possíveis locais de estoque/patrimônio não declarado.",
                'fontes'=>'BrasilAPI CNPJ',
                'tese'=>'Requerer diligência de Oficial de Justiça em todos os endereços de filiais para penhora de bens móveis e mercadorias (Art. 836 CPC).'];
        }

        // 4. Capital irrisório vs débito
        if (isset($dados['valor_execucao'])) {
            $capital = (float)($cnpj['capital'] ?? 0);
            $debito  = (float)$dados['valor_execucao'];
            if ($capital > 0 && $capital < $debito * 0.5) {
                $alertas[] = ['tipo'=>'inconsistencia_renda','relevancia'=>'media',
                    'descricao'=>"Capital social (R$ ".number_format($capital,2,',','.').") é inferior a 50% do débito exequendo (R$ ".number_format($debito,2,',','.').").",
                    'fontes'=>'BrasilAPI CNPJ',
                    'tese'=>'Indício de subcapitalização dolosa. Fundamento para IDPJ por confusão patrimonial ou desvio de finalidade (Art. 50 CC).'];
            }
        }

        // 5. Múltiplos sócios
        if (count($cnpj['socios'] ?? []) > 1) {
            $alertas[] = ['tipo'=>'grupo_economico','relevancia'=>'media',
                'descricao'=>count($cnpj['socios'])." sócios identificados no QSA. Verificar se possuem outras empresas registradas.",
                'fontes'=>'BrasilAPI CNPJ',
                'tese'=>'Investigar sócios individualmente para IDPJ (Art. 50 CC / Art. 855-A CLT) ou reconhecimento de grupo econômico.'];
        }
    }

    // 6. DataJud — devedor é credor em outro processo
    if ($datajud && ($datajud['como_credor'] ?? 0) > 0) {
        $alertas[] = ['tipo'=>'ocultacao_bem','relevancia'=>'alta',
            'descricao'=>"DataJud identificou {$datajud['como_credor']} processo(s) onde o executado figura como AUTOR/REQUERENTE. Há crédito ativo a ser interceptado.",
            'fontes'=>'DataJud CNJ',
            'tese'=>'Penhora no rosto dos autos nos processos identificados (Art. 860 CPC).'];
    }

    // 7. Offshore
    if ($offshore && ($offshore['total'] ?? 0) > 0) {
        $alertas[] = ['tipo'=>'offshore_detectada','relevancia'=>'alta',
            'descricao'=>"Base ICIJ (Panama/Pandora Papers) identificou {$offshore['total']} vinculação(ões) com estruturas offshore.",
            'fontes'=>'ICIJ Offshore Leaks',
            'tese'=>'Indício de evasão de divisas. Base para repatriação de bens, carta rogatória e IDPJ Inversa.'];
    }

    // 8. NOVO — RegistroBR: domínio em nome de terceiro
    if ($registrobr && !isset($registrobr['erro']) && !empty($dados['devedor_nome'])) {
        $titular = strtolower($registrobr['titular'] ?? '');
        $devedor = strtolower($dados['devedor_nome'] ?? '');
        if ($titular && strpos($titular, explode(' ', $devedor)[0]) === false) {
            $alertas[] = ['tipo'=>'laranja_identificado','relevancia'=>'media',
                'descricao'=>"Domínio investigado está registrado em nome de \"{$registrobr['titular']}\", distinto do executado. Possível interposição de pessoa (laranja).",
                'fontes'=>'BrasilAPI RegistroBR (WHOIS)',
                'tese'=>'Indício de uso de testa-de-ferro para administração do negócio. Investigar relação entre titular do domínio e o executado.'];
        }
    }

    // 9. NOVO — Fundos CVM encontrados
    if ($cvm_fundo && ($cvm_fundo['encontrado'] ?? false)) {
        $alertas[] = ['tipo'=>'ativo_intangivel','relevancia'=>'alta',
            'descricao'=>"Identificada(s) ".count($cvm_fundo['fundos'])." cota(s) de fundo(s) de investimento registrado(s) na CVM em nome do executado/empresa.",
            'fontes'=>'CVM Dados Abertos',
            'tese'=>'Cotas de fundos de investimento são penhoráveis (Art. 835, IX CPC). Requerer ofício ao administrador do fundo para bloqueio.'];
    }

    usort($alertas, function($a, $b) {
        $ord = ['alta'=>0,'media'=>1,'baixa'=>2];
        return ($ord[$a['relevancia']] ?? 2) - ($ord[$b['relevancia']] ?? 2);
    });
    return $alertas;
}

// ═══════════════════════════════════════════════════════════════
// GERADOR DE PETIÇÃO COM TEMPLATE (mantido)
// ═══════════════════════════════════════════════════════════════
function pat_gerar_peticao_template($template, $variaveis) {
    $texto = $template;
    foreach ($variaveis as $chave => $valor) $texto = str_replace('{{' . $chave . '}}', $valor, $texto);
    return $texto;
}

function pat_gerar_peticao($expediente, $sistemas, $advogado, $ultima_diligencia = null) {
    $sistemas_nomes = [
        'sisbajud'=>'SISBAJUD – Sistema de Busca de Ativos do Poder Judiciário',
        'renajud'=>'RENAJUD – Sistema de Restrição Judicial de Veículos Automotores',
        'infojud'=>'INFOJUD – Sistema de Informações ao Judiciário',
        'sniper'=>'SNIPER – Sistema Nacional de Investigação Patrimonial e Recuperação de Ativos',
        'cnib'=>'CNIB – Central Nacional de Indisponibilidade de Bens',
        'censec'=>'CENSEC – Central Notarial de Serviços Eletrônicos Compartilhados',
        'criptojud'=>'CRIPTOJUD – Sistema de Bloqueio de Criptoativos',
    ];
    $lista_sistemas = ''; $i = 1;
    foreach ($sistemas as $s) { $lista_sistemas .= "{$i}. **".($sistemas_nomes[$s] ?? strtoupper($s))."**\n"; $i++; }
    $data_br = date('d').' de '.pat_mes_extenso(date('m')).' de '.date('Y');
    $ultima  = $ultima_diligencia ? date('d/m/Y', strtotime($ultima_diligencia->data_requerimento)) : null;
    $renovacao = $ultima
        ? "Em que pese terem sido realizadas buscas patrimoniais em **{$ultima}**, transcorrido o prazo razoável de 12 (doze) meses (REsp 1.786.608/SP), requer-se a **renovação** das diligências."
        : "Até o momento não foram realizadas diligências eletrônicas nos autos, o que ora se requer.";
    $valor_fmt = 'R$ ' . number_format($expediente->valor_execucao, 2, ',', '.');
    $adv_oab = property_exists($advogado, 'oab') ? $advogado->oab : 'XXXXX';

    $texto  = "EXCELENTÍSSIMO SENHOR DOUTOR JUIZ DE DIREITO DA __ VARA CÍVEL DA COMARCA DE RECIFE/PE\n\n";
    $texto .= "Autos nº {$expediente->case_no}\n\n";
    $texto .= "**{$advogado->name}**, advogado(a) inscrito(a) na OAB/PE sob o nº {$adv_oab}, nos autos do processo em epígrafe, vem, respeitosamente, requerer:\n\n";
    $texto .= "**DO REQUERIMENTO DE DILIGÊNCIAS PATRIMONIAIS ELETRÔNICAS**\n\n";
    $texto .= "Trata-se de {$expediente->case_title}, movida em face de **".strtoupper($expediente->devedor_nome)."**, ";
    $texto .= ($expediente->devedor_tipo === 'pj' ? 'CNPJ' : 'CPF')." nº {$expediente->devedor_doc}, cujo débito totaliza **{$valor_fmt}**.\n\n";
    $texto .= "{$renovacao}\n\n";
    $texto .= "Ante o exposto, requer o deferimento das seguintes diligências eletrônicas:\n\n{$lista_sistemas}\n";
    $texto .= "Requer ainda que, uma vez localizados bens ou ativos financeiros, seja determinado o bloqueio/arresto/penhora nos termos do art. 854 do CPC.\n\n";
    $texto .= "Nestes termos, pede deferimento.\n\nRecife/PE, {$data_br}.\n\n**{$advogado->name}**\nOAB/PE nº {$adv_oab}";
    return $texto;
}

// ═══════════════════════════════════════════════════════════════
// HTTP HELPERS
// ═══════════════════════════════════════════════════════════════
function pat_http_get($url, $headers = []) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_USERAGENT => 'Breno Paiva Advocacia/3.0',
        CURLOPT_HTTPHEADER => array_merge(['Accept: application/json'], $headers),
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    $body = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
    if ($code === 200 && $body) return json_decode($body, true);
    return null;
}
function pat_http_post($url, $body, $headers = []) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body,
        CURLOPT_USERAGENT => 'Breno Paiva Advocacia/3.0',
        CURLOPT_HTTPHEADER => array_merge(['Accept: application/json'], $headers),
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    $resp = curl_exec($ch); curl_close($ch);
    return $resp ? json_decode($resp, true) : null;
}

// ═══════════════════════════════════════════════════════════════
// UTILITÁRIOS
// ═══════════════════════════════════════════════════════════════
function pat_mes_extenso($m) {
    $meses = ['01'=>'janeiro','02'=>'fevereiro','03'=>'março','04'=>'abril','05'=>'maio','06'=>'junho','07'=>'julho','08'=>'agosto','09'=>'setembro','10'=>'outubro','11'=>'novembro','12'=>'dezembro'];
    return $meses[$m] ?? '';
}
function pat_formatar_doc($doc) {
    $d = preg_replace('/\D/', '', $doc);
    if (strlen($d) === 11) return preg_replace('/(\d{3})(\d{3})(\d{3})(\d{2})/', '$1.$2.$3-$4', $d);
    if (strlen($d) === 14) return preg_replace('/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/', '$1.$2.$3/$4-$5', $d);
    return $doc;
}
function pat_tipo_doc($doc) {
    $d = preg_replace('/\D/', '', $doc);
    if (strlen($d) === 11) return 'pf';
    if (strlen($d) === 14) return 'pj';
    return null;
}

// ═══════════════════════════════════════════════════════════════
// DIAGNÓSTICO DE SAÚDE DAS APIs — teste real com validação de conteúdo
// ═══════════════════════════════════════════════════════════════
function pat_diagnostico_completo() {
    $resultados = [];
    $cnpj_teste = '00000000000191'; // Banco do Brasil — CNPJ estável para teste
    $cep_teste  = '70150900';       // Praça dos Três Poderes, Brasília — CEP estável

    // ── 1. BrasilAPI CNPJ ──────────────────────────────────────
    $resultados[] = pat_testar_api(
        'BrasilAPI CNPJ',
        'cnpj',
        function() use ($cnpj_teste) {
            $r = pat_http_get("https://brasilapi.com.br/api/cnpj/v1/{$cnpj_teste}");
            if (!$r) return ['ok' => false, 'motivo' => 'Sem resposta do servidor'];
            if (empty($r['razao_social'])) return ['ok' => false, 'motivo' => 'Resposta sem razão social — formato inesperado'];
            return ['ok' => true, 'amostra' => $r['razao_social']];
        }
    );

    // ── 2. OpenCNPJ ────────────────────────────────────────────
    $resultados[] = pat_testar_api(
        'OpenCNPJ',
        'cnpj',
        function() use ($cnpj_teste) {
            $r = pat_http_get("https://api.opencnpj.org/{$cnpj_teste}");
            if (!$r || isset($r['message'])) return ['ok' => false, 'motivo' => 'Sem resultado ou CNPJ não encontrado'];
            if (empty($r['razao_social'])) return ['ok' => false, 'motivo' => 'Resposta sem razão social — formato inesperado'];
            return ['ok' => true, 'amostra' => $r['razao_social']];
        }
    );

    // ── 3. CNPJá ───────────────────────────────────────────────
    $resultados[] = pat_testar_api(
        'CNPJá',
        'cnpj',
        function() use ($cnpj_teste) {
            $r = pat_http_get("https://api.cnpja.com/office/{$cnpj_teste}?strategy=CACHE_IF_FRESH");
            if (!$r) return ['ok' => false, 'motivo' => 'Sem resposta (pode ser limite de 5 req/min atingido)'];
            if (empty($r['company']['name'])) return ['ok' => false, 'motivo' => 'Resposta sem nome da empresa'];
            return ['ok' => true, 'amostra' => $r['company']['name']];
        }
    );

    // ── 4. ReceitaWS (fallback) ────────────────────────────────
    $resultados[] = pat_testar_api(
        'ReceitaWS',
        'cnpj',
        function() use ($cnpj_teste) {
            $r = pat_http_get("https://www.receitaws.com.br/v1/cnpj/{$cnpj_teste}");
            if (!$r || (isset($r['status']) && $r['status'] === 'ERROR')) return ['ok' => false, 'motivo' => 'Sem resposta ou erro retornado'];
            if (empty($r['nome'])) return ['ok' => false, 'motivo' => 'Resposta sem nome'];
            return ['ok' => true, 'amostra' => $r['nome']];
        }
    );

    // ── 5. DataJud CNJ ─────────────────────────────────────────
    $resultados[] = pat_testar_api(
        'DataJud CNJ',
        'judicial',
        function() {
            $body = json_encode(['size' => 1, 'query' => ['match_all' => new stdClass()]]);
            $r = pat_http_post(
                "https://api-publica.datajud.cnj.jus.br/api_publica_tjpe/_search",
                $body,
                ['Content-Type: application/json', 'Authorization: ApiKey ' . PAT_DATAJUD_APIKEY]
            );
            if (!$r) return ['ok' => false, 'motivo' => 'Sem resposta — chave de API pode ter expirado'];
            if (!isset($r['hits'])) return ['ok' => false, 'motivo' => 'Resposta sem estrutura "hits" esperada — possível mudança na API ou chave inválida'];
            return ['ok' => true, 'amostra' => ($r['hits']['total']['value'] ?? 0) . ' processos indexados no TJPE'];
        }
    );

    // ── 6. ViaCEP ──────────────────────────────────────────────
    $resultados[] = pat_testar_api(
        'ViaCEP',
        'apoio',
        function() use ($cep_teste) {
            $r = pat_http_get("https://viacep.com.br/ws/{$cep_teste}/json/");
            if (!$r || isset($r['erro'])) return ['ok' => false, 'motivo' => 'CEP não encontrado ou sem resposta'];
            if (empty($r['logradouro'])) return ['ok' => false, 'motivo' => 'Resposta sem logradouro'];
            return ['ok' => true, 'amostra' => $r['logradouro']];
        }
    );

    // ── 7. FIPE ────────────────────────────────────────────────
    $resultados[] = pat_testar_api(
        'FIPE (Parallelum)',
        'apoio',
        function() {
            $r = pat_http_get("https://parallelum.com.br/fipe/api/v1/carros/marcas");
            if (!$r || !is_array($r) || count($r) === 0) return ['ok' => false, 'motivo' => 'Sem lista de marcas retornada'];
            return ['ok' => true, 'amostra' => count($r) . ' marcas de veículos disponíveis'];
        }
    );

    // ── 8. BCB Selic/IPCA ──────────────────────────────────────
    $resultados[] = pat_testar_api(
        'Banco Central (Selic/IPCA)',
        'apoio',
        function() {
            $r = pat_http_get('https://brasilapi.com.br/api/taxas/v1');
            if (!$r || !is_array($r)) return ['ok' => false, 'motivo' => 'Sem lista de taxas retornada'];
            $selic = null;
            foreach ($r as $t) if (strtolower($t['nome'] ?? '') === 'selic') $selic = $t['valor'];
            if ($selic === null) return ['ok' => false, 'motivo' => 'Taxa Selic não encontrada na resposta'];
            return ['ok' => true, 'amostra' => "Selic atual: {$selic}% a.a."];
        }
    );

    // ── 9. Portal da Transparência ─────────────────────────────
    $resultados[] = pat_testar_api(
        'Portal da Transparência',
        'token',
        function() use ($cnpj_teste) {
            $r = pat_http_get("https://api.portaldatransparencia.gov.br/api-de-dados/contratos?cnpjContratado={$cnpj_teste}&pagina=1", ['chave-api-dados: ' . PAT_TOKEN_TRANSPARENCIA]);
            if ($r === null) return ['ok' => false, 'motivo' => 'Sem resposta — token "demo" pode ter expirado. Cadastre um token gratuito em portaldatransparencia.gov.br/api-de-dados'];
            return ['ok' => true, 'amostra' => 'API respondeu (' . (is_array($r) ? count($r) : 0) . ' resultado(s) para o CNPJ teste)'];
        }
    );

    // ── 10. ICIJ Offshore Leaks ────────────────────────────────
    $resultados[] = pat_testar_api(
        'ICIJ Offshore Leaks',
        'apoio',
        function() {
            $body = json_encode(['query' => 'Mossack Fonseca', 'type' => 'Entity']);
            $r = pat_http_post('https://offshoreleaks.icij.org/api/v1/reconcile', $body, ['Content-Type: application/json']);
            if ($r === null) return ['ok' => false, 'motivo' => 'Sem resposta do servidor ICIJ — API de reconciliação pode estar fora do ar'];
            if (!isset($r['result'])) return ['ok' => false, 'motivo' => 'Resposta sem campo "result" — formato da API pode ter mudado'];
            return ['ok' => true, 'amostra' => 'API de reconciliação respondeu corretamente (' . count($r['result']) . ' resultado(s) de teste)'];
        }
    );

    // ── 11. Registro.br (domínio) ──────────────────────────────
    $resultados[] = pat_testar_api(
        'Registro.br (domínio)',
        'apoio',
        function() {
            $r = pat_http_get("https://brasilapi.com.br/api/registrobr/v1/google.com.br");
            if (!$r) return ['ok' => false, 'motivo' => 'Sem resposta do servidor'];
            if (empty($r['status']) && empty($r['fqdn'])) return ['ok' => false, 'motivo' => 'Resposta sem status/fqdn — formato inesperado'];
            return ['ok' => true, 'amostra' => 'Consulta de domínio funcional'];
        }
    );

    // ── 12. Blockchain BTC ─────────────────────────────────────
    $resultados[] = pat_testar_api(
        'Blockchain.info (BTC)',
        'apoio',
        function() {
            // Endereço público conhecido (carteira genesis de teste)
            $r = pat_http_get("https://blockchain.info/rawaddr/1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?limit=1");
            if (!$r) return ['ok' => false, 'motivo' => 'Sem resposta — endpoint pode estar fora do ar ou bloqueado pelo host'];
            if (!isset($r['final_balance'])) return ['ok' => false, 'motivo' => 'Resposta sem saldo — formato inesperado'];
            return ['ok' => true, 'amostra' => 'Consulta de saldo BTC funcional'];
        }
    );

    return $resultados;
}

/**
 * Wrapper que executa o teste, mede o tempo e captura exceções,
 * para que uma API com erro não derrube o diagnóstico inteiro.
 */
function pat_testar_api($nome, $categoria, $callback) {
    $inicio = microtime(true);
    try {
        $resultado = $callback();
    } catch (\Throwable $e) {
        $resultado = ['ok' => false, 'motivo' => 'Exceção: ' . $e->getMessage()];
    }
    $tempo_ms = round((microtime(true) - $inicio) * 1000);

    return [
        'nome'      => $nome,
        'categoria' => $categoria,
        'ok'        => $resultado['ok'] ?? false,
        'motivo'    => $resultado['motivo'] ?? null,
        'amostra'   => $resultado['amostra'] ?? null,
        'tempo_ms'  => $tempo_ms,
        'testado_em'=> date('Y-m-d H:i:s'),
    ];
}
