<?php
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/db.php';

/**
 * Helper para operações WebDAV no Nextcloud
 */
class Nextcloud {
    private $username;
    private $password;
    private $baseUrl;
    private $webdavUsers = [];
    private $activeWebdavUser;
    private $propfindAttempts = [];

    private function resolvePropfindDir($dir, $depth = 1) {
        $requested = trim((string)$dir, '/');
        $depth = (int)$depth;
        if ($depth <= 0) {
            $depth = 1;
        }

        $result = $this->executePropfindBest($requested, $depth);
        if (($result['http_code'] ?? 0) === 207 || $requested === '') {
            return [
                'requested' => $requested,
                'resolved' => $requested,
                'result' => $result,
            ];
        }

        if (($result['http_code'] ?? 0) !== 404 || $requested === '') {
            return [
                'requested' => $requested,
                'resolved' => $requested,
                'result' => $result,
            ];
        }

        $tryCandidates = [];
        if (strpos($requested, '/') === false) {
            $resolvedDir = $this->findDirectoryPathByName($requested);
            if ($resolvedDir === null || $resolvedDir === '') {
                $resolvedDir = $this->findDirectoryPathByNameDeep($requested, 5, 2500);
            }
            if (is_string($resolvedDir) && $resolvedDir !== '' && $resolvedDir !== $requested) {
                $tryCandidates[] = $resolvedDir;
            }
        } else {
            $parts = array_values(array_filter(explode('/', $requested), 'strlen'));
            while (count($parts) > 1) {
                array_shift($parts);
                $candidate = implode('/', $parts);
                if ($candidate !== '') {
                    $tryCandidates[] = $candidate;
                }
            }

            $lastSegment = '';
            if (!empty($parts)) {
                $lastSegment = trim((string)end($parts));
            } else {
                $pieces = array_values(array_filter(explode('/', $requested), 'strlen'));
                if (!empty($pieces)) {
                    $lastSegment = trim((string)end($pieces));
                }
            }

            if ($lastSegment !== '') {
                $resolvedDir = $this->findDirectoryPathByNameDeep($lastSegment, 5, 2500);
                if (is_string($resolvedDir) && $resolvedDir !== '' && $resolvedDir !== $requested) {
                    $tryCandidates[] = $resolvedDir;
                }
            }
        }

        foreach ($tryCandidates as $candidate) {
            $candidate = trim((string)$candidate, '/');
            if ($candidate === '' || $candidate === $requested) {
                continue;
            }
            $probe = $this->executePropfindBest($candidate, $depth);
            if (($probe['http_code'] ?? 0) === 207) {
                return [
                    'requested' => $requested,
                    'resolved' => $candidate,
                    'result' => $probe,
                ];
            }
        }

        return [
            'requested' => $requested,
            'resolved' => $requested,
            'result' => $result,
        ];
    }

    private function getConfiguredWebdavCredentials() {
        try {
            $pdo = get_db_connection();
            $stmt = $pdo->prepare("SELECT setting_key, setting_value FROM system_settings WHERE setting_key IN ('nc_webdav_user', 'nc_webdav_pass')");
            $stmt->execute();
            $rows = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);

            $configuredUser = trim((string)($rows['nc_webdav_user'] ?? ''));
            $configuredPass = (string)($rows['nc_webdav_pass'] ?? '');
            if ($configuredUser !== '' && $configuredPass !== '') {
                return [
                    'user' => $configuredUser,
                    'pass' => $configuredPass,
                ];
            }
        } catch (Throwable $e) {
        }

        return null;
    }

    public function __construct() {
        $configuredCredentials = $this->getConfiguredWebdavCredentials();
        if ($configuredCredentials) {
            $this->username = $configuredCredentials['user'];
            $this->password = $configuredCredentials['pass'];
        } else {
            if (!isset($_SESSION['username']) || !isset($_SESSION['nc_pass'])) {
                throw new Exception("Sessão Nextcloud não encontrada. Por favor, faça login novamente.");
            }
            $this->username = $_SESSION['username'];
            $this->password = $_SESSION['nc_pass'];
        }
        $candidateUsers = [
            (string)$this->username,
            (string)($_SESSION['nc_user_id'] ?? ''),
            (string)($_SESSION['username'] ?? ''),
        ];
        foreach ($candidateUsers as $candidateUser) {
            $candidateUser = trim($candidateUser);
            if ($candidateUser !== '' && !in_array($candidateUser, $this->webdavUsers, true)) {
                $this->webdavUsers[] = $candidateUser;
            }
        }
        if (empty($this->webdavUsers)) {
            throw new Exception("Usuario WebDAV do Nextcloud nao identificado.");
        }

        $this->activeWebdavUser = $this->webdavUsers[0];
        // URL base do WebDAV: https://servidor/remote.php/dav/files/USUARIO/
        $this->baseUrl = rtrim(NC_WEBDAV_URL, '/') . '/' . rawurlencode($this->activeWebdavUser) . '/';
    }

    /**
     * Monta a URL WebDAV codificando cada segmento do caminho.
     */
    private function buildUrl($path = '', $webdavUser = null) {
        $webdavUser = trim((string)($webdavUser ?: $this->activeWebdavUser));
        $path = trim((string)$path, '/');
        $baseUrl = rtrim(NC_WEBDAV_URL, '/') . '/' . rawurlencode($webdavUser) . '/';
        if ($path === '') {
            return $baseUrl;
        }

        $segments = array_map('rawurlencode', explode('/', $path));
        return $baseUrl . implode('/', $segments);
    }

    /**
     * Executa um PROPFIND e devolve a resposta bruta e o codigo HTTP.
     */
    private function executePropfind($path = '', $depth = 1, $webdavUser = null) {
        $normalizedPath = trim((string)$path, '/');
        $effectiveUser = trim((string)($webdavUser ?: $this->activeWebdavUser));
        $url = $this->buildUrl($normalizedPath, $effectiveUser);
        if ($normalizedPath !== '') {
            $url .= '/';
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PROPFIND');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Depth: ' . $depth, 'Content-Type: text/xml']);

        $xml_request = '<?xml version="1.0" encoding="utf-8" ?>
            <d:propfind xmlns:d="DAV:">
                <d:prop>
                    <d:displayname/>
                    <d:getlastmodified/>
                    <d:getcontentlength/>
                    <d:resourcetype/>
                </d:prop>
            </d:propfind>';
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_request);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'path' => $normalizedPath,
            'url' => $url,
            'webdav_user' => $effectiveUser,
            'http_code' => $httpCode,
            'response' => $response,
        ];
    }

    private function executePropfindBest($path = '', $depth = 1) {
        $bestResult = null;
        $bestEntryCount = -1;

        foreach ($this->webdavUsers as $webdavUser) {
            $result = $this->executePropfind($path, $depth, $webdavUser);
            $attemptKey = trim((string)$path, '/') . '|depth:' . $depth . '|user:' . $webdavUser;
            $attemptEntries = [];
            $attemptEntryCount = 0;
            if (($result['http_code'] ?? 0) === 207) {
                $attemptEntries = $this->parsePropfindEntries($result['response'], $result['url']);
                $attemptEntryCount = count($attemptEntries);
            }
            $this->propfindAttempts[$attemptKey] = [
                'path' => trim((string)$path, '/'),
                'depth' => (string)$depth,
                'webdav_user' => $webdavUser,
                'http_code' => $result['http_code'] ?? null,
                'entry_count' => $attemptEntryCount,
            ];

            if ($bestResult === null) {
                $bestResult = $result;
            }

            if (($result['http_code'] ?? 0) !== 207) {
                continue;
            }

            if ($attemptEntryCount > $bestEntryCount) {
                $bestEntryCount = $attemptEntryCount;
                $bestResult = $result;
            }
        }

        if (isset($bestResult['webdav_user']) && $bestResult['webdav_user'] !== '') {
            $this->activeWebdavUser = $bestResult['webdav_user'];
            $this->baseUrl = rtrim(NC_WEBDAV_URL, '/') . '/' . rawurlencode($this->activeWebdavUser) . '/';
        }

        return $bestResult ?: $this->executePropfind($path, $depth);
    }

    /**
     * Converte a resposta WebDAV em entradas utilizaveis.
     */
    private function parsePropfindEntries($response, $requestUrl) {
        $entries = [];
        $requestPath = urldecode((string)parse_url($requestUrl, PHP_URL_PATH));

        try {
            $xml = new SimpleXMLElement($response);
            $xml->registerXPathNamespace('d', 'DAV:');
            $responses = $xml->xpath('//d:response');
            if (!is_array($responses)) {
                return [];
            }

            foreach ($responses as $res) {
                $hrefNodes = $res->xpath('d:href');
                $propstatNodes = $res->xpath('d:propstat');
                if (!is_array($hrefNodes) || empty($hrefNodes) || !is_array($propstatNodes) || empty($propstatNodes)) {
                    continue;
                }

                $href = (string)$hrefNodes[0];
                $propstat = null;
                foreach ($propstatNodes as $candidatePropstat) {
                    $statusNodes = $candidatePropstat->xpath('d:status');
                    $statusText = is_array($statusNodes) && isset($statusNodes[0]) ? (string)$statusNodes[0] : '';
                    if ($statusText !== '' && preg_match('/\\s200\\s/i', $statusText)) {
                        $propstat = $candidatePropstat;
                        break;
                    }
                }
                if ($propstat === null) {
                    $propstat = $propstatNodes[0];
                }

                $propNodes = $propstat->xpath('d:prop');
                if (!is_array($propNodes) || empty($propNodes)) {
                    continue;
                }
                $prop = $propNodes[0];
                $hrefPath = urldecode((string)parse_url($href, PHP_URL_PATH));
                if ($hrefPath === '') {
                    $hrefPath = urldecode($href);
                }

                if (rtrim($hrefPath, '/') === rtrim($requestPath, '/')) {
                    continue;
                }

                $collectionNodes = $prop->xpath('d:resourcetype/d:collection');
                $displayNameNodes = $prop->xpath('d:displayname');
                $sizeNodes = $prop->xpath('d:getcontentlength');
                $mtimeNodes = $prop->xpath('d:getlastmodified');

                $isDir = is_array($collectionNodes) && count($collectionNodes) > 0;
                $displayName = is_array($displayNameNodes) && isset($displayNameNodes[0]) ? trim((string)$displayNameNodes[0]) : '';
                $filename = basename(urldecode($href));
                if ($displayName === '') {
                    $displayName = $filename;
                }

                $entries[] = [
                    'href' => $href,
                    'name' => $filename,
                    'display_name' => $displayName,
                    'relative_path' => $this->getRelativePathFromHref($href),
                    'is_dir' => $isDir,
                    'size' => is_array($sizeNodes) && isset($sizeNodes[0]) ? (int)$sizeNodes[0] : 0,
                    'mtime' => is_array($mtimeNodes) && isset($mtimeNodes[0]) ? strtotime((string)$mtimeNodes[0]) : 0,
                ];
            }
        } catch (Exception $e) {
            return [];
        }

        return $entries;
    }

    /**
     * Extrai o caminho relativo ao usuario a partir do href devolvido pelo WebDAV.
     */
    private function getRelativePathFromHref($href) {
        $basePath = parse_url($this->baseUrl, PHP_URL_PATH);
        $hrefPath = parse_url((string)$href, PHP_URL_PATH);
        if ($hrefPath === null || $hrefPath === false || $hrefPath === '') {
            $hrefPath = (string)$href;
        }

        $decodedBasePath = urldecode((string)$basePath);
        $decodedHrefPath = urldecode((string)$hrefPath);

        if (strpos($decodedHrefPath, $decodedBasePath) === 0) {
            return trim(substr($decodedHrefPath, strlen($decodedBasePath)), '/');
        }

        return trim($decodedHrefPath, '/');
    }

    /**
     * Procura uma pasta pelo nome na raiz do usuario.
     */
    public function findDirectoryPathByName($folderName) {
        $folderName = trim((string)$folderName);
        if ($folderName === '') {
            return '';
        }

        $result = $this->executePropfindBest('', 1);
        if ($result['http_code'] !== 207) {
            return null;
        }

        $entries = $this->parsePropfindEntries($result['response'], $result['url']);
        foreach ($entries as $entry) {
            if (!$entry['is_dir']) {
                continue;
            }

            if (strcasecmp($entry['display_name'], $folderName) === 0 || strcasecmp($entry['name'], $folderName) === 0) {
                return $this->getRelativePathFromHref($entry['href']);
            }
        }

        return null;
    }

    public function findDirectoryPathByNameDeep($folderName, $maxDepth = 4, $maxNodes = 2000) {
        $folderName = trim((string)$folderName);
        if ($folderName === '' || $maxDepth < 1 || $maxNodes < 1) {
            return null;
        }

        $queue = [
            [
                'path' => '',
                'depth' => 1,
            ]
        ];
        $visited = [];
        $scanned = 0;

        while (!empty($queue) && $scanned < $maxNodes) {
            $node = array_shift($queue);
            $basePath = trim((string)($node['path'] ?? ''), '/');
            $depth = (int)($node['depth'] ?? 1);

            $visitKey = strtolower($basePath);
            if (isset($visited[$visitKey])) {
                continue;
            }
            $visited[$visitKey] = true;

            $result = $this->executePropfindBest($basePath, 1);
            if (($result['http_code'] ?? 0) !== 207) {
                continue;
            }

            $entries = $this->parsePropfindEntries($result['response'], $result['url']);
            foreach ($entries as $entry) {
                if (empty($entry['is_dir'])) {
                    continue;
                }
                $name = (string)($entry['display_name'] ?: $entry['name']);
                if ($name !== '' && strcasecmp($name, $folderName) === 0) {
                    $found = trim((string)($entry['relative_path'] ?? ''), '/');
                    return $found !== '' ? $found : null;
                }
            }

            if ($depth >= $maxDepth) {
                continue;
            }

            foreach ($entries as $entry) {
                if (empty($entry['is_dir'])) {
                    continue;
                }

                $childPath = trim((string)($entry['relative_path'] ?? ''), '/');
                if ($childPath === '') {
                    continue;
                }

                $queue[] = [
                    'path' => $childPath,
                    'depth' => $depth + 1,
                ];
                $scanned++;
                if ($scanned >= $maxNodes) {
                    break;
                }
            }
        }

        return null;
    }

    /**
     * Lista diretorios imediatos da raiz do usuario.
     */
    public function listRootDirectories() {
        $result = $this->executePropfindBest('', 1);
        if ($result['http_code'] !== 207) {
            return [];
        }

        $entries = $this->parsePropfindEntries($result['response'], $result['url']);
        $directories = [];
        foreach ($entries as $entry) {
            if (!$entry['is_dir']) {
                continue;
            }

            $directories[] = [
                'name' => $entry['display_name'] ?: $entry['name'],
                'relative_path' => trim((string)($entry['relative_path'] ?? ''), '/'),
            ];
        }

        return $directories;
    }

    /**
     * Lista entradas imediatas de uma pasta para diagnostico.
     */
    public function listImmediateEntries($path = '') {
        $resolved = $this->resolvePropfindDir($path, 1);
        $normalizedDir = $resolved['resolved'];
        $requestedDir = $resolved['requested'];
        $result = $resolved['result'];
        if ($result['http_code'] !== 207) {
            return [
                'path' => $normalizedDir,
                'requested_path' => $requestedDir,
                'http_code' => $result['http_code'],
                'webdav_user' => $result['webdav_user'] ?? $this->activeWebdavUser,
                'entries' => [],
            ];
        }

        $entries = $this->parsePropfindEntries($result['response'], $result['url']);
        $items = [];
        foreach ($entries as $entry) {
            $items[] = [
                'name' => $entry['display_name'] ?: $entry['name'],
                'relative_path' => trim((string)($entry['relative_path'] ?? ''), '/'),
                'is_dir' => !empty($entry['is_dir']),
            ];
        }

        return [
            'path' => $normalizedDir,
            'requested_path' => $requestedDir,
            'http_code' => $result['http_code'],
            'webdav_user' => $result['webdav_user'] ?? $this->activeWebdavUser,
            'entries' => $items,
        ];
    }

    public function getActiveWebdavUser() {
        return $this->activeWebdavUser;
    }

    public function getPropfindAttempts() {
        return array_values($this->propfindAttempts);
    }

    private function isVisibleContractFile($filename) {
        $filename = trim((string)$filename);
        if ($filename === '') {
            return false;
        }

        $basename = basename($filename);
        if ($basename === '' || strpos($basename, '.') === 0 || strpos($basename, '~') === 0 || strpos($basename, '_') === 0) {
            return false;
        }

        return true;
    }

    private function extractUnitFolderFromRelativePath($relativePath, $rootDir) {
        $relativePath = trim(str_replace('\\', '/', (string)$relativePath), '/');
        $rootDir = trim(str_replace('\\', '/', (string)$rootDir), '/');
        if ($relativePath === '') {
            return '';
        }

        $tail = $relativePath;
        if ($rootDir !== '') {
            $prefix = $rootDir . '/';
            if (stripos($tail, $prefix) === 0) {
                $tail = substr($tail, strlen($prefix));
            } elseif (strcasecmp($tail, $rootDir) === 0) {
                $tail = '';
            }
        }

        $tail = trim((string)$tail, '/');
        if ($tail === '' || strpos($tail, '/') === false) {
            return '';
        }

        $parts = explode('/', $tail);
        return trim((string)($parts[0] ?? ''), '/');
    }

    private function appendPdfEntriesFromEntries(array $entries, $rootDir, array &$files, array &$seenPaths) {
        $rootDir = trim((string)$rootDir, '/');
        foreach ($entries as $entry) {
            $filename = $entry['name'];
            $relativePath = trim((string)($entry['relative_path'] ?? ''), '/');

            if (!empty($entry['is_dir'])) {
                continue;
            }

            if (!$this->isVisibleContractFile($filename)) {
                continue;
            }

            $remotePath = $relativePath !== '' ? $relativePath : (($rootDir !== '' ? $rootDir . '/' : '') . $filename);
            $remoteKey = strtolower($remotePath);
            if (isset($seenPaths[$remoteKey])) {
                continue;
            }
            $seenPaths[$remoteKey] = true;

            $unitFolder = $this->extractUnitFolderFromRelativePath($remotePath, $rootDir);

            $files[] = [
                'name' => $filename,
                'remote_path' => $remotePath,
                'href' => $entry['href'],
                'size' => $entry['size'],
                'mtime' => $entry['mtime'],
                'unit_folder' => $unitFolder
            ];
        }
    }

    public function listEntriesRecursive($remoteDir = 'CONTRATOS', $maxEntries = 100, array &$visited = []) {
        $normalizedDir = trim((string)$remoteDir, '/');
        if ($normalizedDir === '' || isset($visited[$normalizedDir]) || $maxEntries <= 0) {
            return [];
        }
        $visited[$normalizedDir] = true;

        $result = $this->executePropfindBest($normalizedDir, 1);
        if (($result['http_code'] ?? 0) !== 207) {
            return [];
        }

        $entries = $this->parsePropfindEntries($result['response'], $result['url']);
        $items = [];

        foreach ($entries as $entry) {
            $items[] = [
                'name' => $entry['display_name'] ?: $entry['name'],
                'relative_path' => trim((string)($entry['relative_path'] ?? ''), '/'),
                'is_dir' => !empty($entry['is_dir']),
                'extension' => strtolower(pathinfo((string)$entry['name'], PATHINFO_EXTENSION)),
            ];

            if (count($items) >= $maxEntries) {
                return $items;
            }
        }

        foreach ($entries as $entry) {
            if (empty($entry['is_dir'])) {
                continue;
            }

            $childPath = trim((string)($entry['relative_path'] ?? ''), '/');
            if ($childPath === '') {
                continue;
            }

            $remaining = $maxEntries - count($items);
            if ($remaining <= 0) {
                break;
            }

            $items = array_merge($items, $this->listEntriesRecursive($childPath, $remaining, $visited));
            if (count($items) >= $maxEntries) {
                break;
            }
        }

        return $items;
    }

    private function listFilesRecursiveDepthOne($remoteDir, $rootDir, array &$visited = [], array &$seenPaths = []) {
        $normalizedDir = trim((string)$remoteDir, '/');
        if (isset($visited[$normalizedDir])) {
            return [];
        }
        $visited[$normalizedDir] = true;

        $result = $this->executePropfindBest($normalizedDir, 1);
        if (($result['http_code'] ?? 0) !== 207) {
            return [];
        }

        $entries = $this->parsePropfindEntries($result['response'], $result['url']);
        $files = [];
        $this->appendPdfEntriesFromEntries($entries, $rootDir, $files, $seenPaths);

        foreach ($entries as $entry) {
            if (empty($entry['is_dir'])) {
                continue;
            }

            $childPath = trim((string)($entry['relative_path'] ?? ''), '/');
            if ($childPath === '') {
                continue;
            }

            $files = array_merge($files, $this->listFilesRecursiveDepthOne($childPath, $rootDir, $visited, $seenPaths));
        }

        return $files;
    }

    private function listFilesRecursiveBfs($rootDir, $maxFiles = 12000, $maxDirs = 4000) {
        $rootDir = trim((string)$rootDir, '/');
        $maxFiles = (int)$maxFiles;
        $maxDirs = (int)$maxDirs;
        if ($maxFiles <= 0) {
            $maxFiles = 12000;
        }
        if ($maxDirs <= 0) {
            $maxDirs = 4000;
        }

        $files = [];
        $visited = [];
        $seenPaths = [];
        $queue = [$rootDir];
        $idx = 0;
        $dirsScanned = 0;

        while ($idx < count($queue) && count($files) < $maxFiles && $dirsScanned < $maxDirs) {
            $currentDir = (string)$queue[$idx];
            $idx++;

            $normalizedDir = trim($currentDir, '/');
            if (isset($visited[$normalizedDir])) {
                continue;
            }
            $visited[$normalizedDir] = true;
            $dirsScanned++;

            $result = $this->executePropfindBest($normalizedDir, 1);
            if (($result['http_code'] ?? 0) !== 207) {
                continue;
            }

            $entries = $this->parsePropfindEntries($result['response'], $result['url']);
            $this->appendPdfEntriesFromEntries($entries, $rootDir, $files, $seenPaths);

            foreach ($entries as $entry) {
                if (empty($entry['is_dir'])) {
                    continue;
                }
                $childPath = trim((string)($entry['relative_path'] ?? ''), '/');
                if ($childPath === '') {
                    continue;
                }
                if (!isset($visited[$childPath])) {
                    $queue[] = $childPath;
                }
            }
        }

        return $files;
    }

    /**
     * Cria uma pasta no Nextcloud
     */
    public function createFolder($path) {
        $url = $this->buildUrl($path);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'MKCOL');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return ($httpCode === 201 || $httpCode === 405); // 201 Criado, 405 Já existe
    }

    /**
     * Faz upload de um arquivo para o Nextcloud
     */
    public function uploadFile($localFilePath, $remotePath) {
        $url = $this->buildUrl($remotePath);

        $fileHandle = fopen($localFilePath, 'r');
        $fileSize = filesize($localFilePath);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_PUT, true);
        curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
        curl_setopt($ch, CURLOPT_INFILESIZE, $fileSize);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        fclose($fileHandle);

        return ($httpCode === 201 || $httpCode === 204); // 201 Criado, 204 Atualizado
    }

    /**
     * Lista arquivos em uma pasta remota (Limitado a 1 nível de subpastas para performance)
     */
    public function listFiles($remoteDir = 'CONTRATOS', $recursive = true, $maxFiles = 12000, $maxDirs = 4000) {
        $resolved = $this->resolvePropfindDir($remoteDir, 1);
        $normalizedDir = $resolved['resolved'];
        $requestedDir = $resolved['requested'];
        $result = $resolved['result'];

        if ($result['http_code'] !== 207) {
            $detail = '';
            if (is_string($result['response']) && stripos($result['response'], 'Acesso Restrito') !== false) {
                $detail = ' Resposta indica tela de login em vez de WebDAV.';
            }
            $resolvedDetail = ($requestedDir !== '' && $normalizedDir !== '' && $requestedDir !== $normalizedDir)
                ? " (resolvido para '{$normalizedDir}')"
                : '';
            throw new Exception("Falha ao listar arquivos no Nextcloud na pasta '{$requestedDir}'{$resolvedDetail} (HTTP {$result['http_code']}).{$detail}");
        }

        if ($recursive) {
            return $this->listFilesRecursiveBfs($normalizedDir, $maxFiles, $maxDirs);
        }

        $files = [];
        $entries = $this->parsePropfindEntries($result['response'], $result['url']);

        $seenPaths = [];
        $this->appendPdfEntriesFromEntries($entries, $normalizedDir, $files, $seenPaths);

        return $files;
    }

    /**
     * Baixa um arquivo do Nextcloud para o servidor local
     */
    public function downloadFile($remotePath, $localDestPath) {
        $url = $this->buildUrl($remotePath);

        $fp = fopen($localDestPath, 'w+');
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_FILE, $fp);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        fclose($fp);

        return ($httpCode === 200);
    }

    /**
     * Deleta um arquivo no Nextcloud
     */
    public function deleteFile($remotePath) {
        $url = $this->buildUrl($remotePath);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return ($httpCode === 204); // 204 No Content (Sucesso)
    }

    /**
     * Move um arquivo no Nextcloud
     */
    public function moveFile($sourcePath, $destPath) {
        $url = $this->buildUrl($sourcePath);
        
        $destUrl = $this->buildUrl($destPath);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'MOVE');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERPWD, $this->username . ":" . $this->password);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ["Destination: $destUrl", "Overwrite: T"]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return ($httpCode === 201 || $httpCode === 204);
    }
}
