<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\TechnicalMaterial;

class MaterialService
{
    private TechnicalMaterial $materialModel;

    public function __construct()
    {
        $this->materialModel = new TechnicalMaterial();
    }

    public function getMaterials(array $filters = [], int $page = 1, int $perPage = 20): array
    {
        return $this->materialModel->findWithPagination($page, $perPage, $filters);
    }

    public function getMaterial(int $id): array
    {
        $material = $this->materialModel->findById($id);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }
        return $material;
    }

    public function uploadMaterial(array $data, array $fileData, int $userId): array
    {
        if (empty($data['title'])) {
            throw new \RuntimeException('El título es obligatorio', 422);
        }
        if (empty($data['type']) || !in_array($data['type'], ['pdf', 'video', 'image', 'document'])) {
            throw new \RuntimeException('Tipo de material no válido', 422);
        }

        $filePath = null;
        $externalUrl = $data['external_url'] ?? null;

        if ($data['type'] === 'video' && empty($externalUrl) && empty($fileData['tmp_name'])) {
            throw new \RuntimeException('Debe proporcionar un archivo o URL externa para videos', 422);
        }

        if (!empty($fileData['tmp_name']) && $fileData['error'] === UPLOAD_ERR_OK) {
            $filePath = $this->storeFile($fileData, $data['type']);
        }

        $id = $this->materialModel->create([
            'title' => $data['title'],
            'description' => $data['description'] ?? null,
            'type' => $data['type'],
            'category_id' => !empty($data['category_id']) ? (int) $data['category_id'] : null,
            'file_path' => $filePath,
            'external_url' => $externalUrl,
            'public_token' => $this->generateToken(),
            'created_by' => $userId,
        ]);

        return $this->getMaterial($id);
    }

    public function updateMaterialWithFile(int $id, array $data, array $fileData): array
    {
        $material = $this->materialModel->findById($id);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }

        $updateData = [];
        foreach (['title', 'description', 'external_url', 'category_id'] as $field) {
            if (array_key_exists($field, $data)) {
                $updateData[$field] = $data[$field];
            }
        }

        if (!empty($fileData['tmp_name']) && $fileData['error'] === UPLOAD_ERR_OK) {
            if (!empty($material['file_path']) && file_exists($material['file_path'])) {
                @unlink($material['file_path']);
            }
            $updateData['file_path'] = $this->storeFile($fileData, $data['type'] ?? $material['type']);
        }

        if (!empty($updateData)) {
            $this->materialModel->update($id, $updateData);
        }

        return $this->getMaterial($id);
    }

    public function updateMaterial(int $id, array $data): array
    {
        $material = $this->materialModel->findById($id);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }

        if (empty($material['public_token'])) {
            $data['public_token'] = $this->generateToken();
        }

        $updateData = [];
        foreach (['title', 'description', 'external_url', 'category_id', 'public_token'] as $field) {
            if (array_key_exists($field, $data)) {
                $updateData[$field] = $data[$field];
            }
        }

        if (!empty($updateData)) {
            $this->materialModel->update($id, $updateData);
        }

        return $this->getMaterial($id);
    }

    public function getPublicMaterial(string $token): array
    {
        $material = $this->materialModel->findByToken($token);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }

        if (empty($material['file_path']) || !file_exists($material['file_path'])) {
            throw new \RuntimeException('Archivo no disponible', 404);
        }

        return $material;
    }

    public function deleteMaterial(int $id): void
    {
        $material = $this->materialModel->findById($id);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }

        if (!empty($material['file_path']) && file_exists($material['file_path'])) {
            @unlink($material['file_path']);
        }

        $this->materialModel->delete($id);
    }

    public function downloadMaterial(int $id): array
    {
        $material = $this->materialModel->findById($id);
        if (!$material) {
            throw new \RuntimeException('Material no encontrado', 404);
        }

        if (empty($material['file_path']) || !file_exists($material['file_path'])) {
            throw new \RuntimeException('Archivo no disponible', 404);
        }

        return $material;
    }

    private function storeFile(array $fileData, string $type): string
    {
        $typeConfig = [
            'pdf'      => ['mimes' => ['application/pdf'],                            'ext' => 'pdf',  'maxSize' => 20 * 1024 * 1024],
            'image'    => ['mimes' => ['image/jpeg','image/png','image/gif','image/webp'], 'ext' => 'jpg',  'maxSize' => 10 * 1024 * 1024],
            'video'    => ['mimes' => ['video/mp4','video/webm','video/ogg'],          'ext' => 'mp4',  'maxSize' => 100 * 1024 * 1024],
            'document' => ['mimes' => ['application/pdf','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/vnd.ms-excel','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/vnd.ms-powerpoint','application/vnd.openxmlformats-officedocument.presentationml.presentation'], 'ext' => 'doc', 'maxSize' => 20 * 1024 * 1024],
        ];

        $cfg = $typeConfig[$type] ?? $typeConfig['pdf'];

        $detectedMime = '';
        if (class_exists('finfo')) {
            $detectedMime = (new \finfo(FILEINFO_MIME_TYPE))->file($fileData['tmp_name']) ?: '';
        }
        $mimeType = $detectedMime ?: ($fileData['type'] ?? '');
        if (!in_array($mimeType, $cfg['mimes'])) {
            throw new \RuntimeException("Tipo de archivo no permitido para {$type}", 422);
        }

        if ($fileData['size'] > $cfg['maxSize']) {
            $maxMB = $cfg['maxSize'] / (1024 * 1024);
            throw new \RuntimeException("El archivo excede los {$maxMB}MB", 422);
        }

        $subDir = STORAGE_PATH . '/materials/';
        if (!is_dir($subDir)) {
            mkdir($subDir, 0755, true);
        }

        $filename = uniqid("material_{$type}_") . '.' . $cfg['ext'];
        $destPath = $subDir . $filename;

        if (!move_uploaded_file($fileData['tmp_name'], $destPath)) {
            throw new \RuntimeException('Error al guardar el archivo', 500);
        }

        return $destPath;
    }

    private function generateToken(): string
    {
        return strtolower(
            bin2hex(random_bytes(3)) . '-' .
            bin2hex(random_bytes(3)) . '-' .
            bin2hex(random_bytes(4))
        );
    }
}
