<?php

namespace App\Http\Middleware;

use App\Services\LicenseCore;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;

class LicenseGuard
{
    private $core;
    private $allowed = [
        'license.show', 'license.activate', 'license.settings', 'license.deactivate',
        'installer.index', 'installer.check-requirements', 'installer.verify-license',
        'installer.test-database', 'installer.save-environment', 'installer.run-installation',
    ];
    
    private $public = [
        '/', 'home', 'terms_service', 'privacy', 'contact', 'services', 'faq',
        'team', 'kb', 'blog', 'login', 'register', 'password.request', 'password.reset',
        'password.email', 'password.update', 'verification.notice', 'verification.verify', 'verification.resend',
    ];

    public function __construct(LicenseCore $core)
    {
        $this->core = $core;
    }

    public function handle(Request $request, Closure $next)
    {
        // Early check for home path - must be first to prevent redirect loops
        if ($request->is('/') || $request->path() === '' || $request->path() === '/') {
            return $next($request);
        }

        // Skip license validation for local development (fast check)
        if ($this->isLocalDevelopment()) {
            return $next($request);
        }

        // Skip for installer (fast path check)
        if ($this->isInstaller($request)) {
            return $next($request);
        }

        // Skip for public routes (optimized - check path first, then route name)
        if ($this->isPublic($request)) {
            return $next($request);
        }

        // Skip if database not ready (before expensive checks)
        if (!$this->dbReady()) {
            return $next($request);
        }

        // Skip for allowed routes (after db check, before integrity check)
        if (in_array(Route::currentRouteName(), $this->allowed)) {
            return $next($request);
        }

        // Integrity check (expensive - moved after fast checks, cached)
        if (!$this->integrityCheck()) {
            return $this->blockAccess('System integrity violation detected');
        }

        // License validation (expensive - last check)
        if (!$this->core->check()) {
            $key = $this->core->getKey();
            if (empty($key)) {
                return Redirect::route('license.show')->with('license_error', 'Application not activated. Please enter your purchase code.');
            }
            return Redirect::route('license.settings')->with('error', 'License verification failed. Please check your settings.');
        }

        return $next($request);
    }

    private function integrityCheck()
    {
        // Cache key for integrity check result
        $cacheKey = 'license_integrity_check';
        
        // Files to check
        $files = [
            app_path('Services/LicenseCore.php'),
            app_path('Http/Middleware/LicenseGuard.php'),
            app_path('Http/Controllers/LicenseController.php'),
        ];

        // Get file modification times for cache invalidation
        $fileMtimes = [];
        foreach ($files as $file) {
            if (file_exists($file)) {
                $fileMtimes[$file] = filemtime($file);
            } else {
                // File doesn't exist - can't cache, return false immediately
                return false;
            }
        }

        // Create cache key with file modification times to invalidate on file changes
        $cacheKeyWithMtime = $cacheKey . '_' . md5(serialize($fileMtimes));

        // Check cache first (fast path)
        $cachedResult = Cache::get($cacheKeyWithMtime);
        if ($cachedResult !== null) {
            return $cachedResult;
        }

        // Cache miss - perform integrity check (expensive operation)
        $result = $this->performIntegrityCheck($files);

        // Cache the result for 1 hour (or until files change)
        Cache::put($cacheKeyWithMtime, $result, now()->addHour());

        return $result;
    }

    /**
     * Perform the actual integrity check (expensive operation)
     * 
     * @param array $files
     * @return bool
     */
    private function performIntegrityCheck(array $files)
    {
        foreach ($files as $file) {
            if (!file_exists($file)) {
                return false;
            }
            
            // Check for expected class names
            $content = file_get_contents($file);
            if (strpos($content, 'LicenseCore') === false && strpos($content, 'LicenseGuard') === false && strpos($content, 'LicenseController') === false) {
                return false;
            }
        }

        // Check if middleware is registered using router
        $router = app('router');
        $middleware = $router->getMiddleware();
        
        return in_array(LicenseGuard::class, $middleware) || 
               in_array('license', $middleware) ||
               in_array(\App\Http\Middleware\AppIntegrityValidator::class, $middleware);
    }

    private function isInstaller($request)
    {
        return $request->is('install*') || $request->is('update*');
    }

    private function dbReady()
    {
        try {
            DB::connection()->getPdo();
            return Schema::hasTable('settings');
        } catch (\Exception $e) {
            return false;
        }
    }

    private function isPublic($request)
    {
        // Fast path check first (no route name resolution needed)
        if ($request->is('public/*')) {
            return true;
        }

        // Check path directly for common public paths (faster than route name)
        $path = $request->path();
        if ($path === '' || $path === '/') {
            return true;
        }

        // Fallback to route name check (slower, but necessary for named routes)
        $currentRoute = Route::currentRouteName();
        return in_array($currentRoute, $this->public);
    }

    private function isLocalDevelopment()
    {
        // Check if running in local development environment with specific host and port
        return $this->isLocalhostWithPort8900();
    }

    private function isLocalhostWithPort8900()
    {
        $host = request()->getHost();
        $port = request()->getPort();
        
        // Allow localhost and 127.0.0.1 with common development ports
        return ($host === 'localhost' || $host === '127.0.0.1') && in_array($port, [8000, 8900, 3000, 8080]);
    }

    private function blockAccess($message)
    {
        return response()->view('errors.503', ['message' => $message], 503);
    }
}
