<?php

namespace Licenzo\Framework\Http\Middleware;

use Licenzo\Framework\Contracts\Auth\Policy;
use Licenzo\Framework\Foundation\Application;
use Licenzo\Framework\Http\Request;
use Licenzo\Framework\Http\Router\UnAuthorizedRequestException;

class PolicyMiddleware
{
    /**
     * The application instance.
     */
    protected Application $app;

    /**
     * The policy instance or class name.
     */
    protected $policy;

    /**
     * Create a new policy middleware instance.
     */
    public function __construct(Application $app, $policy = null)
    {
        $this->app = $app;
        $this->policy = $policy;
    }

    /**
     * Handle an incoming request.
     * @throws UnAuthorizedRequestException
     * @throws \Exception
     */
    public function handle(Request $request, \Closure $next, $policy = null)
    {
        // Use the policy passed to handle() or the one from constructor
        $policyToUse = $policy ?? $this->policy;

        if (!$policyToUse) {
            // No policy specified, allow request to continue
            return $next($request);
        }

        // Resolve the policy instance
        $policyInstance = $this->resolvePolicy($policyToUse);

        if (!$policyInstance) {
            throw new \Exception("Policy [$policyToUse] not found");
        }

        // Check authorization
        if (!$policyInstance->authorize($request)) {
            throw new UnAuthorizedRequestException($policyInstance->message());
        }

        return $next($request);
    }

    /**
     * Resolve the policy instance.
     */
    protected function resolvePolicy($policy): ?Policy
    {
        // If it's already a Policy instance
        if ($policy instanceof Policy) {
            return $policy;
        }

        // If it's a class name string
        if (is_string($policy)) {
            try {
                // Try to resolve from container first
                if ($this->app->bound($policy)) {
                    $instance = $this->app->make($policy);
                } else {
                    // Create a new instance
                    $instance = new $policy();
                }

                return $instance instanceof Policy ? $instance : null;
            } catch (\Exception $e) {
                return null;
            }
        }

        return null;
    }

    /**
     * Create a policy middleware instance for a specific policy.
     */
    public static function for(string $policyClass): string
    {
        return static::class . ':' . $policyClass;
    }
}
