<?php

/**
 * Сервис для унифицированной загрузки файлов
 * Инкапсулирует логику загрузки с валидацией и обработкой ошибок
 *
 * @package Flowaxy\Support\Services
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Support\Services;

use Exception;
use Flowaxy\Core\System\PathResolver;
use Flowaxy\Support\Facades\Log;
use Flowaxy\Support\Facades\Upload;
use Flowaxy\Support\Helpers\FileHelper;
use Flowaxy\Support\Helpers\ResourceCleanupHelper;
use Flowaxy\Support\Helpers\UploadHelper;
use Throwable;

use function array_merge;

final class FileUploadService
{
    /**
     * Загрузка файла с валидацией
     *
     * @param array<string, mixed> $file Файл из $_FILES
     * @param array<string, mixed> $options Опции загрузки
     * @return array{success: bool, file?: string, path?: string, error?: string}
     */
    public function upload(array $file, array $options = []): array
    {
        $defaultOptions = [
            'upload_dir' => null,
            'allowed_extensions' => ['zip'],
            'allowed_mime_types' => ['application/zip', 'application/x-zip-compressed'],
            'max_file_size' => 50 * 1024 * 1024, // 50 MB
            'overwrite' => false,
            'naming_strategy' => 'random',
        ];

        $options = array_merge($defaultOptions, $options);

        try {
            // Валидация файла
            $validation = $this->validate($file, $options);
            if (!$validation['valid']) {
                return [
                    'success' => false,
                    'error' => $validation['error'] ?? 'Файл не валідний',
                ];
            }

            // Определяем директорию загрузки
            $uploadDir = $options['upload_dir'] ?? $this->getTempDirectory();

            // Создаем директорию, если нужно
            if (!FileHelper::makeDirectory($uploadDir, 0755, true)) {
                return [
                    'success' => false,
                    'error' => 'Не вдалося створити директорію завантаження',
                ];
            }

            // Загружаем файл
            $upload = Upload::setUploadDir($uploadDir)
                ->setAllowedExtensions($options['allowed_extensions'])
                ->setAllowedMimeTypes($options['allowed_mime_types'])
                ->setMaxFileSize($options['max_file_size']);

            if ($options['naming_strategy'] === 'random') {
                $upload->setNamingStrategy('random');
            }

            if ($options['overwrite']) {
                $upload->setOverwrite(true);
            }

            $result = $upload->handle($file);

            if ($result === false) {
                return [
                    'success' => false,
                    'error' => 'Не вдалося завантажити файл',
                ];
            }

            return [
                'success' => true,
                'file' => $result['file'] ?? null,
                'path' => $result['path'] ?? null,
            ];
        } catch (Throwable $e) {
            try {
                Log::Error('Помилка завантаження файлу', ['exception' => $e]);
            } catch (Throwable $logError) {
                // Ignore logging errors
            }
            return [
                'success' => false,
                'error' => $e->getMessage(),
            ];
        }
    }

    /**
     * Валидация файла
     *
     * @param array<string, mixed> $file Файл из $_FILES
     * @param array<string, mixed> $options Опции валидации
     * @return array{valid: bool, error?: string}
     */
    public function validate(array $file, array $options = []): array
    {
        // Проверка наличия файла
        if (empty($file) || !isset($file['tmp_name'])) {
            return [
                'valid' => false,
                'error' => 'Файл не вибрано',
            ];
        }

        // Проверка ошибок загрузки
        if (!UploadHelper::isValid($file)) {
            $error = UploadHelper::getError($file);
            return [
                'valid' => false,
                'error' => $error ?: 'Помилка завантаження файлу',
            ];
        }

        // Проверка размера
        if (isset($options['max_file_size'])) {
            $fileSize = UploadHelper::getSize($file);
            if ($fileSize > $options['max_file_size']) {
                return [
                    'valid' => false,
                    'error' => 'Файл перевищує максимальний розмір',
                ];
            }
        }

        // Проверка расширения
        if (isset($options['allowed_extensions']) && !empty($options['allowed_extensions'])) {
            $extension = UploadHelper::getExtension($file);
            if (!in_array(strtolower($extension), array_map('strtolower', $options['allowed_extensions']), true)) {
                return [
                    'valid' => false,
                    'error' => 'Недозволене розширення файлу',
                ];
            }
        }

        // Проверка MIME типа
        if (isset($options['allowed_mime_types']) && !empty($options['allowed_mime_types'])) {
            $mimeType = UploadHelper::getMimeType($file);
            if (!in_array($mimeType, $options['allowed_mime_types'], true)) {
                return [
                    'valid' => false,
                    'error' => 'Недозволений MIME тип файлу',
                ];
            }
        }

        return ['valid' => true];
    }

    /**
     * Перемещение файла во временную директорию
     *
     * @param array<string, mixed> $file Файл из $_FILES
     * @param string|null $tempDir Временная директория (если null, используется storage/temp/)
     * @return string|false Путь к файлу или false при ошибке
     */
    public function moveToTemp(array $file, ?string $tempDir = null): string|false
    {
        try {
            $tempDir = $tempDir ?? $this->getTempDirectory();

            if (!FileHelper::makeDirectory($tempDir, 0755, true)) {
                return false;
            }

            $result = $this->upload($file, [
                'upload_dir' => $tempDir,
                'naming_strategy' => 'random',
            ]);

            if ($result['success'] && isset($result['file'])) {
                return $result['file'];
            }

            return false;
        } catch (Throwable $e) {
            try {
                Log::Error('Помилка переміщення файлу', ['exception' => $e]);
            } catch (Throwable $logError) {
                // Ignore logging errors
            }
            return false;
        }
    }

    /**
     * Очистка временных файлов
     *
     * @param string|array<string> $files Путь к файлу или массив путей
     * @return bool
     */
    public function cleanup(string|array $files): bool
    {
        $files = is_array($files) ? $files : [$files];
        $success = true;

        foreach ($files as $file) {
            if ($file && FileHelper::exists($file)) {
                $deleted = ResourceCleanupHelper::cleanupTempFiles($file);
                if ($deleted === 0) {
                    try {
                        Log::Warning('Помилка видалення тимчасового файлу', [
                            'file' => $file,
                            'exception' => new Exception('Не вдалося видалити тимчасовий файл'),
                        ]);
                    } catch (Throwable $e) {
                        // Ignore logging errors
                    }
                    $success = false;
                }
            }
        }

        return $success;
    }

    /**
     * Получение временной директории
     *
     * @return string
     */
    private function getTempDirectory(): string
    {
        $storageDir = PathResolver::storage();
        $tempDir = $storageDir . DS . 'temp' . DS;

        // Проверяем существование директории
        if (!FileHelper::isDirectory($tempDir)) {
            if (!FileHelper::makeDirectory($tempDir, 0755, true)) {
                throw new Exception('Не вдалося створити тимчасову директорію: ' . $tempDir);
            }
        }

        // Проверяем права на запись
        if (!is_writable($tempDir)) {
            throw new Exception('Немає прав на запис у тимчасову директорію: ' . $tempDir);
        }

        return $tempDir;
    }
}
