<?php

namespace WMS\Middleware;

use WMS\Core\Response;
use WMS\Core\Request;

/**
 * Middleware de autenticación JWT.
 * Valida el token en el header Authorization: Bearer <token>.
 */
class AuthMiddleware
{
    private static ?array $currentUser = null;

    /**
     * Rutas públicas que no requieren autenticación.
     */
    private static array $publicRoutes = [
        '/api/auth/login',
    ];

    public static function handle(): bool
    {
        $uri      = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        $basePath = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/');
        if ($basePath && $basePath !== '.' && $basePath !== '/' && str_starts_with($uri, $basePath)) {
            $uri = substr($uri, strlen($basePath)) ?: '/';
        }

        // Permitir rutas públicas
        foreach (self::$publicRoutes as $route) {
            if (str_starts_with($uri, $route)) {
                return true;
            }
        }

        $authHeader = Request::header('Authorization');
        // Accept token via query param for CSV downloads
        if (empty($authHeader) && !empty($_GET['_token'])) {
            $authHeader = 'Bearer ' . $_GET['_token'];
        }
        if (!$authHeader || !str_starts_with($authHeader, 'Bearer ')) {
            Response::error('Token de autenticación requerido', 401);
            return false;
        }

        $token = substr($authHeader, 7);
        $payload = self::verifyToken($token);

        if (!$payload) {
            Response::error('Token inválido o expirado', 401);
            return false;
        }

        self::$currentUser = $payload;
        return true;
    }

    /**
     * Obtiene el usuario autenticado actual.
     */
    public static function user(): ?array
    {
        return self::$currentUser;
    }

    /**
     * Obtiene el ID del usuario autenticado.
     */
    public static function userId(): ?int
    {
        return self::$currentUser['user_id'] ?? null;
    }

    public static function warehouseId(): ?int
    {
        return self::$currentUser['warehouse_id'] ?? null;
    }

    public static function enforceWarehouse(int $requestedWarehouseId): void
    {
        $userWh = self::warehouseId();
        if ($userWh && $userWh !== $requestedWarehouseId) {
            \WMS\Core\Response::error('No tiene permiso para operar en este almacen. Almacen asignado: ' . $userWh, 403);
            exit;
        }
    }

    /**
     * Genera un token JWT para un usuario.
     */
    public static function generateToken(array $user): string
    {
        $config = require __DIR__ . '/../config/app.php';
        $jwt = $config['jwt'];

        $header = self::base64UrlEncode(json_encode([
            'typ' => 'JWT',
            'alg' => $jwt['algorithm'],
        ]));

        $payload = self::base64UrlEncode(json_encode([
            'user_id'      => $user['id'],
            'username'     => $user['username'],
            'role'         => $user['role'],
            'warehouse_id' => $user['warehouse_id'],
            'iat'          => time(),
            'exp'          => time() + $jwt['expiration'],
        ]));

        $signature = self::base64UrlEncode(
            hash_hmac('sha256', "$header.$payload", $jwt['secret'], true)
        );

        return "$header.$payload.$signature";
    }

    /**
     * Verifica y decodifica un token JWT.
     */
    private static function verifyToken(string $token): ?array
    {
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return null;
        }

        [$header, $payload, $signature] = $parts;

        $config = require __DIR__ . '/../config/app.php';
        $jwt = $config['jwt'];

        $expectedSig = self::base64UrlEncode(
            hash_hmac('sha256', "$header.$payload", $jwt['secret'], true)
        );

        if (!hash_equals($expectedSig, $signature)) {
            return null;
        }

        $data = json_decode(self::base64UrlDecode($payload), true);

        if (!$data || !isset($data['exp']) || $data['exp'] < time()) {
            return null;
        }

        return $data;
    }

    private static function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    private static function base64UrlDecode(string $data): string
    {
        return base64_decode(strtr($data, '-_', '+/'));
    }
}
