<?php
declare(strict_types=1);

namespace App\Services;

use App\Core\Auth;

final class DocumentStore
{
    public const MAX_BYTES = 8388608;

    public static function root(): string
    {
        return str_replace('\\', '/', dirname(BASE_PATH)) . '/storage/docs';
    }

    public static function path(int $tenantId, int $documentId, bool $signed = false): string
    {
        $suffix = $signed ? '-signed' : '';
        return self::root() . '/' . $tenantId . '/' . $documentId . $suffix . '.pdf';
    }

    /** @param array<string, mixed> $file */
    public static function accept(array $file): string
    {
        $err = (int) ($file['error'] ?? UPLOAD_ERR_NO_FILE);
        if ($err === UPLOAD_ERR_NO_FILE) {
            throw new \RuntimeException('Upload the signed PDF. It is required.');
        }
        if ($err !== UPLOAD_ERR_OK) {
            throw new \RuntimeException('The PDF did not upload. Try a smaller file.');
        }
        $tmp = (string) ($file['tmp_name'] ?? '');
        $name = self::safeName((string) ($file['name'] ?? 'signed.pdf'));
        $size = (int) ($file['size'] ?? 0);
        if ($tmp === '' || !is_uploaded_file($tmp)) {
            throw new \RuntimeException('The PDF did not upload.');
        }
        if ($size < 5 || $size > self::MAX_BYTES) {
            throw new \RuntimeException('Use a PDF up to 8 MB.');
        }
        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
        if ($ext !== 'pdf') {
            throw new \RuntimeException('Only PDF files can be uploaded.');
        }
        $mime = '';
        if (class_exists(\finfo::class)) {
            $mime = (string) (new \finfo(FILEINFO_MIME_TYPE))->file($tmp);
        }
        $head = (string) file_get_contents($tmp, false, null, 0, 5);
        if ($head !== '%PDF-' || ($mime !== '' && $mime !== 'application/pdf' && $mime !== 'application/x-pdf')) {
            throw new \RuntimeException('That file is not a PDF.');
        }
        return $name;
    }

    public static function save(int $documentId, string $tmpPath, bool $signed = false): void
    {
        $tid = (int) Auth::instance()->tenantId();
        $dir = self::root() . '/' . $tid;
        if (!is_dir($dir) && !mkdir($dir, 0750, true) && !is_dir($dir)) {
            throw new \RuntimeException('Could not store the PDF.');
        }
        $dest = self::path($tid, $documentId, $signed);
        if (!@copy($tmpPath, $dest) || !is_file($dest)) {
            throw new \RuntimeException('Could not store the PDF.');
        }
    }

    public static function exists(int $tenantId, int $documentId, bool $signed = false): bool
    {
        return is_file(self::path($tenantId, $documentId, $signed));
    }

    public static function stream(int $tenantId, int $documentId, string $downloadName, bool $signed = false): void
    {
        self::streamFile(self::path($tenantId, $documentId, $signed), $downloadName, $signed);
    }

    public static function entityPath(int $tenantId, string $entity, int $id): string
    {
        if (!in_array($entity, ['invoice', 'contract'], true)) {
            throw new \RuntimeException('Invalid file type.');
        }
        return self::root() . '/' . $tenantId . '/' . $entity . '-' . $id . '.pdf';
    }

    public static function existsEntity(int $tenantId, string $entity, int $id): bool
    {
        return is_file(self::entityPath($tenantId, $entity, $id));
    }

    public static function streamEntity(int $tenantId, string $entity, int $id, string $downloadName): void
    {
        self::streamFile(self::entityPath($tenantId, $entity, $id), $downloadName, false);
    }

    private static function streamFile(string $path, string $downloadName, bool $signed): void
    {
        if (!is_file($path)) {
            http_response_code(404);
            echo 'File not found.';
            return;
        }
        $name = self::safeName($downloadName !== '' ? $downloadName : ($signed ? 'signed.pdf' : 'document.pdf'));
        header('Content-Type: application/pdf');
        header('Content-Disposition: inline; filename="' . $name . '"');
        header('X-Content-Type-Options: nosniff');
        header('Content-Length: ' . (string) filesize($path));
        readfile($path);
    }

    public static function safeName(string $name): string
    {
        $base = basename(str_replace(["\0", '\\'], '', $name));
        $base = preg_replace('/[^A-Za-z0-9._-]+/', '-', $base) ?? 'document.pdf';
        $base = trim($base, '.-');
        if ($base === '' || !str_ends_with(strtolower($base), '.pdf')) {
            $base = ($base !== '' ? $base : 'document') . '.pdf';
        }
        return mb_substr($base, 0, 160);
    }
}
