<?php
date_default_timezone_set('America/Sao_Paulo');

define('DIR_RETORNOS_NUCLEA', realpath(__DIR__ . '/../pagamentos/anexos/nuclea/retornos-nuclea') ?: (__DIR__ . '/../pagamentos/anexos/nuclea/retornos-nuclea'));

function h($value)
{
    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

function dinheiro($value)
{
    return number_format((float) $value, 2, ',', '.');
}

function normalizarValor($value)
{
    $value = trim((string) $value);
    return $value === '' ? 0.0 : (float) str_replace(',', '.', $value);
}

function nomeArquivoSeguro($value, $fallback)
{
    $value = preg_replace('/[^A-Za-z0-9_.-]+/', '_', (string) $value);
    $value = trim($value, '._-');
    return $value !== '' ? $value : $fallback;
}

function textoNo($xpath, $contexto, $nome)
{
    $nodes = $xpath->query('./*[local-name()="' . $nome . '"]', $contexto);
    if ($nodes === false || $nodes->length === 0) {
        return '';
    }
    return trim($nodes->item(0)->textContent);
}

function textoNoVariantes($xpath, $contexto, $nomes)
{
    foreach ($nomes as $nome) {
        $valor = textoNo($xpath, $contexto, $nome);
        if ($valor !== '') {
            return $valor;
        }
    }
    return '';
}

function primeiroTexto($xpath, $nome)
{
    $nodes = $xpath->query('//*[local-name()="' . $nome . '"]');
    if ($nodes === false || $nodes->length === 0) {
        return '';
    }
    return trim($nodes->item(0)->textContent);
}

function primeiroTextoAncestral($xpath, $contexto, $nome)
{
    $nodes = $xpath->query('ancestor-or-self::*/*[local-name()="' . $nome . '"]', $contexto);
    if ($nodes === false || $nodes->length === 0) {
        return '';
    }
    return trim($nodes->item($nodes->length - 1)->textContent);
}

function carregarXmlTexto($raw)
{
    if ($raw === false || trim(str_replace("\0", '', (string) $raw)) === '') {
        throw new Exception('Nao foi possivel ler o arquivo XML.');
    }

    libxml_use_internal_errors(true);
    $tentativas = [
        $raw,
        mb_convert_encoding($raw, 'UTF-8', 'UTF-16BE'),
        mb_convert_encoding($raw, 'UTF-8', 'UTF-16LE'),
    ];

    foreach ($tentativas as $xmlTexto) {
        $dom = new DOMDocument('1.0', 'UTF-8');
        $dom->preserveWhiteSpace = false;
        if (@$dom->loadXML($xmlTexto, LIBXML_NONET)) {
            return $dom;
        }
    }

    $erros = [];
    foreach (libxml_get_errors() as $erro) {
        $erros[] = trim($erro->message) . ' na linha ' . $erro->line;
    }
    libxml_clear_errors();
    throw new Exception('XML invalido. ' . implode('; ', $erros));
}

function carregarXmlArquivo($path)
{
    return carregarXmlTexto(file_get_contents($path));
}

function tipoArquivoNuclea($nomeArquivo, $nomArq)
{
    $base = strtoupper($nomeArquivo . ' ' . $nomArq);
    if (strpos($base, 'ASLC030') !== false || strpos($base, 'ASLC034') !== false) {
        return 'Liquidado';
    }
    if (strpos($base, 'RET') !== false || strpos($base, 'PRO') !== false) {
        return 'Processado';
    }
    if (strpos($base, 'ASLC029') !== false || strpos($base, 'ASLC031') !== false) {
        return 'Aguardando';
    }
    return 'XML';
}

function normalizarNomArqBase($nomArq)
{
    $nomArq = strtoupper(trim((string) $nomArq));
    $nomArq = preg_replace('/^RET/', '', $nomArq);
    $nomArq = preg_replace('/_(RET|PRO)$/', '', $nomArq);
    return $nomArq;
}

function resultadoVazio()
{
    return [
        'meta' => [
            'nom_arq' => '',
            'num_ctrl_emis' => '',
            'dt_ref' => '',
            'dt_hr_arq' => '',
            'ispb_emissor' => '',
            'ispb_destinatario' => '',
            'cnpj_creddr' => '',
            'ispb_if_devedor' => '',
            'ispb_if_credor' => '',
        ],
        'resumo' => [
            'qtd_cnpj' => 0,
            'qtd_centralizadoras' => 0,
            'qtd_pontos' => 0,
            'total_geral' => 0.0,
            'datas_pagamento' => [],
            'qtd_controles' => 0,
            'qtd_controles_duplicados' => 0,
            'qtd_cod_erro' => 0,
            'qtd_nuliquid' => 0,
            'qtd_liquid_ok' => 0,
            'qtd_liquid_erro' => 0,
            'qtd_sem_cod_ocorc' => 0,
            'qtd_liquid_sem_retorno' => 0,
            'qtd_retorno_ignorado' => 0,
            'qtd_divergencia_valor' => 0,
            'total_liquid_ok' => 0.0,
            'total_liquid_erro' => 0.0,
            'total_liquid_pendente' => 0.0,
        ],
        'por_cnpj' => [],
        'por_arranjo' => [],
        'duplicados' => [],
        'controles' => [],
        'envios' => [],
        'liquidacoes' => [],
        'status_liquidacoes' => [],
        'arquivos' => [],
        'ignorados' => [],
        'divergencias_valor' => [],
    ];
}

function analisarXml($dom, $nomeArquivo = '')
{
    $xpath = new DOMXPath($dom);
    $nomArq = primeiroTexto($xpath, 'NomArq');
    $pontos = $xpath->query('//*[local-name()="Grupo_ASLC029_PontoVenda" or local-name()="Grupo_ASLC029RET_PontoVendaActo" or local-name()="Grupo_ASLC029RET_PontoVendaRecsdo" or local-name()="Grupo_ASLC031_PontoVenda" or local-name()="Grupo_ASLC031RET_PontoVendaActo" or local-name()="Grupo_ASLC031RET_PontoVendaRecsdo"]');
    $centralizadoras = $xpath->query('//*[local-name()="Grupo_ASLC029_Centrlz" or local-name()="Grupo_ASLC029RET_CentrlzActo" or local-name()="Grupo_ASLC029RET_CentrlzRecsdo" or local-name()="Grupo_ASLC031_Centrlz" or local-name()="Grupo_ASLC031RET_CentrlzActo" or local-name()="Grupo_ASLC031RET_CentrlzRecsdo"]');
    $attrsErro = $xpath->query('//@CodErro');
    $gruposLiquidacao = $xpath->query('//*[./*[local-name()="NULiquid"]]');

    $porCnpj = [];
    $porArranjo = [];
    $controles = [];
    $envios = [];
    $liquidacoes = [];
    $datasPagamento = [];
    $totalGeral = 0.0;

    foreach ($pontos as $ponto) {
        $cnpj = textoNoVariantes($xpath, $ponto, ['CNPJ_CPFPontoVenda', 'CNPJ_CPFPontoVendaActo', 'CNPJ_CPFPontoVendaRecsdo']);
        $nome = textoNoVariantes($xpath, $ponto, ['NomePontoVenda', 'NomePontoVendaActo', 'NomePontoVendaRecsdo']);
        $arranjo = textoNo($xpath, $ponto, 'CodInstitdrArrajPgto');
        $dataPagamento = textoNo($xpath, $ponto, 'DtPgto');
        $valorTexto = textoNo($xpath, $ponto, 'VlrPgto');
        $valor = normalizarValor($valorTexto);
        $temValor = $valorTexto !== '';
        $controle = textoNoVariantes($xpath, $ponto, ['NumCtrlCreddrPontoVenda', 'NumCtrlCreddrPontoVendaActo', 'NumCtrlCreddrPontoVendaRecsdo']);
        $ctrlCip = textoNoVariantes($xpath, $ponto, ['NumCtrlCIPPontoVenda', 'NumCtrlCIPPontoVendaActo', 'NumCtrlCIPPontoVendaRecsdo']);
        $nuLiquid = textoNo($xpath, $ponto, 'NULiquid');
        $codPontoVenda = textoNoVariantes($xpath, $ponto, ['CodPontoVenda', 'CodPontoVendaActo', 'CodPontoVendaRecsdo']);
        $ispb = textoNoVariantes($xpath, $ponto, ['ISPBIFLiquidPontoVenda', 'ISPBIFLiquidPontoVendaActo', 'ISPBIFLiquidPontoVendaRecsdo']);
        $tipoValor = textoNo($xpath, $ponto, 'TpVlrPgto');

        $cnpj = $cnpj === '' ? 'SEM_CNPJ' : $cnpj;
        $nome = $nome === '' ? '-' : $nome;
        $arranjo = $arranjo === '' ? '-' : $arranjo;

        if ($temValor) {
            if (!isset($porCnpj[$cnpj])) {
                $porCnpj[$cnpj] = [
                    'cnpj' => $cnpj,
                    'nome' => $nome,
                    'qtd' => 0,
                    'total' => 0.0,
                    'arranjos' => [],
                    'cod_pontos' => [],
                    'ispbs' => [],
                    'datas' => [],
                ];
            }
            $porCnpj[$cnpj]['qtd']++;
            $porCnpj[$cnpj]['total'] += $valor;
            $porCnpj[$cnpj]['arranjos'][$arranjo] = true;
            if ($codPontoVenda !== '') {
                $porCnpj[$cnpj]['cod_pontos'][$codPontoVenda] = true;
            }
            if ($ispb !== '') {
                $porCnpj[$cnpj]['ispbs'][$ispb] = true;
            }
            if ($dataPagamento !== '') {
                $porCnpj[$cnpj]['datas'][$dataPagamento] = true;
                $datasPagamento[$dataPagamento] = true;
            }

            $chaveArranjo = $cnpj . '|' . $arranjo;
            if (!isset($porArranjo[$chaveArranjo])) {
                $porArranjo[$chaveArranjo] = [
                    'cnpj' => $cnpj,
                    'nome' => $nome,
                    'arranjo' => $arranjo,
                    'qtd' => 0,
                    'total' => 0.0,
                    'datas' => [],
                    'tipo_valor' => [],
                ];
            }
            $porArranjo[$chaveArranjo]['qtd']++;
            $porArranjo[$chaveArranjo]['total'] += $valor;
            if ($dataPagamento !== '') {
                $porArranjo[$chaveArranjo]['datas'][$dataPagamento] = true;
            }
            if ($tipoValor !== '') {
                $porArranjo[$chaveArranjo]['tipo_valor'][$tipoValor] = true;
            }
            $totalGeral += $valor;
        }

        if ($controle !== '') {
            if ($temValor) {
                $controles[$controle] = ($controles[$controle] ?? 0) + 1;
            }
            if (!isset($envios[$controle])) {
                $envios[$controle] = [
                    'controle' => $controle,
                    'cnpj' => $cnpj,
                    'nome' => $nome,
                    'arranjo' => $arranjo,
                    'dt_pgto' => $dataPagamento,
                    'valor' => $valor,
                    'nu_liquid' => $nuLiquid,
                    'ctrl_cip' => $ctrlCip,
                    'nom_arq' => normalizarNomArqBase($nomArq),
                ];
            } else {
                if ($temValor) {
                    $envios[$controle]['valor'] += $valor;
                }
                foreach (['cnpj' => $cnpj, 'nome' => $nome, 'arranjo' => $arranjo, 'dt_pgto' => $dataPagamento] as $campo => $conteudo) {
                    if (($envios[$controle][$campo] === '' || $envios[$controle][$campo] === '-' || $envios[$controle][$campo] === 'SEM_CNPJ') && $conteudo !== '') {
                        $envios[$controle][$campo] = $conteudo;
                    }
                }
                if ($envios[$controle]['nu_liquid'] === '' && $nuLiquid !== '') {
                    $envios[$controle]['nu_liquid'] = $nuLiquid;
                }
                if ($envios[$controle]['ctrl_cip'] === '' && $ctrlCip !== '') {
                    $envios[$controle]['ctrl_cip'] = $ctrlCip;
                }
            }
        }
    }

    foreach ($gruposLiquidacao as $grupo) {
        $nuLiquid = textoNo($xpath, $grupo, 'NULiquid');
        if ($nuLiquid === '') {
            continue;
        }
        $controle = textoNoVariantes($xpath, $grupo, ['NumCtrlCreddrPontoVenda', 'NumCtrlCreddrPontoVendaActo', 'NumCtrlCreddrPontoVendaRecsdo']);
        $ctrlCip = textoNoVariantes($xpath, $grupo, ['NumCtrlCIPPontoVenda', 'NumCtrlCIPPontoVendaActo', 'NumCtrlCIPPontoVendaRecsdo']);
        $codOcorc = textoNo($xpath, $grupo, 'CodOcorc');

        if (!isset($liquidacoes[$nuLiquid])) {
            $liquidacoes[$nuLiquid] = [
                'nu_liquid' => $nuLiquid,
                'controle' => $controle,
                'ctrl_cip' => $ctrlCip,
                'cod_ocorc' => $codOcorc,
                'dt_hr_manut' => textoNo($xpath, $grupo, 'DtHrManut'),
                'sit_ret_req' => primeiroTextoAncestral($xpath, $grupo, 'SitRetReq'),
                'cnpj_creddr' => primeiroTextoAncestral($xpath, $grupo, 'CNPJCreddr'),
                'nom_arq' => primeiroTexto($xpath, 'NomArq'),
                'arquivos' => [],
            ];
        } else {
            foreach (['controle' => $controle, 'ctrl_cip' => $ctrlCip] as $campo => $conteudo) {
                if ($liquidacoes[$nuLiquid][$campo] === '' && $conteudo !== '') {
                    $liquidacoes[$nuLiquid][$campo] = $conteudo;
                }
            }
            if ($codOcorc !== '') {
                $liquidacoes[$nuLiquid]['cod_ocorc'] = $codOcorc;
            }
        }
    }

    uasort($porCnpj, function ($a, $b) {
        return $b['total'] <=> $a['total'];
    });
    uasort($porArranjo, function ($a, $b) {
        return [$a['cnpj'], $a['arranjo']] <=> [$b['cnpj'], $b['arranjo']];
    });

    $duplicados = [];
    foreach ($controles as $controle => $qtd) {
        if ($qtd > 1) {
            $duplicados[$controle] = $qtd;
        }
    }

    return [
        'tipo' => tipoArquivoNuclea($nomeArquivo, $nomArq),
        'meta' => [
            'nom_arq' => $nomArq,
            'num_ctrl_emis' => primeiroTexto($xpath, 'NumCtrlEmis'),
            'dt_ref' => primeiroTexto($xpath, 'DtRef'),
            'dt_hr_arq' => primeiroTexto($xpath, 'DtHrArq'),
            'ispb_emissor' => primeiroTexto($xpath, 'ISPBEmissor'),
            'ispb_destinatario' => primeiroTexto($xpath, 'ISPBDestinatario'),
            'cnpj_creddr' => primeiroTexto($xpath, 'CNPJCreddr'),
            'ispb_if_devedor' => primeiroTexto($xpath, 'ISPBIFDevdr'),
            'ispb_if_credor' => primeiroTexto($xpath, 'ISPBIFCredr'),
        ],
        'resumo' => [
            'qtd_cnpj' => count($porCnpj),
            'qtd_centralizadoras' => $centralizadoras ? $centralizadoras->length : 0,
            'qtd_pontos' => $pontos ? $pontos->length : 0,
            'total_geral' => $totalGeral,
            'datas_pagamento' => array_keys($datasPagamento),
            'qtd_controles' => count($controles),
            'qtd_controles_duplicados' => count($duplicados),
            'qtd_cod_erro' => $attrsErro ? $attrsErro->length : 0,
            'qtd_nuliquid' => count($liquidacoes),
        ],
        'por_cnpj' => $porCnpj,
        'por_arranjo' => $porArranjo,
        'duplicados' => $duplicados,
        'controles' => $controles,
        'envios' => $envios,
        'liquidacoes' => $liquidacoes,
        'status_liquidacoes' => [],
    ];
}

function filtrarResultado($resultado, $controlesPermitidos, $nuLiquidsPermitidos)
{
    $filtrado = $resultado;
    $filtrado['liquidacoes'] = [];
    $filtrado['envios'] = [];

    foreach ($resultado['liquidacoes'] as $nuLiquid => $liquidacao) {
        $controle = $liquidacao['controle'] ?? '';
        $manterPorControle = $controle !== '' && isset($controlesPermitidos[$controle]);
        $manterPorNuLiquid = isset($nuLiquidsPermitidos[$nuLiquid]);

        if ($manterPorControle || $manterPorNuLiquid) {
            $filtrado['liquidacoes'][$nuLiquid] = $liquidacao;
            if ($controle !== '' && isset($resultado['envios'][$controle])) {
                $filtrado['envios'][$controle] = $resultado['envios'][$controle];
            }
        }
    }

    $filtrado['resumo']['qtd_nuliquid'] = count($filtrado['liquidacoes']);
    return $filtrado;
}

function anexarEnviosPorOrdem(&$resultado, $enviosOrdenados)
{
    if (count($enviosOrdenados) === 0 || count($resultado['liquidacoes']) === 0) {
        return;
    }

    $idx = 0;
    foreach ($resultado['liquidacoes'] as $nuLiquid => &$liquidacao) {
        $controle = $liquidacao['controle'] ?? '';
        if ($controle === '' || isset($resultado['envios'][$controle])) {
            continue;
        }

        $envio = $enviosOrdenados[$idx] ?? null;
        if ($envio === null) {
            break;
        }

        $envio['controle'] = $controle;
        $envio['nu_liquid'] = $nuLiquid;
        $envio['ctrl_cip'] = $liquidacao['ctrl_cip'] ?? ($envio['ctrl_cip'] ?? '');
        $resultado['envios'][$controle] = $envio;
        $idx++;
    }
    unset($liquidacao);
}

function juntarConjuntos(&$destino, $origem)
{
    foreach ($origem as $chave => $_) {
        $destino[$chave] = true;
    }
}

function juntarLiquidacoes(&$destino, $origem, $arquivo, $tipo)
{
    foreach ($origem as $nuLiquid => $linha) {
        if (!isset($destino[$nuLiquid])) {
            $destino[$nuLiquid] = $linha;
            $destino[$nuLiquid]['arquivos'] = [];
        } else {
            foreach (['controle', 'ctrl_cip', 'dt_hr_manut', 'sit_ret_req', 'cnpj_creddr', 'nom_arq'] as $campo) {
                if (($destino[$nuLiquid][$campo] ?? '') === '' && ($linha[$campo] ?? '') !== '') {
                    $destino[$nuLiquid][$campo] = $linha[$campo];
                }
            }
            if (($linha['cod_ocorc'] ?? '') !== '') {
                $destino[$nuLiquid]['cod_ocorc'] = $linha['cod_ocorc'];
            }
        }

        $destino[$nuLiquid]['arquivos'][] = [
            'arquivo' => $arquivo,
            'tipo' => $tipo,
            'cod_ocorc' => $linha['cod_ocorc'] ?? '',
        ];
    }
}

function statusCodOcorc($codOcorc, $tiposArquivo)
{
    if ($codOcorc === '00') {
        return ['classe' => 'ok', 'texto' => 'Liquidado com sucesso'];
    }
    if ($codOcorc === '' && !in_array('Liquidado', $tiposArquivo, true)) {
        return ['classe' => 'pendente', 'texto' => 'Processado, aguardando liquidado'];
    }
    if ($codOcorc === '') {
        return ['classe' => 'pendente', 'texto' => 'Sem CodOcorc'];
    }
    return ['classe' => 'erro', 'texto' => 'Erro na liquidacao'];
}

function montarStatusLiquidacoes(&$resultado)
{
    $status = [];
    $chavesComRetorno = [];
    $ok = 0;
    $erro = 0;
    $semCodOcorc = 0;
    $semRetorno = 0;
    $totalOk = 0.0;
    $totalErro = 0.0;
    $totalPendente = 0.0;

    foreach ($resultado['liquidacoes'] as $nuLiquid => $liquidacao) {
        $controle = $liquidacao['controle'] ?? '';
        $envio = $controle !== '' && isset($resultado['envios'][$controle]) ? $resultado['envios'][$controle] : null;
        $codOcorc = $liquidacao['cod_ocorc'] ?? '';
        $tiposArquivo = array_column($liquidacao['arquivos'] ?? [], 'tipo');
        $statusCod = statusCodOcorc($codOcorc, $tiposArquivo);
        $valorStatus = (float) ($envio['valor'] ?? 0);

        if ($codOcorc === '00') {
            $ok++;
            $totalOk += $valorStatus;
        } elseif ($codOcorc === '') {
            $semCodOcorc++;
            $totalPendente += $valorStatus;
        } else {
            $erro++;
            $totalErro += $valorStatus;
        }

        $status[] = [
            'nu_liquid' => $nuLiquid,
            'controle' => $controle,
            'ctrl_cip' => $liquidacao['ctrl_cip'] ?? '',
            'cnpj' => $envio['cnpj'] ?? ($liquidacao['cnpj_creddr'] ?? ''),
            'nome' => $envio['nome'] ?? '',
            'arranjo' => $envio['arranjo'] ?? '',
            'dt_pgto' => $envio['dt_pgto'] ?? '',
            'valor' => $envio['valor'] ?? 0,
            'cod_ocorc' => $codOcorc,
            'status' => $statusCod['texto'],
            'classe' => $statusCod['classe'],
            'dt_hr_manut' => $liquidacao['dt_hr_manut'] ?? '',
            'arquivos' => implode(', ', array_map(function ($arquivo) {
                return $arquivo['tipo'] . ': ' . $arquivo['arquivo'];
            }, $liquidacao['arquivos'] ?? [])),
        ];

        if ($envio !== null) {
            $chavesComRetorno[chaveComparacaoEnvio($envio)] = true;
        }
    }

    foreach ($resultado['envios'] as $controle => $envio) {
        if (($envio['nu_liquid'] ?? '') !== '') {
            continue;
        }
        if (isset($chavesComRetorno[chaveComparacaoEnvio($envio)])) {
            continue;
        }
        $semRetorno++;
        $totalPendente += (float) ($envio['valor'] ?? 0);
        $status[] = [
            'nu_liquid' => '',
            'controle' => $controle,
            'ctrl_cip' => $envio['ctrl_cip'] ?? '',
            'cnpj' => $envio['cnpj'] ?? '',
            'nome' => $envio['nome'] ?? '',
            'arranjo' => $envio['arranjo'] ?? '',
            'dt_pgto' => $envio['dt_pgto'] ?? '',
            'valor' => $envio['valor'] ?? 0,
            'cod_ocorc' => '',
            'status' => 'Aguardando retorno processado',
            'classe' => 'pendente',
            'dt_hr_manut' => '',
            'arquivos' => '',
        ];
    }

    usort($status, function ($a, $b) {
        return [$a['classe'], $a['nu_liquid'], $a['controle']] <=> [$b['classe'], $b['nu_liquid'], $b['controle']];
    });

    $resultado['status_liquidacoes'] = $status;
    $resultado['resumo']['qtd_nuliquid'] = count($resultado['liquidacoes']);
    $resultado['resumo']['qtd_liquid_ok'] = $ok;
    $resultado['resumo']['qtd_liquid_erro'] = $erro;
    $resultado['resumo']['qtd_sem_cod_ocorc'] = $semCodOcorc;
    $resultado['resumo']['qtd_liquid_sem_retorno'] = $semRetorno;
    $resultado['resumo']['total_liquid_ok'] = $totalOk;
    $resultado['resumo']['total_liquid_erro'] = $totalErro;
    $resultado['resumo']['total_liquid_pendente'] = $totalPendente;
}

function chaveComparacaoEnvio($envio)
{
    return implode('|', [
        $envio['cnpj'] ?? '',
        $envio['arranjo'] ?? '',
        $envio['dt_pgto'] ?? '',
        number_format((float) ($envio['valor'] ?? 0), 2, '.', ''),
    ]);
}

function consolidarResultados($resultados, $ignorados = [], $nomArqsComFallback = [], $divergenciasValor = [])
{
    $consolidado = resultadoVazio();
    $controles = [];
    $datasPagamento = [];

    foreach ($resultados as $item) {
        $nomeArquivo = $item['arquivo'];
        $tipoArquivo = $item['tipo'];
        $resultado = $item['resultado'];
        $resumo = $resultado['resumo'];

        if ($consolidado['meta']['nom_arq'] === '') {
            $consolidado['meta'] = $resultado['meta'];
        }

        $consolidado['arquivos'][] = [
            'arquivo' => $nomeArquivo,
            'tipo' => $tipoArquivo,
            'nom_arq' => $resultado['meta']['nom_arq'],
            'qtd_cnpj' => $resumo['qtd_cnpj'],
            'qtd_centralizadoras' => $resumo['qtd_centralizadoras'],
            'qtd_pontos' => $resumo['qtd_pontos'],
            'total_geral' => $resumo['total_geral'],
            'qtd_cod_erro' => $resumo['qtd_cod_erro'],
            'qtd_nuliquid' => $resumo['qtd_nuliquid'],
        ];

        $consolidado['resumo']['qtd_centralizadoras'] += $resumo['qtd_centralizadoras'];
        $consolidado['resumo']['qtd_pontos'] += $resumo['qtd_pontos'];
        $consolidado['resumo']['total_geral'] += $resumo['total_geral'];
        $consolidado['resumo']['qtd_cod_erro'] += $resumo['qtd_cod_erro'];

        foreach ($resumo['datas_pagamento'] as $dataPagamento) {
            $datasPagamento[$dataPagamento] = true;
        }

        foreach ($resultado['por_cnpj'] as $cnpj => $linha) {
            if (!isset($consolidado['por_cnpj'][$cnpj])) {
                $consolidado['por_cnpj'][$cnpj] = $linha;
            } else {
                $consolidado['por_cnpj'][$cnpj]['qtd'] += $linha['qtd'];
                $consolidado['por_cnpj'][$cnpj]['total'] += $linha['total'];
                juntarConjuntos($consolidado['por_cnpj'][$cnpj]['arranjos'], $linha['arranjos']);
                juntarConjuntos($consolidado['por_cnpj'][$cnpj]['cod_pontos'], $linha['cod_pontos']);
                juntarConjuntos($consolidado['por_cnpj'][$cnpj]['ispbs'], $linha['ispbs']);
                juntarConjuntos($consolidado['por_cnpj'][$cnpj]['datas'], $linha['datas']);
            }
        }

        foreach ($resultado['por_arranjo'] as $chave => $linha) {
            if (!isset($consolidado['por_arranjo'][$chave])) {
                $consolidado['por_arranjo'][$chave] = $linha;
            } else {
                $consolidado['por_arranjo'][$chave]['qtd'] += $linha['qtd'];
                $consolidado['por_arranjo'][$chave]['total'] += $linha['total'];
                juntarConjuntos($consolidado['por_arranjo'][$chave]['datas'], $linha['datas']);
                juntarConjuntos($consolidado['por_arranjo'][$chave]['tipo_valor'], $linha['tipo_valor']);
            }
        }

        foreach ($resultado['controles'] as $controle => $qtd) {
            $controles[$controle] = ($controles[$controle] ?? 0) + $qtd;
        }

        foreach ($resultado['envios'] as $controle => $envio) {
            if (!isset($consolidado['envios'][$controle])) {
                $consolidado['envios'][$controle] = $envio;
            } else {
                foreach (['cnpj', 'nome', 'arranjo', 'dt_pgto'] as $campo) {
                    if (($consolidado['envios'][$controle][$campo] === '' || $consolidado['envios'][$controle][$campo] === '-' || $consolidado['envios'][$controle][$campo] === 'SEM_CNPJ') && ($envio[$campo] ?? '') !== '') {
                        $consolidado['envios'][$controle][$campo] = $envio[$campo];
                    }
                }
                if (($consolidado['envios'][$controle]['nu_liquid'] ?? '') === '' && ($envio['nu_liquid'] ?? '') !== '') {
                    $consolidado['envios'][$controle]['nu_liquid'] = $envio['nu_liquid'];
                }
                if (($consolidado['envios'][$controle]['ctrl_cip'] ?? '') === '' && ($envio['ctrl_cip'] ?? '') !== '') {
                    $consolidado['envios'][$controle]['ctrl_cip'] = $envio['ctrl_cip'];
                }
                if (($consolidado['envios'][$controle]['valor'] ?? 0) == 0 && ($envio['valor'] ?? 0) > 0) {
                    $consolidado['envios'][$controle]['valor'] = $envio['valor'];
                }
            }
        }

        juntarLiquidacoes($consolidado['liquidacoes'], $resultado['liquidacoes'], $nomeArquivo, $tipoArquivo);
    }

    uasort($consolidado['por_cnpj'], function ($a, $b) {
        return $b['total'] <=> $a['total'];
    });
    uasort($consolidado['por_arranjo'], function ($a, $b) {
        return [$a['cnpj'], $a['arranjo']] <=> [$b['cnpj'], $b['arranjo']];
    });

    $duplicados = [];
    foreach ($controles as $controle => $qtd) {
        if ($qtd > 1) {
            $duplicados[$controle] = $qtd;
        }
    }

    $consolidado['controles'] = $controles;
    $consolidado['duplicados'] = $duplicados;
    $consolidado['ignorados'] = $ignorados;
    $consolidado['divergencias_valor'] = $divergenciasValor;
    $consolidado['resumo']['qtd_cnpj'] = count($consolidado['por_cnpj']);
    $consolidado['resumo']['datas_pagamento'] = array_keys($datasPagamento);
    $consolidado['resumo']['qtd_controles'] = count($controles);
    $consolidado['resumo']['qtd_controles_duplicados'] = count($duplicados);
    $consolidado['resumo']['qtd_retorno_ignorado'] = count($ignorados);
    $consolidado['resumo']['qtd_divergencia_valor'] = count($divergenciasValor);
    foreach ($consolidado['envios'] as $controle => $envio) {
        $nomArqEnvio = $envio['nom_arq'] ?? '';
        if (($envio['nu_liquid'] ?? '') === '' && $nomArqEnvio !== '' && isset($nomArqsComFallback[$nomArqEnvio])) {
            unset($consolidado['envios'][$controle]);
        }
    }
    montarStatusLiquidacoes($consolidado);
    return $consolidado;
}

function resumoValorArquivo($item)
{
    return [
        'arquivo' => $item['arquivo'],
        'nom_arq' => normalizarNomArqBase($item['resultado']['meta']['nom_arq'] ?? ''),
        'total' => (float) ($item['resultado']['resumo']['total_geral'] ?? 0),
        'pontos' => (int) ($item['resultado']['resumo']['qtd_pontos'] ?? 0),
    ];
}

function valoresDivergem($a, $b)
{
    return abs($a['total'] - $b['total']) > 0.009 || $a['pontos'] !== $b['pontos'];
}

function uploadsParaResultados($campo, $tipoPadrao)
{
    $resultados = [];
    if (empty($_FILES[$campo]['tmp_name'])) {
        return $resultados;
    }

    $tmpNames = is_array($_FILES[$campo]['tmp_name']) ? $_FILES[$campo]['tmp_name'] : [$_FILES[$campo]['tmp_name']];
    $nomes = is_array($_FILES[$campo]['name']) ? $_FILES[$campo]['name'] : [$_FILES[$campo]['name'] ?? ''];

    foreach ($tmpNames as $idx => $tmpName) {
        if ($tmpName === '' || !is_uploaded_file($tmpName)) {
            continue;
        }
        $nome = $nomes[$idx] ?? ('arquivo_' . ($idx + 1));
        $analise = analisarXml(carregarXmlArquivo($tmpName), $nome);
        $tipo = $tipoPadrao ?: $analise['tipo'];
        $resultados[] = ['arquivo' => $nome, 'tipo' => $tipo, 'resultado' => $analise];
    }

    return $resultados;
}

function retornosDaPastaServidor()
{
    $arquivos = [];
    if (!is_dir(DIR_RETORNOS_NUCLEA)) {
        return $arquivos;
    }

    foreach (glob(DIR_RETORNOS_NUCLEA . '/*.xml') ?: [] as $path) {
        $nome = basename($path);
        $analise = analisarXml(carregarXmlArquivo($path), $nome);
        $arquivos[] = ['arquivo' => $nome, 'tipo' => $analise['tipo'], 'resultado' => $analise];
    }

    return $arquivos;
}

function enviadosDaPastaRetornos($arquivosPasta)
{
    $enviados = [];
    foreach ($arquivosPasta as $item) {
        if ($item['tipo'] === 'Aguardando') {
            $enviados[] = [
                'arquivo' => $item['arquivo'],
                'tipo' => 'Aguardando',
                'resultado' => $item['resultado'],
            ];
        }
    }
    return $enviados;
}

function prepararMerge($enviados, $retornos)
{
    if (count($enviados) === 0) {
        throw new Exception('Selecione os XMLs de aguardando/enviados. Os retornos so entram no merge se pertencerem a esses arquivos.');
    }

    $base = consolidarResultados($enviados);
    $controlesPermitidos = [];
    $nomArqsPermitidos = [];
    $enviosPorNomArq = [];
    $resumosEnviados = [];
    $divergenciasValor = [];
    foreach ($base['envios'] as $controle => $_) {
        $controlesPermitidos[$controle] = true;
    }
    foreach ($enviados as $item) {
        $nomArq = normalizarNomArqBase($item['resultado']['meta']['nom_arq'] ?? '');
        if ($nomArq === '') {
            continue;
        }
        $nomArqsPermitidos[$nomArq] = true;
        $enviosPorNomArq[$nomArq] = array_values($item['resultado']['envios']);
        $resumosEnviados[$nomArq] = resumoValorArquivo($item);
    }

    $retornosUsados = [];
    $ignorados = [];
    $nuLiquidsPermitidos = [];
    $nomArqsComFallback = [];

    foreach ($retornos as $item) {
        if ($item['tipo'] === 'Aguardando') {
            $nomArqRetorno = normalizarNomArqBase($item['resultado']['meta']['nom_arq'] ?? '');
            if (isset($resumosEnviados[$nomArqRetorno])) {
                $resumoRetorno = resumoValorArquivo($item);
                if (valoresDivergem($resumosEnviados[$nomArqRetorno], $resumoRetorno)) {
                    $divergenciasValor[] = [
                        'nom_arq' => $nomArqRetorno,
                        'arquivo_enviado' => $resumosEnviados[$nomArqRetorno]['arquivo'],
                        'arquivo_retorno' => $resumoRetorno['arquivo'],
                        'total_enviado' => $resumosEnviados[$nomArqRetorno]['total'],
                        'total_retorno' => $resumoRetorno['total'],
                        'pontos_enviado' => $resumosEnviados[$nomArqRetorno]['pontos'],
                        'pontos_retorno' => $resumoRetorno['pontos'],
                        'erro' => 'Valores do arquivo enviado nao batem com o arquivo de aguardando da pasta de retornos',
                    ];
                }
            }
            $ignorados[] = ['arquivo' => $item['arquivo'], 'motivo' => 'Arquivo de aguardando dentro da pasta de retornos'];
            continue;
        }
        if ($item['tipo'] !== 'Processado') {
            continue;
        }
        $nomArqRetorno = normalizarNomArqBase($item['resultado']['meta']['nom_arq'] ?? '');
        $filtrado = filtrarResultado($item['resultado'], $controlesPermitidos, []);
        if (count($filtrado['liquidacoes']) === 0 && isset($nomArqsPermitidos[$nomArqRetorno])) {
            $filtrado = $item['resultado'];
            anexarEnviosPorOrdem($filtrado, $enviosPorNomArq[$nomArqRetorno] ?? []);
            $nomArqsComFallback[$nomArqRetorno] = true;
        }
        if (count($filtrado['liquidacoes']) === 0) {
            $ignorados[] = ['arquivo' => $item['arquivo'], 'motivo' => 'Processado nao pertence aos enviados selecionados'];
            continue;
        }
        foreach ($filtrado['liquidacoes'] as $nuLiquid => $_) {
            $nuLiquidsPermitidos[$nuLiquid] = true;
        }
        $retornosUsados[] = ['arquivo' => $item['arquivo'], 'tipo' => $item['tipo'], 'resultado' => $filtrado];
    }

    foreach ($retornos as $item) {
        if ($item['tipo'] !== 'Liquidado') {
            continue;
        }
        $filtrado = filtrarResultado($item['resultado'], [], $nuLiquidsPermitidos);
        if (count($filtrado['liquidacoes']) === 0) {
            $ignorados[] = ['arquivo' => $item['arquivo'], 'motivo' => 'Liquidado sem NULiquid ligado aos processados dos enviados'];
            continue;
        }
        $retornosUsados[] = ['arquivo' => $item['arquivo'], 'tipo' => $item['tipo'], 'resultado' => $filtrado];
    }

    return consolidarResultados(array_merge($enviados, $retornosUsados), $ignorados, $nomArqsComFallback, $divergenciasValor);
}

function ordenarLista(&$lista)
{
    $lista = array_values($lista);
    sort($lista);
}

function normalizarResultadoExportacao($resultado)
{
    foreach (['por_cnpj', 'por_arranjo'] as $grupo) {
        foreach ($resultado[$grupo] as &$linha) {
            foreach (['arranjos', 'cod_pontos', 'ispbs', 'datas', 'tipo_valor'] as $campo) {
                if (isset($linha[$campo]) && is_array($linha[$campo])) {
                    $linha[$campo] = array_keys($linha[$campo]);
                    ordenarLista($linha[$campo]);
                }
            }
        }
        unset($linha);
    }
    if (isset($resultado['resumo']['datas_pagamento']) && is_array($resultado['resumo']['datas_pagamento'])) {
        ordenarLista($resultado['resumo']['datas_pagamento']);
    }
    return $resultado;
}

function payloadExportacao($resultado, $nomeUpload)
{
    return base64_encode(serialize([
        'nome_upload' => $nomeUpload,
        'gerado_em' => date('Y-m-d H:i:s'),
        'resultado' => normalizarResultadoExportacao($resultado),
    ]));
}

function lerPayloadExportacao()
{
    $payload = $_POST['payload_exportacao'] ?? '';
    if ($payload === '') {
        throw new Exception('Nao ha dados analisados para exportar.');
    }
    $raw = base64_decode($payload, true);
    if ($raw === false) {
        throw new Exception('Payload de exportacao invalido.');
    }
    $dados = @unserialize($raw, ['allowed_classes' => false]);
    if (!is_array($dados) || empty($dados['resultado']) || !is_array($dados['resultado'])) {
        throw new Exception('Payload de exportacao invalido.');
    }
    return $dados;
}

function xmlAdicionarTexto($dom, $pai, $nome, $valor)
{
    $node = $dom->createElement($nome);
    $node->appendChild($dom->createTextNode((string) $valor));
    $pai->appendChild($node);
}

function gerarXmlExportacao($dados)
{
    $resultado = $dados['resultado'];
    $dom = new DOMDocument('1.0', 'UTF-8');
    $dom->formatOutput = true;
    $root = $dom->createElement('analise_xml_nuclea');
    $dom->appendChild($root);
    xmlAdicionarTexto($dom, $root, 'arquivo_upload', $dados['nome_upload'] ?? '');
    xmlAdicionarTexto($dom, $root, 'gerado_em', $dados['gerado_em'] ?? date('Y-m-d H:i:s'));

    foreach (['meta', 'resumo'] as $blocoNome) {
        $bloco = $dom->createElement($blocoNome);
        foreach ($resultado[$blocoNome] as $nome => $valor) {
            if (is_array($valor)) {
                xmlAdicionarTexto($dom, $bloco, $nome, implode(', ', $valor));
            } else {
                xmlAdicionarTexto($dom, $bloco, $nome, $valor);
            }
        }
        $root->appendChild($bloco);
    }

    foreach (['arquivos', 'status_liquidacoes', 'ignorados', 'divergencias_valor'] as $listaNome) {
        $lista = $dom->createElement($listaNome);
        foreach ($resultado[$listaNome] as $linha) {
            $item = $dom->createElement('item');
            foreach ($linha as $nome => $valor) {
                xmlAdicionarTexto($dom, $item, $nome, $valor);
            }
            $lista->appendChild($item);
        }
        $root->appendChild($lista);
    }

    return $dom->saveXML();
}

function enviarExportacaoXml($dados)
{
    $resultado = $dados['resultado'];
    $base = nomeArquivoSeguro($resultado['meta']['nom_arq'] ?? '', 'merge_retorno_nuclea');
    $conteudo = gerarXmlExportacao($dados);
    header('Content-Type: application/xml; charset=UTF-8');
    header('Content-Disposition: attachment; filename="' . $base . '_merge.xml"');
    header('Content-Length: ' . strlen($conteudo));
    header('Cache-Control: no-cache, must-revalidate');
    header('Pragma: public');
    echo $conteudo;
    exit;
}
