<?php

namespace Core\System;

class AdminSecurityMiddleware
{
    protected string $ip;
    protected string $userAgent;
    protected string $uri;
    protected string $path;

    public function __construct()
    {
        $this->ip        = (string)($_SERVER['REMOTE_ADDR'] ?? 'unknown');
        $this->userAgent = (string)($_SERVER['HTTP_USER_AGENT'] ?? 'unknown');
        $this->uri       = (string)($_SERVER['REQUEST_URI'] ?? '/');
        $this->path      = parse_url($this->uri, PHP_URL_PATH) ?: '/';
    }

    public function handle()
    {
        if (!$this->isAdminRoute()) {
            return;
        }

        if ($this->isPublicAdminRoute()) {
            return;
        }

        $this->checkAdminSession();
        $this->checkSessionFingerprint();
    }

    protected function isAdminRoute(): bool
    {
        return $this->path === '/admin' || str_starts_with($this->path, '/admin/');
    }

    protected function isPublicAdminRoute(): bool
    {
        $public = [
            '/admin/login',
            '/admin/forgot-password',
            '/admin/reset-password',
        ];

        return in_array($this->path, $public, true);
    }

    /** Session idle timeout in seconds (120 minutes) */
    private const SESSION_TIMEOUT = 7200;

    protected function checkAdminSession(): void
    {
        if (
            !isset($_SESSION['admin_logged_in']) ||
            $_SESSION['admin_logged_in'] !== true ||
            empty($_SESSION['admin_id']) ||
            (int) $_SESSION['admin_id'] <= 0
        ) {
            $this->log('warning', 'Unauthorized admin access attempt');
            header('Location: /admin/login', true, 302);
            exit;
        }

        // Session timeout: destroy idle sessions
        $lastActivity = (int)($_SESSION['admin_last_activity'] ?? 0);
        if ($lastActivity > 0 && (time() - $lastActivity) > self::SESSION_TIMEOUT) {
            $this->log('info', 'Admin session expired due to inactivity');
            session_unset();
            session_destroy();
            header('Location: /admin/login?reason=timeout');
            exit;
        }
        $_SESSION['admin_last_activity'] = time();

        $this->checkRoleAccess();
    }

    protected function checkRoleAccess(): void
    {
        $role = (string)($_SESSION['admin_role'] ?? 'owner');
        if ($role === 'owner' || $role === 'manager') {
            return;
        }

        $path = $this->path;

        $alwaysBlocked = [
            '/admin/settings', '/admin/integrations', '/admin/system', '/admin/plugins',
            '/admin/updates-license', '/admin/finance', '/admin/customers', '/admin/appearance',
            '/admin/home-sections', '/admin/footer', '/admin/languages', '/admin/pages',
            '/admin/reviews',
        ];
        foreach ($alwaysBlocked as $blocked) {
            if ($path === $blocked || str_starts_with($path, $blocked . '/')) {
                $this->deny();
            }
        }

        // Notification AJAX endpoints are accessible to all authenticated admins
        $notifAjaxRoutes = [
            '/admin/notifications/unread',
            '/admin/notifications/unread-count',
            '/admin/notifications/read-from-dropdown',
            '/admin/notifications/read',
            '/admin/notifications/read-all',
            '/admin/notifications/archive',
            '/admin/notifications/archive-all',
        ];
        foreach ($notifAjaxRoutes as $allowed) {
            if ($path === $allowed || str_starts_with($path, $allowed . '/')) {
                return; // allow — don't check further permissions
            }
        }


        if ($path === '/admin/orders/ecotrack/communes') {
            $canShipping = \Core\App\Models\TeamMemberModel::can($role, 'manage_shipping');
            $canConfirm  = \Core\App\Models\TeamMemberModel::can($role, 'confirm_orders');
            if (!$canShipping && !$canConfirm) {
                $this->deny();
            }
            return;
        }

        // ── Team Communication: accessible to ALL authenticated admins ──
        $teamCommPrefixes = [
            '/admin/team/communication',
            '/admin/team/messages',
            '/admin/team/meetings',
            '/admin/team/group-chat',
            '/admin/team/presence',
            '/admin/team/announcements',
        ];
        foreach ($teamCommPrefixes as $commPrefix) {
            if ($path === $commPrefix || str_starts_with($path, $commPrefix . '/')) {
                return; // allow — no permission check needed
            }
        }

        // ── Dashboard: all roles can access, content is role-based ──
        if ($path === '/admin/dashboard') {
            return;
        }

        $routePermissions = [
            '/admin/orders/status'        => 'confirm_orders',
            '/admin/orders/return'        => 'manage_returns',
            '/admin/orders/return-provider' => 'manage_returns',
            '/admin/orders/call-attempt'  => 'confirm_orders',
            '/admin/shipments'            => 'manage_shipping',
            '/admin/orders/send'          => 'manage_shipping',
            '/admin/orders/prepare'       => 'manage_shipping',
            '/admin/orders/shipping'      => 'manage_shipping',
            '/admin/orders/ecotrack'      => 'manage_shipping',
            '/admin/suspicious-orders'    => 'view_suspicious',
            '/admin/products'             => 'view_products',
            '/admin/categories'           => 'view_products',
            '/admin/team'                 => 'manage_team',
            '/admin/orders/call-attempts' => 'view_stats',
        ];

        foreach ($routePermissions as $route => $permission) {
            if ($path === $route || str_starts_with($path, $route . '/')) {
                if (!\Core\App\Models\TeamMemberModel::can($role, $permission)) {
                    $this->deny();
                }
                break;
            }
        }

        if ($path === '/admin/orders' || str_starts_with($path, '/admin/orders/')) {
            $ordersExclude = [
                '/admin/orders/status', '/admin/orders/call-attempt', '/admin/orders/return',
                '/admin/orders/send', '/admin/orders/prepare', '/admin/orders/shipping', '/admin/orders/ecotrack',
            ];
            foreach ($ordersExclude as $ex) {
                if ($path === $ex || str_starts_with($path, $ex . '/')) {
                    return;
                }
            }
            $can = \Core\App\Models\TeamMemberModel::can($role, 'view_orders')
                || \Core\App\Models\TeamMemberModel::can($role, 'view_assigned_orders')
                || \Core\App\Models\TeamMemberModel::can($role, 'view_confirmed_orders')
                || \Core\App\Models\TeamMemberModel::can($role, 'view_orders_readonly');
            if (!$can) {
                $this->deny();
            }
        }
    }

    private function deny(): never
    {
        http_response_code(403);
        if (file_exists(ROOT_PATH . '/admin/templates/errors/403.php')) {
            require ROOT_PATH . '/admin/templates/errors/403.php';
        } else {
            echo '<div style="font-family:sans-serif;text-align:center;padding:60px"><h1 style="font-size:48px;color:#ef4444">403</h1><p style="color:#6b7280">' . htmlspecialchars(__('errors.403','Access Denied')) . '</p><a href="/admin/dashboard" style="color:#4f7eff">← ' . __('sidebar.dashboard','Dashboard') . '</a></div>';
        }
        exit;
    }

    protected function checkSessionFingerprint(): void
    {
        $fingerprint = hash('sha256', $this->ip . '|' . $this->userAgent);

        if (!isset($_SESSION['admin_fingerprint'])) {
            $_SESSION['admin_fingerprint'] = $fingerprint;
            return;
        }

        if (!hash_equals((string) $_SESSION['admin_fingerprint'], $fingerprint)) {
            session_destroy();
            $this->log('ban', 'Admin session hijacking detected');
            http_response_code(403);
            exit('403 Forbidden');
        }
    }

    protected function log(string $type, string $message): void
    {
        try {
            $db = db();
            $stmt = $db->prepare("INSERT INTO security_logs (type, ip, message, user_agent, url, created_at) VALUES (:type, :ip, :message, :ua, :url, NOW())");
            $stmt->execute([
                'type'    => $type,
                'ip'      => $this->ip,
                'message' => $message,
                'ua'      => $this->userAgent,
                'url'     => $this->path,
            ]);
        } catch (\Throwable $e) {
        }
    }
}
