<?php
declare(strict_types=1);

session_start();
date_default_timezone_set('America/Sao_Paulo');

$phpseclibBase = __DIR__ . '/phpseclib';
if (!is_dir($phpseclibBase)) {
    http_response_code(500);
    echo 'phpseclib nao encontrado em sftp-nuclea/phpseclib.';
    exit;
}

$currentIncludePath = get_include_path();
$extraPaths = [
    $phpseclibBase,
    $phpseclibBase . '/Net',
    $phpseclibBase . '/Crypt',
    $phpseclibBase . '/Math',
    $phpseclibBase . '/File',
    $phpseclibBase . '/System',
];
set_include_path($currentIncludePath . PATH_SEPARATOR . implode(PATH_SEPARATOR, $extraPaths));

if (!defined('NET_SSH2_LOGGING')) {
    define('NET_SSH2_LOGGING', 1);
}

require_once $phpseclibBase . '/bootstrap.php';
require_once $phpseclibBase . '/Net/SFTP.php';
require_once $phpseclibBase . '/Crypt/RSA.php';
require_once __DIR__ . '/spbseguranca.php';

if (!class_exists('Net_SFTP') || !class_exists('Crypt_RSA')) {
    http_response_code(500);
    echo 'Falha ao carregar classes do phpseclib.';
    exit;
}

const SFTP_SOCKET_TIMEOUT_SECONDS = 3;
const SFTP_SSH_TIMEOUT_SECONDS = 5;

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

class SftpConnectionException extends RuntimeException
{
    /** @var array<string, string> */
    private $details;

    public function __construct(string $message, array $details = [])
    {
        parent::__construct($message);
        $this->details = $details;
    }

    public function getDetails(): array
    {
        return $this->details;
    }
}

function buildSftpDebugDetails(
    string $stage,
    string $host,
    int $port,
    string $username,
    string $privateKey,
    float $startedAt,
    array $extra = []
): array {
    $details = [
        'endpoint' => $host . ':' . $port,
        'host' => $host,
        'porta' => (string)$port,
        'usuario' => $username,
        'etapa' => $stage,
        'timeout_socket_segundos' => (string)SFTP_SOCKET_TIMEOUT_SECONDS,
        'timeout_phpseclib_segundos' => (string)SFTP_SSH_TIMEOUT_SECONDS,
        'chave_privada' => $privateKey,
        'passphrase_informada' => 'nao',
        'tempo_decorrido_segundos' => number_format(microtime(true) - $startedAt, 3, '.', ''),
    ];

    foreach ($extra as $key => $value) {
        if ($value === null || $value === '') {
            continue;
        }
        $details[$key] = is_scalar($value) ? (string)$value : json_encode($value);
    }

    return $details;
}

function sftpDebugDetailsFromThrowable(Throwable $e): array
{
    if ($e instanceof SftpConnectionException) {
        return $e->getDetails();
    }

    return [
        'mensagem_erro' => $e->getMessage(),
        'classe_erro' => get_class($e),
        'arquivo' => $e->getFile(),
        'linha' => (string)$e->getLine(),
    ];
}

function collectSftpLibraryDetails($sftp): array
{
    $details = [];

    if (is_object($sftp) && method_exists($sftp, 'isTimeout') && $sftp->isTimeout()) {
        $details['timeout_interno_phpseclib'] = 'sim';
    }
    if (is_object($sftp) && method_exists($sftp, 'getLastError')) {
        $lastError = (string)$sftp->getLastError();
        if ($lastError !== '') {
            $details['ultimo_erro_ssh'] = $lastError;
        }
    }
    if (is_object($sftp) && method_exists($sftp, 'getErrors')) {
        $errors = $sftp->getErrors();
        if (is_array($errors) && count($errors) > 0) {
            $details['erros_ssh'] = implode(' | ', $errors);
        }
    }
    if (is_object($sftp) && method_exists($sftp, 'getLastSFTPError')) {
        $lastSftpError = (string)$sftp->getLastSFTPError();
        if ($lastSftpError !== '') {
            $details['ultimo_erro_sftp'] = $lastSftpError;
        }
    }
    if (is_object($sftp) && method_exists($sftp, 'getSFTPErrors')) {
        $sftpErrors = $sftp->getSFTPErrors();
        if (is_array($sftpErrors) && count($sftpErrors) > 0) {
            $details['erros_sftp'] = implode(' | ', $sftpErrors);
        }
    }
    if (is_object($sftp) && method_exists($sftp, 'getLog')) {
        $log = $sftp->getLog();
        if (is_array($log) && count($log) > 0) {
            $logText = implode(' | ', array_slice($log, -20));
            $details['log_ssh_ultimos_eventos'] = $logText;
        } elseif (is_string($log) && $log !== '') {
            $details['log_ssh_ultimos_eventos'] = substr(strip_tags($log), -4000);
        }
    }

    return $details;
}

function normalizeRemotePath(string $path): string
{
    $path = trim($path);
    if ($path === '') {
        return '/';
    }

    if ($path[0] !== '/') {
        $path = '/' . $path;
    }

    $parts = [];
    foreach (explode('/', $path) as $part) {
        if ($part === '' || $part === '.') {
            continue;
        }
        if ($part === '..') {
            array_pop($parts);
            continue;
        }
        $parts[] = $part;
    }

    return '/' . implode('/', $parts);
}

function parentRemotePath(string $path): string
{
    if ($path === '/' || $path === '') {
        return '/';
    }

    $segments = explode('/', trim($path, '/'));
    array_pop($segments);
    if (count($segments) === 0) {
        return '/';
    }

    return '/' . implode('/', $segments);
}

function sftpConnect(array $config): array
{
    $startedAt = microtime(true);
    $host = (string)($config['host'] ?? '');
    $port = (int)($config['port'] ?? 22);
    $username = (string)($config['username'] ?? '');
    $privateKey = (string)($config['private_key'] ?? '');
    $passphrase = (string)($config['passphrase'] ?? '');

    if ($host === '' || $username === '' || $privateKey === '') {
        throw new SftpConnectionException(
            'Host, usuario e chave privada sao obrigatorios.',
            buildSftpDebugDetails('validacao_configuracao', $host, $port, $username, $privateKey, $startedAt)
        );
    }

    if (!is_file($privateKey)) {
        throw new SftpConnectionException(
            'Chave privada nao encontrada: ' . $privateKey,
            buildSftpDebugDetails('validacao_chave_privada', $host, $port, $username, $privateKey, $startedAt)
        );
    }

    $keyContents = @file_get_contents($privateKey);
    if ($keyContents === false || $keyContents === '') {
        throw new SftpConnectionException(
            'Nao foi possivel ler a chave privada: ' . $privateKey,
            buildSftpDebugDetails('leitura_chave_privada', $host, $port, $username, $privateKey, $startedAt)
        );
    }

    $rsa = new Crypt_RSA();
    if ($passphrase !== '') {
        $rsa->setPassword($passphrase);
    }
    if (!$rsa->loadKey($keyContents)) {
        throw new SftpConnectionException(
            'Falha ao carregar chave privada. Verifique formato OpenSSH e passphrase.',
            buildSftpDebugDetails('carregamento_chave_privada', $host, $port, $username, $privateKey, $startedAt, [
                'passphrase_informada' => $passphrase !== '' ? 'sim' : 'nao',
            ])
        );
    }

    $socketErrorNo = 0;
    $socketError = '';
    $socket = @fsockopen($host, $port, $socketErrorNo, $socketError, SFTP_SOCKET_TIMEOUT_SECONDS);
    if (!is_resource($socket)) {
        throw new SftpConnectionException(
            'Falha de rede ao conectar em ' . $host . ':' . $port .
            ' (erro ' . $socketErrorNo . ': ' . $socketError . ').',
            buildSftpDebugDetails('abertura_socket', $host, $port, $username, $privateKey, $startedAt, [
                'passphrase_informada' => $passphrase !== '' ? 'sim' : 'nao',
                'socket_errno' => (string)$socketErrorNo,
                'socket_erro' => $socketError,
            ])
        );
    }
    fclose($socket);

    $sftp = new Net_SFTP($host, $port, SFTP_SSH_TIMEOUT_SECONDS);
    $phpseclibWarnings = [];

    set_error_handler(
        static function (int $severity, string $message, string $file, int $line) use (&$phpseclibWarnings): bool {
            $phpseclibWarnings[] = $message . ' em ' . $file . ':' . $line;
            return true;
        }
    );

    try {
        $loginOk = $sftp->login($username, $rsa);
    } finally {
        restore_error_handler();
    }

    if (!$loginOk) {
        $libraryDetails = collectSftpLibraryDetails($sftp);
        if (count($phpseclibWarnings) > 0) {
            $libraryDetails['avisos_phpseclib'] = implode(' | ', array_slice($phpseclibWarnings, -10));
        }
        $suffix = count($libraryDetails) > 0 ? ' Detalhes: ' . implode(' || ', $libraryDetails) : '';
        throw new SftpConnectionException(
            'Falha de autenticacao com chave privada/passphrase.' . $suffix,
            buildSftpDebugDetails('login_sftp', $host, $port, $username, $privateKey, $startedAt, array_merge([
                'passphrase_informada' => $passphrase !== '' ? 'sim' : 'nao',
            ], $libraryDetails))
        );
    }

    return [null, $sftp];
}

function listRemoteDirectory($sftp, string $path): array
{
    $rawList = $sftp->rawlist($path, false);
    if ($rawList === false) {
        throw new RuntimeException('Nao foi possivel abrir diretorio remoto: ' . $path);
    }

    $items = [];
    foreach ($rawList as $entry => $attrs) {
        if ($entry === '.' || $entry === '..') {
            continue;
        }

        $entryPath = normalizeRemotePath($path . '/' . $entry);
        $type = isset($attrs['type']) ? (int)$attrs['type'] : -1;
        $directoryType = defined('NET_SFTP_TYPE_DIRECTORY') ? NET_SFTP_TYPE_DIRECTORY : 2;
        $isDir = $type === $directoryType;
        $size = $isDir ? null : (isset($attrs['size']) ? (int)$attrs['size'] : null);
        $mtime = isset($attrs['mtime']) ? (int)$attrs['mtime'] : null;

        $items[] = [
            'name' => $entry,
            'path' => $entryPath,
            'is_dir' => $isDir,
            'size' => $size,
            'mtime' => $mtime,
        ];
    }

    usort(
        $items,
        static function (array $a, array $b): int {
            if ($a['is_dir'] !== $b['is_dir']) {
                return $a['is_dir'] ? -1 : 1;
            }
            return strcasecmp($a['name'], $b['name']);
        }
    );

    return $items;
}

function sortItems(array $items, string $sortField, string $sortDirection): array
{
    $allowedFields = ['name', 'size', 'mtime'];
    if (!in_array($sortField, $allowedFields, true)) {
        $sortField = 'name';
    }

    $sortDirection = strtolower($sortDirection) === 'desc' ? 'desc' : 'asc';
    $multiplier = $sortDirection === 'desc' ? -1 : 1;

    usort(
        $items,
        static function (array $a, array $b) use ($sortField, $multiplier): int {
            if ($a['is_dir'] !== $b['is_dir']) {
                return $a['is_dir'] ? -1 : 1;
            }

            if ($sortField === 'name') {
                return strcasecmp($a['name'], $b['name']) * $multiplier;
            }

            $aValue = $a[$sortField] ?? null;
            $bValue = $b[$sortField] ?? null;

            if ($aValue === $bValue) {
                return strcasecmp($a['name'], $b['name']);
            }
            if ($aValue === null) {
                return 1;
            }
            if ($bValue === null) {
                return -1;
            }

            return (($aValue <=> $bValue) * $multiplier);
        }
    );

    return $items;
}

function formatBytes(?int $bytes): string
{
    if ($bytes === null) {
        return '-';
    }

    $units = ['B', 'KB', 'MB', 'GB', 'TB'];
    $value = (float)$bytes;
    $i = 0;
    while ($value >= 1024 && $i < count($units) - 1) {
        $value /= 1024;
        $i++;
    }
    return number_format($value, $i === 0 ? 0 : 2, ',', '.') . ' ' . $units[$i];
}

function formatDate(?int $timestamp): string
{
    if ($timestamp === null) {
        return '-';
    }
    return date('d/m/Y H:i:s', $timestamp);
}

function isSpbPayload(string $payload): bool
{
    if (strlen($payload) < SpbSeguranca::TAMANHO_CABECALHO) {
        return false;
    }
    $headerSize = unpack('n', substr($payload, 0, 2))[1] ?? 0;
    if ($headerSize !== SpbSeguranca::TAMANHO_CABECALHO) {
        return false;
    }
    $version = ord($payload[2]);
    return $version === SpbSeguranca::VERSAO_2 || $version === SpbSeguranca::VERSAO_3;
}

function decryptSpbToUtf8Xml(string $payload, array $security): string
{
    $privateKeyPath = (string)($security['private_key'] ?? '');
    $privateKeyPass = (string)($security['private_key_password'] ?? '');
    $senderCertPath = (string)($security['sender_cert'] ?? '');

    if (!is_file($privateKeyPath) || !is_file($senderCertPath)) {
        throw new RuntimeException('Arquivos de seguranca SPB nao encontrados no caminho configurado.');
    }

    $privateKeyPem = file_get_contents($privateKeyPath);
    $senderCertPem = file_get_contents($senderCertPath);
    if ($privateKeyPem === false || $senderCertPem === false) {
        throw new RuntimeException('Nao foi possivel ler os arquivos de seguranca SPB.');
    }

    $privateKey = openssl_pkey_get_private($privateKeyPem, $privateKeyPass);
    if ($privateKey === false) {
        throw new RuntimeException('Falha ao abrir chave privada SPB (senha incorreta?).');
    }

    $xmlOriginal = SpbSeguranca::decifrarEVerificar($payload, $privateKey, $senderCertPem);
    $xmlUtf8 = function_exists('mb_convert_encoding')
        ? mb_convert_encoding($xmlOriginal, 'UTF-8', 'UTF-16BE')
        : $xmlOriginal;

    $dom = new DOMDocument('1.0', 'UTF-8');
    $dom->preserveWhiteSpace = false;
    $dom->formatOutput = true;

    if (@$dom->loadXML($xmlUtf8)) {
        return $dom->saveXML();
    }

    return $xmlUtf8;
}

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

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

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

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

function carregarXmlTexto(string $raw): DOMDocument
{
    if (trim($raw) === '') {
        throw new RuntimeException('Nao foi possivel ler o XML descriptografado.');
    }

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

    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 RuntimeException('XML invalido apos descriptografia. ' . implode('; ', $erros));
}

function analisarXmlPreview(DOMDocument $dom): array
{
    $xpath = new DOMXPath($dom);
    $pontos = $xpath->query('//*[local-name()="Grupo_ASLC027_PontoVenda" or local-name()="Grupo_ASLC029_PontoVenda" or local-name()="Grupo_ASLC029RET_PontoVendaRecsdo" or local-name()="Grupo_ASLC031_PontoVenda" or local-name()="Grupo_ASLC031RET_PontoVendaRecsdo"]');
    $centralizadoras = $xpath->query('//*[local-name()="Grupo_ASLC027_Centrlz" or local-name()="Grupo_ASLC029_Centrlz" or local-name()="Grupo_ASLC029RET_CentrlzRecsdo" or local-name()="Grupo_ASLC031_Centrlz" or local-name()="Grupo_ASLC031RET_CentrlzRecsdo"]');
    $attrsErro = $xpath->query('//@CodErro');

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

    foreach ($pontos as $ponto) {
        $cnpj = textoNo($xpath, $ponto, 'CNPJ_CPFPontoVenda');
        $nome = textoNo($xpath, $ponto, 'NomePontoVenda');
        $arranjo = textoNo($xpath, $ponto, 'CodInstitdrArrajPgto');
        $dataPagamento = textoNo($xpath, $ponto, 'DtPgto');
        $valor = normalizarValor(textoNo($xpath, $ponto, 'VlrPgto'));
        $controle = textoNo($xpath, $ponto, 'NumCtrlCreddrPontoVenda');
        $codPontoVenda = textoNo($xpath, $ponto, 'CodPontoVenda');
        $ispb = textoNo($xpath, $ponto, 'ISPBIFLiquidPontoVenda');
        $tipoValor = textoNo($xpath, $ponto, 'TpVlrPgto');

        if ($cnpj === '') {
            $cnpj = 'SEM_CNPJ';
        }
        if ($nome === '') {
            $nome = '-';
        }
        if ($arranjo === '') {
            $arranjo = '-';
        }

        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;
        }

        if ($controle !== '') {
            $controles[$controle] = ($controles[$controle] ?? 0) + 1;
        }
        $totalGeral += $valor;
    }

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

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

    return [
        'meta' => [
            'nom_arq' => primeiroTexto($xpath, '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,
        ],
        'por_cnpj' => $porCnpj,
        'por_arranjo' => $porArranjo,
        'duplicados' => $duplicados,
    ];
}

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

function comparableUploadName(string $filename): string
{
    $filename = safeLocalFilename($filename);
    $info = pathinfo($filename);
    return strtolower((string)($info['filename'] ?? $filename));
}

function comparableXmlNomArq(string $nomArq): string
{
    return strtolower(trim($nomArq));
}

function ensureDirectory(string $dir): void
{
    if (!is_dir($dir) && !mkdir($dir, 0775, true)) {
        throw new RuntimeException('Nao foi possivel criar a pasta local: ' . $dir);
    }
}

function formatXmlUtf8(string $xml): string
{
    $dom = carregarXmlTexto($xml);
    $dom->formatOutput = true;
    return (string)$dom->saveXML();
}

function firstExistingPath(array $candidates): string
{
    foreach ($candidates as $candidate) {
        if (is_string($candidate) && $candidate !== '' && is_file($candidate)) {
            return $candidate;
        }
    }
    return '';
}

$homeDir = (string)($_SERVER['HOME'] ?? getenv('HOME') ?: '');
$externalCertBase = $homeDir !== '' ? rtrim($homeDir, '/\\') . '/certificados/certificados_nuclea' : '';
$localCertBase = __DIR__ . '/certificados_nuclea';

$hardcodedConfig = [
    'host' => 'sftp-prd.nucleaservicos.com.br',
    'port' => 22,
    'username' => 'SLC42403952PRD',
    'private_key' => firstExistingPath([
        $externalCertBase . '/id_rsa4',
        $localCertBase . '/id_rsa4',
        __DIR__ . '/id_rsa4',
    ]),
];
$spbSecurityConfig = [
    'sender_cert' => firstExistingPath([
        $externalCertBase . '/SLC-T011.cer',
        $localCertBase . '/SLC-T011.cer',
    ]),
    'private_key' => firstExistingPath([
        $externalCertBase . '/Certnew.key',
        $localCertBase . '/Certnew.key',
    ]),
    'private_key_password' => 'Du25041977@',
];

$message = '';
$messageType = 'info';
$messageDetails = [];
$shouldPromptPassphrase = false;
$uploadPreview = null;

if (isset($_GET['logout'])) {
    unset($_SESSION['sftp_passphrase']);
    unset($_SESSION['sftp_upload_pending']);
    header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
    exit;
}

$requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if ($requestMethod === 'POST') {
    $action = (string)($_POST['action'] ?? '');
    if ($action === 'set_passphrase') {
        $_SESSION['sftp_passphrase'] = (string)($_POST['passphrase'] ?? '');
        $redirectPath = normalizeRemotePath((string)($_POST['path'] ?? '/'));
        header('Location: ?path=' . rawurlencode($redirectPath));
        exit;
    }
    if ($action === 'clear_passphrase') {
        unset($_SESSION['sftp_passphrase']);
        header('Location: ?path=%2F');
        exit;
    }
    if ($action === 'cancel_upload') {
        unset($_SESSION['sftp_upload_pending']);
        $_SESSION['sftp_flash_message'] = 'Upload cancelado.';
        $_SESSION['sftp_flash_type'] = 'info';
        $cancelPath = normalizeRemotePath((string)($_POST['path'] ?? '/'));
        header('Location: ?path=' . rawurlencode($cancelPath));
        exit;
    }
    if ($action === 'upload_preview') {
        $uploadPath = normalizeRemotePath((string)($_POST['path'] ?? '/'));
        $uploadSort = (string)($_POST['sort'] ?? 'name');
        $uploadDir = (string)($_POST['dir'] ?? 'asc');

        try {
            if (!isset($_FILES['upload_encrypted']) || !is_array($_FILES['upload_encrypted'])) {
                throw new RuntimeException('Selecione o arquivo criptografado.');
            }
            if (!isset($_FILES['upload_xml']) || !is_array($_FILES['upload_xml'])) {
                throw new RuntimeException('Selecione o XML correspondente.');
            }

            $encryptedFile = $_FILES['upload_encrypted'];
            $xmlFile = $_FILES['upload_xml'];
            $encryptedErrorCode = (int)($encryptedFile['error'] ?? UPLOAD_ERR_NO_FILE);
            $xmlErrorCode = (int)($xmlFile['error'] ?? UPLOAD_ERR_NO_FILE);
            if ($encryptedErrorCode !== UPLOAD_ERR_OK) {
                throw new RuntimeException('Falha no upload do arquivo criptografado (codigo ' . $encryptedErrorCode . ').');
            }
            if ($xmlErrorCode !== UPLOAD_ERR_OK) {
                throw new RuntimeException('Falha no upload do XML (codigo ' . $xmlErrorCode . ').');
            }

            $encryptedTmpName = (string)($encryptedFile['tmp_name'] ?? '');
            $xmlTmpName = (string)($xmlFile['tmp_name'] ?? '');
            $originalName = safeLocalFilename((string)($encryptedFile['name'] ?? ''));
            $xmlOriginalName = safeLocalFilename((string)($xmlFile['name'] ?? ''));
            if (!is_uploaded_file($encryptedTmpName)) {
                throw new RuntimeException('Upload local do arquivo criptografado invalido.');
            }
            if (!is_uploaded_file($xmlTmpName)) {
                throw new RuntimeException('Upload local do XML invalido.');
            }

            $encryptedPayload = file_get_contents($encryptedTmpName);
            if ($encryptedPayload === false || $encryptedPayload === '') {
                throw new RuntimeException('Nao foi possivel ler o arquivo criptografado enviado.');
            }

            if (!isSpbPayload($encryptedPayload)) {
                throw new RuntimeException('Envie um arquivo SPB criptografado valido no primeiro campo.');
            }

            $xmlRaw = file_get_contents($xmlTmpName);
            if ($xmlRaw === false || trim($xmlRaw) === '') {
                throw new RuntimeException('Nao foi possivel ler o XML enviado.');
            }

            $xmlDom = carregarXmlTexto($xmlRaw);
            $xmlDom->formatOutput = true;
            $xmlUtf8 = (string)$xmlDom->saveXML();
            $analysis = analisarXmlPreview($xmlDom);

            $nomArqXml = (string)($analysis['meta']['nom_arq'] ?? '');
            if ($nomArqXml === '') {
                throw new RuntimeException('O XML nao possui a tag NomArq para conferir com o arquivo criptografado.');
            }
            if (comparableUploadName($originalName) !== comparableXmlNomArq($nomArqXml)) {
                throw new RuntimeException(
                    'O arquivo criptografado precisa bater com o NomArq do XML. ' .
                    'Arquivo: ' . $originalName . ' | NomArq: ' . $nomArqXml
                );
            }

            $baseDir = __DIR__ . '/uploads/nuclea/' . date('Ymd');
            ensureDirectory($baseDir);
            $uniqueBase = date('His') . '_' . bin2hex(random_bytes(4)) . '_' . pathinfo($originalName, PATHINFO_FILENAME);
            $encryptedPath = $baseDir . '/' . $uniqueBase . '_criptografado_' . $originalName;
            $xmlPath = $baseDir . '/' . $uniqueBase . '_xml_' . $xmlOriginalName;

            if (file_put_contents($encryptedPath, $encryptedPayload) === false) {
                throw new RuntimeException('Nao foi possivel salvar o arquivo criptografado localmente.');
            }
            if (file_put_contents($xmlPath, $xmlUtf8) === false) {
                throw new RuntimeException('Nao foi possivel salvar o XML localmente.');
            }

            $_SESSION['sftp_upload_pending'] = [
                'token' => bin2hex(random_bytes(16)),
                'path' => $uploadPath,
                'sort' => $uploadSort,
                'dir' => $uploadDir,
                'original_name' => $originalName,
                'xml_original_name' => $xmlOriginalName,
                'encrypted_path' => $encryptedPath,
                'xml_path' => $xmlPath,
                'analysis' => $analysis,
                'created_at' => time(),
            ];

            $_SESSION['sftp_flash_message'] = 'Previa gerada pelo XML. Confira os dados e confirme para enviar o arquivo criptografado ao SFTP.';
            $_SESSION['sftp_flash_type'] = 'success';
        } catch (Throwable $e) {
            unset($_SESSION['sftp_upload_pending']);
            $_SESSION['sftp_flash_message'] = $e->getMessage();
            $_SESSION['sftp_flash_type'] = 'error';
        }

        header(
            'Location: ?path=' . rawurlencode($uploadPath) .
            '&sort=' . rawurlencode($uploadSort) .
            '&dir=' . rawurlencode($uploadDir)
        );
        exit;
    }
    if ($action === 'confirm_upload') {
        $uploadPath = normalizeRemotePath((string)($_POST['path'] ?? '/'));
        $uploadSort = (string)($_POST['sort'] ?? 'name');
        $uploadDir = (string)($_POST['dir'] ?? 'asc');

        try {
            if (!array_key_exists('sftp_passphrase', $_SESSION)) {
                throw new RuntimeException('Informe a senha/passphrase antes de enviar arquivos.');
            }
            $pending = $_SESSION['sftp_upload_pending'] ?? null;
            if (!is_array($pending)) {
                throw new RuntimeException('Nenhum upload pendente para confirmar.');
            }
            $token = (string)($_POST['token'] ?? '');
            if ($token === '' || !hash_equals((string)$pending['token'], $token)) {
                throw new RuntimeException('Confirmacao de upload invalida. Gere a previa novamente.');
            }

            $originalName = (string)$pending['original_name'];
            $encryptedPath = (string)$pending['encrypted_path'];
            if (!is_file($encryptedPath)) {
                throw new RuntimeException('Arquivo criptografado local nao encontrado. Gere a previa novamente.');
            }

            $uploadConfig = $hardcodedConfig;
            $uploadConfig['passphrase'] = (string)$_SESSION['sftp_passphrase'];
            [, $uploadSftp] = sftpConnect($uploadConfig);

            $remoteFilePath = normalizeRemotePath($uploadPath . '/' . $originalName);
            $putResult = $uploadSftp->put($remoteFilePath, $encryptedPath, NET_SFTP_LOCAL_FILE);
            if ($putResult !== true) {
                throw new SftpConnectionException(
                    'Falha ao enviar arquivo para o SFTP.',
                    buildSftpDebugDetails(
                        'upload_arquivo_sftp',
                        (string)$uploadConfig['host'],
                        (int)$uploadConfig['port'],
                        (string)$uploadConfig['username'],
                        (string)$uploadConfig['private_key'],
                        microtime(true),
                        array_merge([
                            'passphrase_informada' => ((string)$uploadConfig['passphrase']) !== '' ? 'sim' : 'nao',
                            'arquivo_local' => $encryptedPath,
                            'arquivo_remoto' => $remoteFilePath,
                        ], collectSftpLibraryDetails($uploadSftp))
                    )
                );
            }

            unset($_SESSION['sftp_upload_pending']);
            $_SESSION['sftp_flash_message'] = 'Upload confirmado e realizado com sucesso: ' . $originalName;
            $_SESSION['sftp_flash_type'] = 'success';
        } catch (Throwable $e) {
            $_SESSION['sftp_flash_message'] = $e->getMessage();
            $_SESSION['sftp_flash_type'] = 'error';
            $_SESSION['sftp_flash_details'] = sftpDebugDetailsFromThrowable($e);
        }

        header(
            'Location: ?path=' . rawurlencode($uploadPath) .
            '&sort=' . rawurlencode($uploadSort) .
            '&dir=' . rawurlencode($uploadDir)
        );
        exit;
    }
}

$path = normalizeRemotePath((string)($_GET['path'] ?? '/'));
$sort = (string)($_GET['sort'] ?? 'name');
$dir = (string)($_GET['dir'] ?? 'asc');
$items = [];
$connected = false;
$hasSessionPassphrase = array_key_exists('sftp_passphrase', $_SESSION);
$config = $hardcodedConfig;
$config['passphrase'] = $hasSessionPassphrase ? (string)$_SESSION['sftp_passphrase'] : '';

if (isset($_SESSION['sftp_flash_message'])) {
    $message = (string)$_SESSION['sftp_flash_message'];
    $messageType = (string)($_SESSION['sftp_flash_type'] ?? 'info');
    if (isset($_SESSION['sftp_flash_details']) && is_array($_SESSION['sftp_flash_details'])) {
        $messageDetails = $_SESSION['sftp_flash_details'];
    }
    unset($_SESSION['sftp_flash_message'], $_SESSION['sftp_flash_type'], $_SESSION['sftp_flash_details']);
}

if ($hasSessionPassphrase) {
    try {
        [, $sftp] = sftpConnect($config);
        $connected = true;
        if ($message === '') {
            $message = 'Conectado no SFTP com sucesso.';
            $messageType = 'success';
        }

        if (isset($_GET['download'])) {
            $downloadPath = normalizeRemotePath((string)$_GET['download']);

            if (!$sftp->is_file($downloadPath)) {
                throw new RuntimeException('Arquivo remoto nao encontrado para download.');
            }

            $filename = basename($downloadPath);
            $fileSize = $sftp->size($downloadPath);
            $fileContents = $sftp->get($downloadPath);
            if ($fileContents === false) {
                throw new RuntimeException('Nao foi possivel baixar o arquivo remoto.');
            }

            $downloadName = $filename;
            $contentType = 'application/octet-stream';
            if (isSpbPayload($fileContents)) {
                $fileContents = decryptSpbToUtf8Xml($fileContents, $spbSecurityConfig);
                $downloadName .= '_decriptado_utf8.xml';
                $contentType = 'application/xml; charset=UTF-8';
                $fileSize = strlen($fileContents);
            }

            header('Content-Description: File Transfer');
            header('Content-Type: ' . $contentType);
            header('Content-Disposition: attachment; filename="' . rawurlencode($downloadName) . '"');
            if (is_int($fileSize) && $fileSize >= 0) {
                header('Content-Length: ' . (string)$fileSize);
            }
            header('Cache-Control: no-cache, must-revalidate');
            header('Pragma: public');
            echo $fileContents;
            exit;
        }

        $items = listRemoteDirectory($sftp, $path);
        $items = sortItems($items, $sort, $dir);
    } catch (Throwable $e) {
        $connected = false;
        unset($_SESSION['sftp_passphrase']);
        $shouldPromptPassphrase = true;
        $message = $e->getMessage();
        $messageType = 'error';
        $messageDetails = sftpDebugDetailsFromThrowable($e);
        if (!isset($messageDetails['endpoint'])) {
            $messageDetails = array_merge(
                buildSftpDebugDetails(
                    'operacao_sftp',
                    (string)$config['host'],
                    (int)$config['port'],
                    (string)$config['username'],
                    (string)$config['private_key'],
                    microtime(true),
                    [
                        'passphrase_informada' => $config['passphrase'] !== '' ? 'sim' : 'nao',
                    ]
                ),
                $messageDetails
            );
        }
    }
} else {
    $shouldPromptPassphrase = true;
    $message = 'Digite a senha/passphrase para abrir a conexao SFTP.';
}

if (isset($_SESSION['sftp_upload_pending']) && is_array($_SESSION['sftp_upload_pending'])) {
    $uploadPreview = $_SESSION['sftp_upload_pending'];
}
?>
<!doctype html>
<html lang="pt-br">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>SFTP Nuclea</title>
    <style>
        :root {
            --bg: #f3f6fb;
            --card: #ffffff;
            --text: #1d2a3b;
            --primary: #0f4c81;
            --border: #dce6f2;
            --success: #0b7a4b;
            --error: #b42318;
        }
        * { box-sizing: border-box; }
        body {
            margin: 0;
            font-family: "Segoe UI", Tahoma, sans-serif;
            color: var(--text);
            background: linear-gradient(160deg, #edf3fb 0%, #f8fbff 100%);
        }
        .container {
            max-width: 1100px;
            margin: 24px auto;
            padding: 0 16px;
        }
        .card {
            background: var(--card);
            border: 1px solid var(--border);
            border-radius: 12px;
            padding: 16px;
            margin-bottom: 16px;
            box-shadow: 0 8px 22px rgba(31, 63, 104, 0.06);
        }
        h1 {
            font-size: 22px;
            margin: 0 0 12px;
            color: var(--primary);
        }
        .grid {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 12px;
        }
        .field {
            display: flex;
            flex-direction: column;
            gap: 6px;
        }
        .field.full { grid-column: 1 / -1; }
        label { font-weight: 600; font-size: 13px; }
        .upload-fields {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 10px;
            width: 100%;
        }
        input {
            width: 100%;
            height: 38px;
            border: 1px solid #c8d7ea;
            border-radius: 8px;
            padding: 0 10px;
            outline: none;
            font-size: 14px;
        }
        input:focus { border-color: #7ea3cd; }
        .actions {
            display: flex;
            gap: 8px;
            margin-top: 12px;
            flex-wrap: wrap;
        }
        .btn {
            border: 0;
            border-radius: 8px;
            padding: 10px 14px;
            cursor: pointer;
            font-weight: 600;
            color: #fff;
            background: var(--primary);
            text-decoration: none;
            display: inline-block;
        }
        .btn.secondary { background: #5f6d7d; }
        .msg {
            border-radius: 8px;
            padding: 10px 12px;
            margin-bottom: 12px;
            font-weight: 600;
            font-size: 14px;
        }
        .msg.success { background: #e8f7ef; color: var(--success); border: 1px solid #bce8d0; }
        .msg.error { background: #fff1f2; color: var(--error); border: 1px solid #fecdd3; }
        .msg.info { background: #eef5ff; color: var(--primary); border: 1px solid #c8d7ea; }
        .debug-details {
            border: 1px solid #fecdd3;
            border-radius: 8px;
            margin: -4px 0 12px;
            background: #fff7f8;
        }
        .debug-details summary {
            cursor: pointer;
            padding: 10px 12px;
            font-weight: 700;
            color: var(--error);
        }
        .debug-details table {
            border-top: 1px solid #fecdd3;
        }
        .debug-details th {
            width: 220px;
            text-transform: none;
            vertical-align: top;
        }
        .debug-details td {
            font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
            white-space: pre-wrap;
            overflow-wrap: anywhere;
        }
        .path {
            font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
            font-size: 13px;
            background: #eef5ff;
            border: 1px solid #d7e4f7;
            border-radius: 8px;
            padding: 8px 10px;
            margin-bottom: 10px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
        }
        th, td {
            border-bottom: 1px solid var(--border);
            text-align: left;
            padding: 10px 8px;
            font-size: 14px;
        }
        th { font-size: 12px; text-transform: uppercase; color: #5c6f88; }
        .muted { color: #607087; font-size: 13px; }
        .preview-grid {
            display: grid;
            grid-template-columns: repeat(5, minmax(130px, 1fr));
            gap: 10px;
            margin: 12px 0;
        }
        .metric {
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 10px;
            background: #fbfdff;
        }
        .metric-label {
            color: #607087;
            font-size: 12px;
            margin-bottom: 4px;
        }
        .metric-value {
            font-size: 18px;
            font-weight: 800;
        }
        .metric-value.total { color: var(--success); }
        .preview-panel {
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 14px;
            background: #ffffff;
            margin: 12px 0;
        }
        .meta-grid {
            display: grid;
            grid-template-columns: repeat(4, minmax(0, 1fr));
            gap: 8px 14px;
            margin: 12px 0;
        }
        .meta-item { min-width: 0; font-size: 13px; }
        .meta-name { color: #607087; display: block; margin-bottom: 2px; }
        .meta-value {
            font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
            overflow-wrap: anywhere;
        }
        .table-wrap {
            overflow: auto;
            max-height: 420px;
            border: 1px solid var(--border);
            border-radius: 8px;
            margin-top: 10px;
        }
        .num { text-align: right; white-space: nowrap; }
        .warn { color: var(--error); }
        @media (max-width: 760px) {
            .grid { grid-template-columns: 1fr; }
            .upload-fields { grid-template-columns: 1fr; }
            .preview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
            .meta-grid { grid-template-columns: 1fr; }
            th:nth-child(3), td:nth-child(3) { display: none; }
        }
    </style>
</head>
<body>
<div class="container">
    <div class="card">
        <h1>Conexao SFTP Nuclea</h1>

        <?php if ($message !== ''): ?>
            <div class="msg <?= in_array($messageType, ['success', 'error', 'info'], true) ? h($messageType) : 'info' ?>"><?= h($message) ?></div>
        <?php endif; ?>
        <?php if ($messageType === 'error' && count($messageDetails) > 0): ?>
            <details class="debug-details" open>
                <summary>Detalhes tecnicos do erro</summary>
                <table>
                    <tbody>
                    <?php foreach ($messageDetails as $detailName => $detailValue): ?>
                        <tr>
                            <th><?= h((string)$detailName) ?></th>
                            <td><?= h((string)$detailValue) ?></td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </details>
        <?php endif; ?>

        <p class="muted">Host: <strong><?= h($config['host']) ?></strong></p>
        <p class="muted">Porta: <strong><?= h((string)$config['port']) ?></strong></p>

        <div class="actions">
            <button class="btn" type="button" onclick="requestPassphrase()">Informar senha</button>
            <form method="post" style="display:inline;">
                <input type="hidden" name="action" value="clear_passphrase">
                <button class="btn secondary" type="submit">Limpar senha</button>
            </form>
            <?php if ($connected): ?>
                <a class="btn secondary" href="?logout=1">Desconectar</a>
            <?php endif; ?>
        </div>

    </div>

    <?php if ($connected): ?>
        <div class="card">
            <div class="path">Diretorio atual: <?= h($path) ?></div>
            <form method="post" enctype="multipart/form-data" style="margin-bottom:10px;">
                <input type="hidden" name="action" value="upload_preview">
                <input type="hidden" name="path" value="<?= h($path) ?>">
                <input type="hidden" name="sort" value="<?= h($sort) ?>">
                <input type="hidden" name="dir" value="<?= h($dir) ?>">
                <div class="actions" style="margin-top:0; margin-bottom: 10px;">
                    <div class="upload-fields">
                        <div>
                            <label for="upload_encrypted">Arquivo criptografado</label>
                            <input id="upload_encrypted" type="file" name="upload_encrypted" required>
                        </div>
                        <div>
                            <label for="upload_xml">XML correspondente</label>
                            <input id="upload_xml" type="file" name="upload_xml" required>
                        </div>
                    </div>
                    <button class="btn" type="submit">Analisar XML e preparar upload</button>
                </div>
            </form>

            <?php if ($uploadPreview !== null && isset($uploadPreview['analysis'])): ?>
                <?php
                    $analysis = $uploadPreview['analysis'];
                    $meta = $analysis['meta'];
                    $resumo = $analysis['resumo'];
                ?>
                <div class="preview-panel">
                    <h1>Previa do arquivo</h1>
                    <p class="muted">
                        Arquivo: <strong><?= h($uploadPreview['original_name']) ?></strong><br>
                        XML: <strong><?= h($uploadPreview['xml_original_name'] ?? basename((string)$uploadPreview['xml_path'])) ?></strong><br>
                        Criptografado salvo em: <?= h($uploadPreview['encrypted_path']) ?><br>
                        XML salvo em: <?= h($uploadPreview['xml_path']) ?>
                    </p>
                    <div class="preview-grid">
                        <div class="metric">
                            <div class="metric-label">Total a pagar</div>
                            <div class="metric-value total">R$ <?= h(dinheiro((float)$resumo['total_geral'])) ?></div>
                        </div>
                        <div class="metric">
                            <div class="metric-label">CNPJs</div>
                            <div class="metric-value"><?= h((string)$resumo['qtd_cnpj']) ?></div>
                        </div>
                        <div class="metric">
                            <div class="metric-label">Pontos de venda</div>
                            <div class="metric-value"><?= h((string)$resumo['qtd_pontos']) ?></div>
                        </div>
                        <div class="metric">
                            <div class="metric-label">Centralizadoras</div>
                            <div class="metric-value"><?= h((string)$resumo['qtd_centralizadoras']) ?></div>
                        </div>
                        <div class="metric">
                            <div class="metric-label">Controles duplicados</div>
                            <div class="metric-value <?= ((int)$resumo['qtd_controles_duplicados'] > 0) ? 'warn' : '' ?>"><?= h((string)$resumo['qtd_controles_duplicados']) ?></div>
                        </div>
                    </div>

                    <div class="meta-grid">
                        <?php foreach ([
                            'NomArq' => $meta['nom_arq'],
                            'NumCtrlEmis' => $meta['num_ctrl_emis'],
                            'DtRef' => $meta['dt_ref'],
                            'DtHrArq' => $meta['dt_hr_arq'],
                            'ISPBEmissor' => $meta['ispb_emissor'],
                            'ISPBDestinatario' => $meta['ispb_destinatario'],
                            'CNPJCreddr' => $meta['cnpj_creddr'],
                            'ISPBIFDevdr' => $meta['ispb_if_devedor'],
                            'ISPBIFCredr' => $meta['ispb_if_credor'],
                            'Datas de pagamento' => implode(', ', $resumo['datas_pagamento']),
                            'Controles unicos' => $resumo['qtd_controles'],
                            'Campos com CodErro' => $resumo['qtd_cod_erro'],
                        ] as $nome => $valor): ?>
                            <div class="meta-item">
                                <span class="meta-name"><?= h($nome) ?></span>
                                <span class="meta-value"><?= h((string)($valor === '' ? '-' : $valor)) ?></span>
                            </div>
                        <?php endforeach; ?>
                    </div>

                    <div class="table-wrap">
                        <table>
                            <thead>
                            <tr>
                                <th>CNPJ/CPF</th>
                                <th>Nome</th>
                                <th>Arranjos</th>
                                <th>CodPontoVenda</th>
                                <th>ISPB Liquidacao</th>
                                <th class="num">Qtd</th>
                                <th class="num">Total</th>
                            </tr>
                            </thead>
                            <tbody>
                            <?php if (count($analysis['por_cnpj']) === 0): ?>
                                <tr><td colspan="7">Nenhum ponto de venda encontrado no XML.</td></tr>
                            <?php else: ?>
                                <?php foreach ($analysis['por_cnpj'] as $linha): ?>
                                    <tr>
                                        <td><?= h($linha['cnpj']) ?></td>
                                        <td><?= h($linha['nome']) ?></td>
                                        <td><?= h(implode(', ', array_keys($linha['arranjos']))) ?></td>
                                        <td><?= h(implode(', ', array_keys($linha['cod_pontos']))) ?></td>
                                        <td><?= h(implode(', ', array_keys($linha['ispbs']))) ?></td>
                                        <td class="num"><?= h((string)$linha['qtd']) ?></td>
                                        <td class="num">R$ <?= h(dinheiro((float)$linha['total'])) ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                    <div class="table-wrap">
                        <table>
                            <thead>
                            <tr>
                                <th>CNPJ/CPF</th>
                                <th>Nome</th>
                                <th>Arranjo</th>
                                <th>Datas</th>
                                <th>Tipo valor</th>
                                <th class="num">Qtd</th>
                                <th class="num">Total</th>
                            </tr>
                            </thead>
                            <tbody>
                            <?php if (count($analysis['por_arranjo']) === 0): ?>
                                <tr><td colspan="7">Nenhum detalhe por arranjo encontrado.</td></tr>
                            <?php else: ?>
                                <?php foreach ($analysis['por_arranjo'] as $linha): ?>
                                    <tr>
                                        <td><?= h($linha['cnpj']) ?></td>
                                        <td><?= h($linha['nome']) ?></td>
                                        <td><?= h($linha['arranjo']) ?></td>
                                        <td><?= h(implode(', ', array_keys($linha['datas']))) ?></td>
                                        <td><?= h(implode(', ', array_keys($linha['tipo_valor']))) ?></td>
                                        <td class="num"><?= h((string)$linha['qtd']) ?></td>
                                        <td class="num">R$ <?= h(dinheiro((float)$linha['total'])) ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                    <div class="actions">
                        <form method="post">
                            <input type="hidden" name="action" value="confirm_upload">
                            <input type="hidden" name="token" value="<?= h($uploadPreview['token']) ?>">
                            <input type="hidden" name="path" value="<?= h($uploadPreview['path']) ?>">
                            <input type="hidden" name="sort" value="<?= h($sort) ?>">
                            <input type="hidden" name="dir" value="<?= h($dir) ?>">
                            <button class="btn" type="submit">Confirmar upload</button>
                        </form>
                        <form method="post">
                            <input type="hidden" name="action" value="cancel_upload">
                            <input type="hidden" name="path" value="<?= h($path) ?>">
                            <button class="btn secondary" type="submit">Cancelar</button>
                        </form>
                    </div>
                </div>
            <?php endif; ?>

            <div class="actions" style="margin-top:0; margin-bottom: 10px;">
                <a class="btn secondary" href="?path=<?= rawurlencode(parentRemotePath($path)) ?>&sort=<?= h($sort) ?>&dir=<?= h($dir) ?>">Subir pasta</a>
                <a class="btn secondary" href="?path=%2F&sort=<?= h($sort) ?>&dir=<?= h($dir) ?>">Ir para /</a>
            </div>
            <table>
                <thead>
                <tr>
                    <?php
                    $nameDir = ($sort === 'name' && strtolower($dir) === 'asc') ? 'desc' : 'asc';
                    $sizeDir = ($sort === 'size' && strtolower($dir) === 'asc') ? 'desc' : 'asc';
                    $mtimeDir = ($sort === 'mtime' && strtolower($dir) === 'asc') ? 'desc' : 'asc';
                    ?>
                    <th><a href="?path=<?= rawurlencode($path) ?>&sort=name&dir=<?= h($nameDir) ?>">Nome</a></th>
                    <th><a href="?path=<?= rawurlencode($path) ?>&sort=size&dir=<?= h($sizeDir) ?>">Tamanho</a></th>
                    <th><a href="?path=<?= rawurlencode($path) ?>&sort=mtime&dir=<?= h($mtimeDir) ?>">Modificado em</a></th>
                    <th>Acao</th>
                </tr>
                </thead>
                <tbody>
                <?php if (count($items) === 0): ?>
                    <tr><td colspan="4">Diretorio vazio.</td></tr>
                <?php else: ?>
                    <?php foreach ($items as $item): ?>
                        <tr>
                            <td>
                                <?php if ($item['is_dir']): ?>
                                    <a href="?path=<?= rawurlencode($item['path']) ?>&sort=<?= h($sort) ?>&dir=<?= h($dir) ?>">[DIR] <?= h($item['name']) ?></a>
                                <?php else: ?>
                                    <?= h($item['name']) ?>
                                <?php endif; ?>
                            </td>
                            <td><?= $item['is_dir'] ? '-' : h(formatBytes($item['size'])) ?></td>
                            <td><?= h(formatDate($item['mtime'])) ?></td>
                            <td>
                                <?php if (!$item['is_dir']): ?>
                                    <a href="?path=<?= rawurlencode($path) ?>&sort=<?= h($sort) ?>&dir=<?= h($dir) ?>&download=<?= rawurlencode($item['path']) ?>">Download</a>
                                <?php else: ?>
                                    -
                                <?php endif; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                <?php endif; ?>
                </tbody>
            </table>
        </div>
    <?php endif; ?>
</div>
<form id="passphrase-form" method="post" style="display:none;">
    <input type="hidden" name="action" value="set_passphrase">
    <input type="hidden" name="path" value="<?= h($path) ?>">
    <input type="hidden" name="passphrase" id="passphrase-value" value="">
</form>
<script>
function requestPassphrase() {
    var value = window.prompt('Digite a senha/passphrase da chave SFTP:', '');
    if (value === null) {
        return;
    }
    document.getElementById('passphrase-value').value = value;
    document.getElementById('passphrase-form').submit();
}
<?php if ($shouldPromptPassphrase): ?>
requestPassphrase();
<?php endif; ?>
</script>
</body>
</html>
