<?php

namespace App\Services;

use App\Models\OrderAttachment;

class AttachmentService
{
    /**
     * Tipos de arquivo permitidos para anexo.
     * Mapeamento mime_type => extensões aceitas.
     */
    private const ALLOWED_TYPES = [
        'application/pdf'                                                          => 'pdf',
        'image/png'                                                                => 'png',
        'image/jpeg'                                                               => 'jpg',
        'image/gif'                                                                => 'gif',
        'application/msword'                                                       => 'doc',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'  => 'docx',
        'application/vnd.ms-excel'                                                 => 'xls',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'        => 'xlsx',
        'text/plain'                                                               => 'txt',
    ];

    /** Tamanho máximo por arquivo: 20 MB */
    private const MAX_SIZE = 20 * 1024 * 1024;

    /**
     * Raiz do diretório de uploads (fora do public/).
     */
    private function getBaseUploadDir(): string
    {
        return dirname(__DIR__, 2) . '/storage/uploads/orders';
    }

    /**
     * Pega o caminho do diretório de upload de uma ordem específica.
     */
    private function getOrderUploadDir(int $orderId): string
    {
        return $this->getBaseUploadDir() . '/' . $orderId;
    }

    /**
     * Garante que o diretório de upload da ordem exista.
     */
    private function ensureDirectory(string $path): void
    {
        if (!is_dir($path)) {
            mkdir($path, 0775, true);
        }
    }

    /**
     * Processa e armazena os arquivos do campo de upload.
     * Retorna a quantidade de arquivos salvos com sucesso.
     *
     * @param int   $orderId
     * @param array $filesInput  Entry do $_FILES (já normalizado como array de arquivos)
     * @return int
     * @throws \Exception em caso de erro crítico
     */
    public function storeFiles(int $orderId, array $filesInput): int
    {
        $uploadDir = $this->getOrderUploadDir($orderId);
        $this->ensureDirectory($uploadDir);

        $model = new OrderAttachment();
        $saved = 0;

        // Normaliza o $_FILES para iterar arquivo a arquivo
        $files = $this->normalizeFilesArray($filesInput);

        foreach ($files as $file) {
            // Ignora entradas vazias (campo não preenchido)
            if ($file['error'] === UPLOAD_ERR_NO_FILE) {
                continue;
            }

            if ($file['error'] !== UPLOAD_ERR_OK) {
                throw new \Exception("Erro no upload do arquivo '{$file['name']}': código {$file['error']}");
            }

            if ($file['size'] > self::MAX_SIZE) {
                throw new \Exception("O arquivo '{$file['name']}' excede o tamanho máximo permitido de 20 MB.");
            }

            // Detecta o tipo MIME real do arquivo (não confiar só no header HTTP)
            $finfo = new \finfo(FILEINFO_MIME_TYPE);
            $mimeType = $finfo->file($file['tmp_name']);

            if (!array_key_exists($mimeType, self::ALLOWED_TYPES)) {
                throw new \Exception("Tipo de arquivo não permitido: '{$file['name']}' ({$mimeType}).");
            }

            $extension  = self::ALLOWED_TYPES[$mimeType];
            $storedName = uniqid('attach_', true) . '.' . $extension;
            $destination = $uploadDir . '/' . $storedName;

            if (!move_uploaded_file($file['tmp_name'], $destination)) {
                throw new \Exception("Falha ao mover o arquivo '{$file['name']}' para o servidor.");
            }

            $model->create([
                'order_id'      => $orderId,
                'original_name' => $file['name'],
                'stored_name'   => $storedName,
                'mime_type'     => $mimeType,
                'size'          => $file['size'],
            ]);

            $saved++;
        }

        return $saved;
    }

    /**
     * Retorna o caminho absoluto do arquivo no disco.
     */
    public function getFilePath(int $orderId, string $storedName): string
    {
        return $this->getOrderUploadDir($orderId) . '/' . $storedName;
    }

    /**
     * Normaliza o array $_FILES para uma lista linear de arquivos,
     * independente de ser upload único ou múltiplo (name[]).
     */
    private function normalizeFilesArray(array $files): array
    {
        // Se é um único arquivo, encapsula em array
        if (!is_array($files['name'])) {
            return [$files];
        }

        $normalized = [];
        $count = count($files['name']);
        for ($i = 0; $i < $count; $i++) {
            $normalized[] = [
                'name'     => $files['name'][$i],
                'type'     => $files['type'][$i],
                'tmp_name' => $files['tmp_name'][$i],
                'error'    => $files['error'][$i],
                'size'     => $files['size'][$i],
            ];
        }

        return $normalized;
    }
}
