<?php

declare(strict_types=1);

namespace App\Core;

use App\Repositories\AccessControlRepository;
use Throwable;

final class AccessControl
{
    /** @var bool */
    private static $contextEnsured = false;

    /** @return array<string,mixed> */
    public static function permissions(): array
    {
        $permissions = \config('access_control.permissions', []);
        return is_array($permissions) ? $permissions : [];
    }

    public static function permissionForRoute(string $page, string $method): ?string
    {
        $routes = \config('access_control.route_permissions', []);
        $contract = is_array($routes) ? ($routes[$page] ?? null) : null;
        if (!is_array($contract)) {
            return null;
        }
        $method = strtoupper($method);
        return isset($contract[$method]) ? (string)$contract[$method] : (isset($contract['*']) ? (string)$contract['*'] : null);
    }

    public static function allows(string $permission): bool
    {
        self::ensureContext();
        $user = Auth::user();
        if (!$user) {
            return false;
        }
        $permissions = self::permissions();
        if (!isset($permissions[$permission])) {
            return false;
        }

        $context = is_array($user['access_control'] ?? null) ? $user['access_control'] : [];
        if (($context['source'] ?? '') === 'ACL' && !empty($context['roles'])) {
            return self::evaluateAcl($permission, $context, $permissions[$permission]);
        }
        return self::evaluateLegacy($permission, $user);
    }

    public static function canAccessPage(string $page): bool
    {
        $permission = self::permissionForRoute($page, 'GET');
        return $permission !== null && self::allows($permission);
    }

    public static function requirePermission(string $permission): void
    {
        Auth::requireLogin();
        if (!self::allows($permission)) {
            self::forbidden();
        }
    }

    public static function forbidden(): void
    {
        http_response_code(403);
        View::render('errors/403', [
            'title' => \current_lang() === 'ar' ? 'غير مصرح بالدخول' : 'Access not available',
            'activePage' => '',
        ]);
        exit;
    }

    public static function allowsBranch(string $hospitalCode): bool
    {
        self::ensureContext();
        $hospitalCode = \normalize_hospital_code($hospitalCode);
        if ($hospitalCode === '' || !BranchReadiness::isLive($hospitalCode)) {
            return false;
        }
        $user = Auth::user();
        if (!$user) {
            return false;
        }
        $context = is_array($user['access_control'] ?? null) ? $user['access_control'] : [];
        $roles = (array)($context['roles'] ?? []);
        foreach ($roles as $role) {
            if (in_array((string)($role['role_code'] ?? ''), ['SITE_ADMIN', 'SITE_DEVELOPER_SUPERUSER'], true)) {
                return true;
            }
        }
        $explicitBranches = array_values(array_filter(array_map(
            'strtolower',
            (array)($context['branches'] ?? [])
        )));

        if (in_array('*', $explicitBranches, true)) {
            return true;
        }

        if ($explicitBranches !== []) {
            return in_array($hospitalCode, $explicitBranches, true);
        }

        foreach ($roles as $role) {
            if ((int)($role['all_branches'] ?? 0) === 1) {
                return true;
            }
        }

        if (($context['source'] ?? '') === 'ACL') {
            return false;
        }

        return true;
    }

    /** @return array<string,array<string,mixed>> */
    public static function allowedHospitals(array $hospitals): array
    {
        $out = [];
        foreach (BranchReadiness::liveHospitals($hospitals) as $code => $hospital) {
            if (self::allowsBranch((string)$code)) {
                $out[(string)$code] = $hospital;
            }
        }
        return $out;
    }

    public static function firstAllowedBranch(): string
    {
        $hospitals = \config('hospitals', []);
        foreach (self::allowedHospitals(is_array($hospitals) ? $hospitals : []) as $code => $unused) {
            return (string)$code;
        }
        return BranchReadiness::fallback(\default_hospital());
    }

    public static function roleLabel(): string
    {
        self::ensureContext();
        $user = Auth::user();
        $roles = (array)($user['access_control']['roles'] ?? []);
        if ($roles === []) {
            return \current_lang() === 'ar' ? 'مستخدم لوحة المعلومات' : 'Dashboard User';
        }
        $role = $roles[0];
        $roleCode = strtoupper(trim((string)($role['role_code'] ?? '')));
        if ($roleCode === 'SITE_DEVELOPER_SUPERUSER') {
            return \current_lang() === 'ar'
                ? 'مدير الموقع والمطور الشامل'
                : 'Site Developer Administrator';
        }
        return (string)($role[\current_lang() === 'ar' ? 'role_name_ar' : 'role_name_en'] ?? $roleCode);
    }

    public static function schemaReady(): bool
    {
        try {
            return (new AccessControlRepository())->schemaReady();
        } catch (Throwable $e) {
            return false;
        }
    }

    public static function refresh(): void
    {
        self::$contextEnsured = false;
        $user = Auth::user();
        if ($user) {
            unset($user['access_control']);
            Session::put('user', $user);
        }
        self::ensureContext();
    }

    private static function ensureContext(): void
    {
        if (self::$contextEnsured) {
            return;
        }
        self::$contextEnsured = true;
        $user = Auth::user();
        if (!$user) {
            return;
        }
        try {
            $repo = new AccessControlRepository();
            if ($repo->schemaReady() && $repo->userHasActiveRole((string)$user['name'])) {
                $user['access_control'] = $repo->contextForUser((string)$user['name']);
            } else {
                $user['access_control'] = self::legacyContext($user);
            }
        } catch (Throwable $e) {
            error_log('[AccessControl] ' . $e->getMessage());
            $user['access_control'] = self::legacyContext($user);
        }
        Session::put('user', $user);
    }

    /** @param array<string,mixed> $permissionContract */
    private static function evaluateAcl(string $permission, array $context, array $permissionContract): bool
    {
        $category = (string)($permissionContract['category'] ?? 'business');
        $businessCategories = (array)\config('access_control.business_categories', ['business', 'reporting', 'workflow']);
        $baseAllowed = false;
        $explicitAllow = false;
        $explicitDeny = false;

        foreach ((array)($context['roles'] ?? []) as $role) {
            $roleCode = (string)($role['role_code'] ?? '');
            if (in_array($roleCode, ['SITE_ADMIN', 'SITE_DEVELOPER_SUPERUSER'], true)) {
                return true;
            }
            $policy = strtoupper((string)($role['policy_mode'] ?? 'EXPLICIT'));
            if ($policy === 'ALL') {
                $baseAllowed = true;
            } elseif ($policy === 'ALL_BUSINESS' && in_array($category, $businessCategories, true)) {
                $baseAllowed = true;
            }
            $effect = strtoupper((string)($context['overrides'][$roleCode][$permission] ?? ''));
            if ($effect === 'DENY') {
                $explicitDeny = true;
            } elseif ($effect === 'ALLOW') {
                $explicitAllow = true;
            }
        }
        if ($explicitDeny) {
            return false;
        }
        if ($explicitAllow) {
            return true;
        }
        return $baseAllowed;
    }

    private static function evaluateLegacy(string $permission, array $user): bool
    {
        $dashboard = (int)($user['dashboard_role'] ?? 0) === 1;
        $finance = (int)($user['dashboard_role_fin'] ?? 0) === 1;
        $revenue = (int)($user['dashboard_role_revenue'] ?? 0) === 1;
        if ($permission === 'daily_revenue_settings.manage' || strpos($permission, 'security.') === 0 || strpos($permission, 'technical.') === 0) {
            return false;
        }
        if ($finance) {
            return true;
        }
        if (in_array($permission, [
            'revenue.view', 'daily_inpatient_revenue.view', 'daily_inpatient_revenue.export',
            'specialty_finance.view', 'daily_revenue_settings.view'
        ], true)) {
            return $revenue;
        }
        return $dashboard;
    }

    /** @return array<string,mixed> */
    private static function legacyContext(array $user): array
    {
        $roleCode = (int)($user['dashboard_role_fin'] ?? 0) === 1
            ? 'LEGACY_FINANCE_ADMIN'
            : ((int)($user['dashboard_role_revenue'] ?? 0) === 1 ? 'LEGACY_REVENUE' : 'LEGACY_DASHBOARD');
        return [
            'source' => 'LEGACY_FALLBACK',
            'schema_ready' => false,
            'roles' => [[
                'role_code' => $roleCode,
                'role_name_en' => 'Legacy Dashboard Access',
                'role_name_ar' => 'صلاحيات لوحة المعلومات القديمة',
                'policy_mode' => 'LEGACY',
                'all_branches' => 1,
            ]],
            'overrides' => [],
            'branches' => [],
            'loaded_at' => date('c'),
        ];
    }
}
