<?php

namespace App\Http\Middleware;

use App\Models\Products\LicenseToken;
use App\Models\Products\Product;
use App\Services\Api\ApiResponse;
use App\Services\Api\HostNormalizer;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Symfony\Component\HttpFoundation\Response;

class ProductToken
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        // Extraer el bearer token de la petición
        $token = $request->bearerToken();

        // Validar que el token exista
        if (!$token) {
            return ApiResponse::unauthorized('Token no proporcionado');
        }

        // Buscar el token en la base de datos
        $licenseToken = LicenseToken::where('token', $token)->first();

        // Validar que el token exista en la base de datos
        if (!$licenseToken) {
            return ApiResponse::unauthorized('Token no válido');
        }

        // Validar que el token esté activo
        if (!$licenseToken->active) {
            return ApiResponse::forbidden('Token inactivo');
        }

        // Validar rango de fechas
        $now = now();

        // Si start_at está definida, verificar que now() >= start_at
        if ($licenseToken->start_at && $now->lessThan($licenseToken->start_at)) {
            return ApiResponse::forbidden('Token aún no válido. Fecha de inicio: ' . $licenseToken->start_at->format('Y-m-d H:i:s'));
        }

        // Si end_at está definida, verificar que now() <= end_at
        if ($licenseToken->end_at && $now->greaterThan($licenseToken->end_at)) {
            return ApiResponse::forbidden('Token expirado. Fecha de expiración: ' . $licenseToken->end_at->format('Y-m-d H:i:s'));
        }

        // Controlar el límite de peticiones usando RateLimiter
        $key = 'rate_limit:token:' . $token;

        // Si el token tiene usage_limit definido, usar ese valor como límite
        // Si no tiene usage_limit, aplicar un límite muy alto por defecto (10000)
        $limit = $licenseToken->usage_limit ?? 10000;

        // Verificar si se excedió el límite de peticiones
        if (RateLimiter::tooManyAttempts($key, $limit)) {
            $seconds = RateLimiter::availableIn($key);

            return ApiResponse::error(
                null,
                'Límite de peticiones excedido. Intente nuevamente en ' . ceil($seconds / 60) . ' minuto(s)',
                429,
                [],
                429
            );
        }

        // Incrementar el contador de peticiones (ventana de 60 segundos)
        RateLimiter::hit($key, 60);

        // Validar el parámetro host del payload
        $host = $request->input('host');

        if (!$host) {
            return ApiResponse::error(
                null,
                'Parámetro host no proporcionado',
                400,
                [],
                400
            );
        }

        // Obtener la acción (si está presente)
        $action = $request->input('action');

        // Normalizar el host para comparación (mantener host completo, no solo dominio)
        $normalizedHost = HostNormalizer::normalize($host);

        // Para la acción 'sync', buscar entorno por host sin filtrar por token (luego validamos)
        // Para otras acciones, buscar entorno por host filtrando por token
        if ($action === 'sync') {
            // Buscar entorno por host (sin filtrar por token)
            $environment = \App\Models\Environments\Environment::all()->first(function ($environment) use ($normalizedHost) {
                return HostNormalizer::normalize($environment->domain) === $normalizedHost;
            });
        } else {
            // Buscar entorno por host filtrando por token
            $environment = $licenseToken->environments()->get()->first(function ($environment) use ($normalizedHost) {
                return HostNormalizer::normalize($environment->domain) === $normalizedHost;
            });
        }

        // Para la acción 'licence', el entorno DEBE existir
        if ($action === 'licence') {
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'licence',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    2002,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Validar que el plugin está presente
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'licence',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'licence',
                    'Plugin not found',
                    2002,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'licence',
                    'Licence does not include this plugin',
                    2001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'products') {
            // Para la acción 'products', el entorno DEBE existir
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'products',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    2002,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Inyectar el entorno y el token en el request (no requiere validación de plugin)
            $request->merge(['_environment' => $environment]);
            $request->merge(['_license_token' => $licenseToken]);
        } elseif ($action === 'setup') {
            // Para la acción 'setup', el entorno DEBE existir
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'setup',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    3000,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Validar que el plugin está presente
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'setup',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'setup',
                    'Plugin not found',
                    3001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'setup',
                    'Licence does not include this plugin',
                    3001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'features') {
            // Para la acción 'features', validar plugin y producto
            // No requiere entorno (similar a 'setup')
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'features',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'features',
                    'Plugin not found',
                    5001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'features',
                    'Licence does not include this plugin',
                    5001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'tutorials') {
            // Para la acción 'tutorials', validar plugin y producto
            // No requiere entorno (similar a 'features')
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'tutorials',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'tutorials',
                    'Plugin not found',
                    4001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'tutorials',
                    'Licence does not include this plugin',
                    4001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'resources') {
            // Para la acción 'resources', validar plugin y producto
            // No requiere entorno (similar a 'features' y 'tutorials')
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'resources',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'resources',
                    'Plugin not found',
                    5001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'resources',
                    'Licence does not include this plugin',
                    5001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'scss') {
            // Para la acción 'scss', el entorno DEBE existir
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'scss',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    3000,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Validar que el plugin está presente
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'scss',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'scss',
                    'Plugin not found',
                    3001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'scss',
                    'Licence does not include this plugin',
                    3001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'scss-cdn') {
            // Para la acción 'scss-cdn', el entorno DEBE existir
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'scss-cdn',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    3000,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Validar que el plugin está presente
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'scss-cdn',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'scss-cdn',
                    'Plugin not found',
                    3001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'scss-cdn',
                    'Licence does not include this plugin',
                    3001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'js') {
            // Para la acción 'js', el entorno DEBE existir
            if (!$environment) {
                // Obtener todos los hosts asociados al token para debugging
                $availableDomains = $licenseToken->environments()->pluck('domain')->toArray();

                return ApiResponse::error(
                    'js',
                    'Environment not found for this host. Host searched: ' . $normalizedHost . '. Available hosts: ' . implode(', ', $availableDomains),
                    4000,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                        'available_hosts' => $availableDomains,
                    ],
                    401
                );
            }

            // Validar que el plugin está presente
            $plugin = $request->input('plugin');
            if (!$plugin) {
                return ApiResponse::error(
                    'js',
                    'Plugin parameter is required',
                    400,
                    [],
                    400
                );
            }

            // Buscar el producto por slug (el plugin debe coincidir con el slug del producto)
            $product = Product::where('slug', $plugin)->first();

            if (!$product) {
                return ApiResponse::error(
                    'js',
                    'Plugin not found',
                    4001,
                    [],
                    401
                );
            }

            // Verificar que el token tiene licencia para este producto
            $hasLicense = $licenseToken->products()
                ->where('products.id', $product->id)
                ->wherePivot('status', 'active')
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.start_at')
                        ->orWhere('license_token_product.start_at', '<=', $now);
                })
                ->where(function ($query) use ($now) {
                    $query->whereNull('license_token_product.end_at')
                        ->orWhere('license_token_product.end_at', '>=', $now);
                })
                ->exists();

            if (!$hasLicense) {
                return ApiResponse::error(
                    'js',
                    'Licence does not include this plugin',
                    4001,
                    [],
                    401
                );
            }

            // Inyectar el producto validado en el request
            $request->merge(['_validated_product' => $product]);
        } elseif ($action === 'sync') {
            // Para la acción 'sync', el entorno DEBE existir y coincidir con el token
            if (!$environment) {
                // Entorno no encontrado → 404
                return ApiResponse::error(
                    'sync',
                    'Environment not found for this host',
                    404,
                    [
                        'searched_host' => $normalizedHost,
                        'original_host' => $host,
                    ],
                    404
                );
            }

            // Si el entorno existe pero no coincide con el token → 401
            if ($environment->license_token_id !== $licenseToken->id) {
                return ApiResponse::error(
                    'sync',
                    'El host proporcionado no está vinculado a este token',
                    401,
                    [],
                    401
                );
            }
        } elseif (!$environment) {
            // Para otras acciones, el entorno debe existir
            return ApiResponse::forbidden('El host proporcionado no está vinculado a este token');
        }

        // Inyectar el LicenseToken y Environment (si existe) en el request para uso en el controlador
        $request->merge([
            '_license_token' => $licenseToken,
            '_environment' => $environment,
            '_normalized_host' => $environment ? $normalizedHost : null,
        ]);

        return $next($request);
    }
}
