<?php

namespace App\Services;

use App\Models\PermissionModel;
use App\Models\RoleModel;
use App\Models\RolePermissionModel;

/**
 * Resolves which navigation links the current user may see.
 */
class NavigationPermissionService
{
    private const ADMIN_ONLY = '__admin__';

    /** Paths any logged-in user may see (auth filter only). */
    private array $publicPaths = [
        'profile',
        'password/change',
        'account/sessions',
        'logout',
    ];

    /** Explicit path → permission slug overrides for non-obvious routes. */
    private array $pathOverrides = [
        'dashboard'                        => 'dashboard.view',
        'hrm'                              => 'employees.view',
        'sale'                             => 'sale.view',
        'contracts'                        => 'contracts.view',
        'procurement'                      => 'procurement.view',
        'inventory'                        => 'inventory.view',
        'accounting'                       => 'accounting.view',
        'operations'                       => 'operations.view',
        'transport'                        => 'transport.view',
        'reports'                          => '__any_reports__',
        'settings'                         => self::ADMIN_ONLY,
        'settings/company'                 => self::ADMIN_ONLY,
        'settings/modules'                 => self::ADMIN_ONLY,
        'settings/fiscal-years'            => self::ADMIN_ONLY,
        'settings/cost-centers'            => self::ADMIN_ONLY,
        'messages'                         => 'messages.view',
        'messages/groups'                  => 'messages.manage_groups',
        'users'                            => 'users.view',
        'roles'                            => 'roles.view',
        'permissions'                      => 'permissions.view',
        'employees/create'                 => 'employees.create',
        'documents'                        => 'employee_documents.view',
        'documents/expiry'                 => 'employee_documents.view',
        'documents/types'                  => 'document_types.view',
        'leave/types'                      => 'leave_types.view',
        'leave/balances'                   => 'leave_balances.view',
        'leave/requests'                   => 'leave_requests.view',
        'leave/calendar'                   => 'leave_calendar.view',
        'attendance/manual'                => 'attendance.manual',
        'attendance/corrections'           => 'attendance_corrections.view',
        'attendance/shifts'                => 'attendance_shifts.view',
        'attendance/shifts/assignments'    => 'attendance_shifts.view',
        'payroll/runs'                     => 'payroll_runs.view',
        'payroll/salary'                   => 'payroll_salary.view',
        'payroll/payslips'                 => 'payroll_payslips.view',
        'payroll/settlements'              => 'payroll_settlements.view',
        'payroll/settings'                 => 'payroll_settings.view',
        'assets/assignments'               => 'assets.view',
        'assets/types'                     => 'asset_types.view',
    ];

    private ?array $userSlugs = null;
    private ?array $allSlugs  = null;

    /**
     * Permission(s) required to show a module in the top navbar.
     * Uses the module dashboard / entry permission — not any sub-page.
     */
    private array $moduleEntryPermissions = [
        'hrm'         => ['employees.view', 'departments.view', 'attendance.view', 'leave_requests.view', 'payroll_runs.view', 'scheduling.view', 'employee_documents.view', 'holidays.view'],
        'sale'        => ['sale.view'],
        'contracts'   => ['contracts.view'],
        'procurement' => ['procurement.view'],
        'inventory'   => ['inventory.view'],
        'accounting'  => ['accounting.view'],
        'operations'  => ['operations.view'],
        'transport'   => ['transport.view'],
    ];

    /** Permission groups that belong to the Sales & CRM module. */
    private array $salePermissionGroups = [
        'sale', 'sale_reports', 'leads', 'lead_sources', 'lead_assignment', 'lead_scoring', 'lead_conversion',
        'companies', 'contacts', 'customer_groups', 'customer_history', 'customer_documents',
        'opportunities', 'sales_stages', 'expected_revenue', 'probability', 'competitors',
        'calls', 'emails', 'meetings', 'tasks', 'follow_ups', 'quotations', 'quotation_approval',
        'price_lists', 'quotation_templates', 'orders', 'order_approval', 'order_tracking',
        'delivery_notes', 'dispatch', 'delivery_tracking', 'invoices', 'payment_status',
        'outstanding_payments', 'sales_returns', 'refunds', 'sales_representatives', 'targets',
        'commissions', 'territories',
    ];

    private ?array $slugGroups = null;
    private ?ModuleRegistryService $moduleRegistry = null;

    public function can(string $slug): bool
    {
        return in_array($slug, $this->userSlugs(), true);
    }

    public function canAny(array $slugs): bool
    {
        foreach ($slugs as $slug) {
            if ($this->can($slug)) {
                return true;
            }
        }

        return false;
    }

    public function canAccessPath(string $path): bool
    {
        return $this->canAccessNavItem($path);
    }

    private function canAccessNavItem(string $path): bool
    {
        $path = trim($path, '/');

        if (! session()->get('isLoggedIn')) {
            return false;
        }

        if ($path === '' || in_array($path, $this->publicPaths, true)) {
            return true;
        }

        if ($path === 'reports') {
            return $this->reportsModuleHasAccessibleReports();
        }

        $permission = $this->resolvePermission($path);

        if ($permission === self::ADMIN_ONLY) {
            return $this->isAdmin();
        }

        if ($permission === '__any_reports__') {
            return $this->reportsModuleHasAccessibleReports();
        }

        if ($permission === null) {
            return false;
        }

        if (! $this->moduleIsEnabledForPath($path)) {
            return false;
        }

        return $this->can($permission);
    }

    /**
     * @param array{groups?: list<array{items?: list<array{path: string}>}>} $module
     */
    public function moduleHasVisibleItems(array $module): bool
    {
        foreach ($module['groups'] ?? [] as $group) {
            foreach ($group['items'] ?? [] as $item) {
                if ($this->canAccessNavItem($item['path'])) {
                    return true;
                }
            }
        }

        return false;
    }

    private function reportsModuleHasAccessibleReports(): bool
    {
        if (! $this->moduleRegistry()->isEnabled('reports')) {
            return false;
        }

        $module = config('Navigation')->modules['reports'] ?? [];

        foreach ($module['groups'] ?? [] as $group) {
            foreach ($group['items'] ?? [] as $item) {
                if ($item['path'] === 'reports') {
                    continue;
                }

                $permission = $this->resolvePermission($item['path']);

                if ($permission === self::ADMIN_ONLY) {
                    if ($this->isAdmin()) {
                        return true;
                    }
                    continue;
                }

                if ($permission !== null && $permission !== '__any_reports__' && $this->can($permission)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * @param list<array{label: string, icon: string, items: list<array{path: string}>}> $groups
     *
     * @return list<array{label: string, icon: string, items: list<array>}>
     */
    public function filterGroups(array $groups): array
    {
        $filtered = [];

        foreach ($groups as $group) {
            $items = array_values(array_filter(
                $group['items'] ?? [],
                fn (array $item): bool => $this->canAccessNavItem($item['path'])
            ));

            if ($items !== []) {
                $group['items'] = $items;
                $filtered[]     = $group;
            }
        }

        return $filtered;
    }

    /**
     * Top navbar module routes mapped to Navigation config keys.
     * Requires the module's entry/dashboard permission — not just any sub-page.
     */
    public function canAccessTopModule(string $route): bool
    {
        if (! $this->moduleRegistry()->isEnabled($route)) {
            return false;
        }

        if ($route === 'dashboard') {
            return $this->can('dashboard.view');
        }

        if ($route === 'reports') {
            return $this->reportsModuleHasAccessibleReports();
        }

        if (isset($this->moduleEntryPermissions[$route])) {
            return $this->canAny($this->moduleEntryPermissions[$route]);
        }

        $nav     = config('Navigation');
        $modules = $nav->modules ?? [];

        if (isset($modules[$route])) {
            return $this->moduleHasVisibleItems($modules[$route]);
        }

        return $this->canAccessPath($route);
    }

    private function resolvePermission(string $path): ?string
    {
        $path = trim(strtolower($path), '/');

        if (isset($this->pathOverrides[$path])) {
            return $this->pathOverrides[$path];
        }

        foreach ($this->candidatePermissions($path) as $candidate) {
            if (! in_array($candidate, $this->allSlugs(), true)) {
                continue;
            }

            if ($this->permissionMatchesPath($path, $candidate)) {
                return $candidate;
            }
        }

        return null;
    }

    /**
     * Prevent cross-module false positives (e.g. sale/tasks → unrelated tasks.view).
     */
    private function permissionMatchesPath(string $path, string $permissionSlug): bool
    {
        $segments = explode('/', $path);

        if (($segments[0] ?? '') !== 'sale' || count($segments) < 2) {
            return true;
        }

        $group = $this->permissionGroupFor($permissionSlug);

        return $group !== null && in_array($group, $this->salePermissionGroups, true);
    }

    private function permissionGroupFor(string $slug): ?string
    {
        if ($this->slugGroups === null) {
            $this->slugGroups = array_column((new PermissionModel())->findAll(), 'group', 'slug');
        }

        return $this->slugGroups[$slug] ?? null;
    }

    /**
     * @return list<string>
     */
    private function candidatePermissions(string $path): array
    {
        $segments = explode('/', $path);
        $candidates = [];

        if (count($segments) >= 2) {
            $candidates[] = str_replace('-', '_', implode('_', array_slice($segments, -2))) . '.view';
        }

        $candidates[] = str_replace('-', '_', end($segments)) . '.view';
        $candidates[] = str_replace('-', '_', implode('_', $segments)) . '.view';

        if (str_contains($path, '/reports')) {
            $candidates[] = str_replace('-', '_', $segments[0]) . '_reports.view';
        }

        $candidates[] = str_replace('-', '_', $segments[0]) . '.view';

        return array_values(array_unique($candidates));
    }

    /**
     * @return list<string>
     */
    private function userSlugs(): array
    {
        if ($this->userSlugs !== null) {
            return $this->userSlugs;
        }

        $cached = session()->get('permission_slugs');
        if (is_array($cached)) {
            $this->userSlugs = $cached;

            return $this->userSlugs;
        }

        $roleId = session()->get('role_id');
        $this->userSlugs = $roleId
            ? (new RolePermissionModel())->slugsForRole((int) $roleId)
            : [];

        return $this->userSlugs;
    }

    /**
     * @return list<string>
     */
    private function allSlugs(): array
    {
        if ($this->allSlugs !== null) {
            return $this->allSlugs;
        }

        $this->allSlugs = array_column((new PermissionModel())->findAll(), 'slug');

        return $this->allSlugs;
    }

    private function isAdmin(): bool
    {
        $roleSlug = session()->get('role_slug');

        if ($roleSlug === null) {
            $roleId = session()->get('role_id');
            if ($roleId) {
                $role     = (new RoleModel())->find((int) $roleId);
                $roleSlug = $role['slug'] ?? null;
                session()->set('role_slug', $roleSlug);
            }
        }

        return $roleSlug === 'administrator';
    }

    private function moduleIsEnabledForPath(string $path): bool
    {
        $moduleKey = $this->moduleRegistry()->resolveModuleForPath($path);

        if ($moduleKey === null) {
            return true;
        }

        return $this->moduleRegistry()->isEnabled($moduleKey);
    }

    private function moduleRegistry(): ModuleRegistryService
    {
        if ($this->moduleRegistry === null) {
            $this->moduleRegistry = new ModuleRegistryService();
        }

        return $this->moduleRegistry;
    }
}
