<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\Response;
use App\Core\UrlHelper;
use App\Core\Env;

final class StorageController
{
    public function public(Request $request): void
    {
        $prefix = '/api/v1/storage/public/';
        $path = $this->cleanRelativePath(substr($request->path, strlen($prefix)));

        if ($path === '') {
            Response::error('Arquivo não informado', 404);
            return;
        }

        $candidates = [];
        $candidates[] = $path;
        if (str_starts_with($path, 'uploads/identidade_visual/')) {
            // Espelho público para URL direta /uploads/identidade_visual/...
            $candidates[] = 'public/' . $path;
        }

        if (str_starts_with($path, 'branding-assets/')) {
            $candidates[] = 'uploads/identidade_visual/' . substr($path, strlen('branding-assets/'));
            $candidates[] = 'storage/uploads/' . $path;
        }

        if (str_starts_with($path, 'identidade_visual/')) {
            $candidates[] = 'uploads/' . $path;
        }

        if (str_starts_with($path, 'uploads/')) {
            $candidates[] = $path;
        } else {
            $candidates[] = 'uploads/' . $path;
        }

        $file = null;
        $usedPath = null;
        foreach (array_unique($candidates) as $candidate) {
            $candidateFile = $this->projectRoot() . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $candidate);
            if (is_file($candidateFile)) {
                $file = $candidateFile;
                $usedPath = $candidate;
                break;
            }
        }

        if (!$file) {
            Response::error('Arquivo não encontrado', 404, [
                'path' => $path,
                'candidates' => array_values(array_unique($candidates)),
                'project_root' => $this->projectRoot(),
            ]);
            return;
        }

        $mime = $this->mime($file);
        $this->sendCorsHeaders();
        header('Content-Type: ' . $mime);
        header('Content-Length: ' . (string) filesize($file));
        header('Cache-Control: public, max-age=86400');
        header('X-Sisprom-Asset-Path: ' . $usedPath);
        readfile($file);
    }

    public function upload(Request $request): void
    {
        Auth::requireUser($request);

        $bucket = $request->path;
        $bucket = preg_replace('#^/api/v1/storage/#', '', $bucket) ?? '';
        $bucket = preg_replace('#/upload$#', '', $bucket) ?? '';
        $bucket = $this->cleanSegment($bucket ?: 'public');

        $targetPath = (string)($request->query['path'] ?? '');
        $targetPath = $targetPath !== '' ? $this->cleanRelativePath($targetPath) : '';

        if (empty($_FILES['file'])) {
            Response::error('Arquivo não enviado', 422);
            return;
        }

        $uploadError = (int)($_FILES['file']['error'] ?? UPLOAD_ERR_OK);
        if ($uploadError !== UPLOAD_ERR_OK) {
            Response::error('Falha no upload do arquivo', 422, [
                'upload_error' => $uploadError,
                'message' => $this->uploadErrorMessage($uploadError),
            ]);
            return;
        }

        if (!is_uploaded_file($_FILES['file']['tmp_name'])) {
            Response::error('Arquivo temporário inválido', 422);
            return;
        }

        $original = basename((string)($_FILES['file']['name'] ?? 'arquivo'));
        $originalExt = strtolower(pathinfo($original, PATHINFO_EXTENSION));
        $allowedImageExt = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'svg', 'ico'];
        $isIdentityUpload = in_array($bucket, ['branding-assets', 'identidade_visual'], true);

        if ($isIdentityUpload && !in_array($originalExt, $allowedImageExt, true)) {
            Response::error('Arquivo inválido para identidade visual. Envie PNG, JPG, WEBP, SVG, GIF ou ICO.', 422, [
                'extension' => $originalExt,
            ]);
            return;
        }

        if ($targetPath === '') {
            $safeName = uniqid('file-', true) . '-' . $this->cleanSegment($original);
            $targetPath = $isIdentityUpload
                ? 'uploads/identidade_visual/' . date('Y/m') . '/' . $safeName
                : 'uploads/' . $bucket . '/' . date('Y/m') . '/' . $safeName;
        } else {
            $targetPath = ltrim($targetPath, '/');
            if ($isIdentityUpload) {
                $targetPath = preg_replace('#^(branding-assets|identidade_visual|uploads/identidade_visual)/#', '', $targetPath) ?? $targetPath;
                $targetPath = 'uploads/identidade_visual/' . $targetPath;
            } else {
                $targetPath = 'uploads/' . $bucket . '/' . $targetPath;
            }
        }

        $targetPath = $this->cleanRelativePath($targetPath);
        if (!str_starts_with($targetPath, 'uploads/')) {
            Response::error('Caminho de upload inválido', 422, ['path' => $targetPath]);
            return;
        }

        $dest = $this->projectRoot() . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $targetPath);
        $dir = dirname($dest);
        if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
            Response::error('Não foi possível criar a pasta de destino do upload', 500, [
                'dir' => $dir,
            ]);
            return;
        }

        if (!move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
            Response::error('Não foi possível salvar o arquivo', 500, [
                'target' => $targetPath,
                'dir_writable' => is_writable($dir),
            ]);
            return;
        }

        @chmod($dest, 0664);

        // Para identidade visual, mantém também uma cópia pública em:
        // backend-php/public/uploads/identidade_visual/...
        // Assim a imagem abre por /uploads/identidade_visual/... mesmo sem router.php.
        if ($isIdentityUpload && str_starts_with($targetPath, 'uploads/identidade_visual/')) {
            $publicMirror = $this->projectRoot() . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $targetPath);
            $publicDir = dirname($publicMirror);
            if (!is_dir($publicDir)) {
                @mkdir($publicDir, 0775, true);
            }
            @copy($dest, $publicMirror);
            @chmod($publicMirror, 0664);
        }

        $publicUrl = UrlHelper::publicBaseUrl() . '/' . str_replace('\\', '/', $targetPath);

        Response::json([
            'path' => $targetPath,
            'storage_path' => $targetPath,
            'db_path' => $targetPath,
            'fullPath' => $targetPath,
            'bucket' => $bucket,
            'publicUrl' => $publicUrl,
            'data' => [
                'path' => $targetPath,
                'storage_path' => $targetPath,
                'db_path' => $targetPath,
                'fullPath' => $targetPath,
                'bucket' => $bucket,
                'publicUrl' => $publicUrl,
            ],
        ]);
    }


    private function sendCorsHeaders(): void
    {
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
        $allowed = array_filter(array_map('trim', explode(',', Env::get('CORS_ORIGINS', '') ?? '')));

        if ($origin !== '' && in_array($origin, $allowed, true)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Vary: Origin');
        }
    }
    private function projectRoot(): string
    {
        return dirname(__DIR__, 2);
    }

    private function cleanRelativePath(string $path): string
    {
        $path = urldecode($path);
        $path = str_replace('\\', '/', $path);
        $path = preg_replace('#/+#', '/', $path) ?? '';
        $path = ltrim($path, '/');
        $parts = [];
        foreach (explode('/', $path) as $part) {
            if ($part === '' || $part === '.' || $part === '..') continue;
            $parts[] = $this->cleanSegment($part);
        }
        return implode('/', $parts);
    }

    private function cleanSegment(string $segment): string
    {
        $segment = trim($segment);
        $segment = preg_replace('/[^a-zA-Z0-9._-]/', '_', $segment) ?? '';
        return trim($segment, '._') ?: 'arquivo';
    }

    private function mime(string $file): string
    {
        $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        return match ($ext) {
            'png' => 'image/png',
            'jpg', 'jpeg' => 'image/jpeg',
            'webp' => 'image/webp',
            'gif' => 'image/gif',
            'svg' => 'image/svg+xml',
            'ico' => 'image/x-icon',
            'pdf' => 'application/pdf',
            default => function_exists('mime_content_type') ? (mime_content_type($file) ?: 'application/octet-stream') : 'application/octet-stream',
        };
    }

    private function uploadErrorMessage(int $code): string
    {
        return match ($code) {
            UPLOAD_ERR_INI_SIZE => 'O arquivo excede upload_max_filesize do PHP.',
            UPLOAD_ERR_FORM_SIZE => 'O arquivo excede o limite do formulário.',
            UPLOAD_ERR_PARTIAL => 'O arquivo foi enviado parcialmente.',
            UPLOAD_ERR_NO_FILE => 'Nenhum arquivo foi enviado.',
            UPLOAD_ERR_NO_TMP_DIR => 'Pasta temporária ausente no servidor.',
            UPLOAD_ERR_CANT_WRITE => 'Falha ao gravar arquivo em disco.',
            UPLOAD_ERR_EXTENSION => 'Uma extensão do PHP bloqueou o upload.',
            default => 'Erro desconhecido no upload.',
        };
    }
}
