<?php

namespace App\Services;

class PermissionGroupHelper
{
    private const DEFAULT_GROUP = 'General';

    /** @var array<int, array{group: string, starts?: string[], contains?: string[], exact?: string[]}> */
    private static array $rules;

    /**
     * Return the display group name for a permission (by name only).
     * Group names follow sidebar menu titles. Order of rules matters (first match wins).
     */
    public static function getGroupName(string $name): string
    {
        foreach (self::rules() as $rule) {
            if (self::matches($name, $rule)) {
                return $rule['group'];
            }
        }
        return self::DEFAULT_GROUP;
    }

    /**
     * Display order of permission groups (matches sidebar menu order).
     */
    public static function getGroupDisplayOrder(): array
    {
        return [
            'Dashboard', 'Admin', 'Customers', 'Corporate', 'MAC Customer',
            'Support', 'Connection', 'Billing & Payment', 'Accounts', 'Reports',
            'Reseller', 'Package', 'POP & Box', 'Mikrotik', 'Store', 'Online Users',
            'CRM', 'Settings', 'SMS & Notification', 'Commission', 'Area & Location',
            'Network & Technical', 'Logs & History', 'General',
        ];
    }

    private static function matches(string $name, array $rule): bool
    {
        if (isset($rule['match']) && is_callable($rule['match'])) {
            return $rule['match']($name);
        }
        if (isset($rule['exact']) && in_array($name, $rule['exact'], true)) {
            return true;
        }
        if (isset($rule['starts_with'])) {
            foreach ($rule['starts_with'] as $prefix) {
                if (str_starts_with($name, $prefix)) {
                    return true;
                }
            }
        }
        if (isset($rule['contains'])) {
            foreach ($rule['contains'] as $substr) {
                if (str_contains($name, $substr)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Ordered rules: first match wins. More specific patterns must appear earlier.
     */
    private static function rules(): array
    {
        if (isset(self::$rules)) {
            return self::$rules;
        }

        self::$rules = [
            ['group' => 'Support', 'contains' => ['support', '-support', '_support', 'token', '-token', 'Token', 'new-line', 'new_connection', 'newjoin', 'department-index', 'cancel-reasons', 'pending-customer', 'approve-local', 'local-user-first', 'login-attempts', 'support_home', 'new_line_support', 'pool_name_in_support', 'token-dashboard', 'comment-token', 'token-note', 'show-connection', 'token-close', 'connection-request', 'connection-history', 'connection-search', 'manage-connection', 'assign-token', 'new-token', 'update-token', 'delete-token', 'token-category', 'token-code', 'TokenBy', 'token-source']],
            ['group' => 'Corporate', 'starts_with' => ['corporate', 'Corporate']],
            ['group' => 'MAC Customer', 'starts_with' => ['mac-customer']],
            ['group' => 'CRM', 'starts_with' => ['crm_', 'crm-']],
            ['group' => 'Reseller', 'contains' => ['reseller', 'sub-reseller']],
            ['group' => 'Mikrotik', 'contains' => ['mikrotik']],
            ['group' => 'POP & Box', 'contains' => ['pop_', 'box_', 'switch_'], 'starts_with' => ['pop-'], 'exact' => ['pop_box', 'box_switch']],
            ['group' => 'Admin', 'contains' => ['permission_', 'role_', 'employee', 'user_', 'user-', 'password', 'login', 'assign-user'], 'exact' => ['assign-permission', 'assign-permission-role', 'assign-role', 'user-registration', 'password-change', 'user-logout', 'get-username-availability', 'log-history', 'deletedUser', 'add-upazila', 'add-thana']],
            ['group' => 'Store', 'contains' => ['inventory', 'requisition', 'supplier_', 'unit_']],
            ['group' => 'Package', 'contains' => ['package', 'sub-package']],
            ['group' => 'Accounts', 'contains' => ['ledger', 'payment_', 'receive_', 'contra_', 'journal_', 'account-group', 'account-statement', 'account_', 'profit_n_loss', 'balance-sheet', 'income', 'income-', 'expense', 'bill-collection', 'other-income', 'accounts-manager', 'accounts-token', 'fund-transfer'], 'exact' => ['bank', 'payroll', 'cash-in-hand']],
            ['group' => 'Reports', 'contains' => ['report', 'Report']],
            ['group' => 'Dashboard', 'contains' => ['dashboard'], 'exact' => ['home']],
            ['group' => 'SMS & Notification', 'contains' => ['sms', 'voice_sms', 'notification']],
            ['group' => 'Commission', 'contains' => ['commission', 'manager_unique', 'manager-accounts', 'pop-accounts']],
            ['group' => 'Customers', 'contains' => ['customer', 'client', 'client_']],
            ['group' => 'Connection', 'contains' => ['connection', 'manage-connection']],
            ['group' => 'Billing & Payment', 'contains' => ['bill', 'money-receipt', 'payment', 'discount', 'recharge', 'ip-bill']],
            ['group' => 'Settings', 'contains' => ['company', 'refresh', 'form-setting', 'email-setting', 'sms-setting', 'billing_start']],
            ['group' => 'Online Users', 'contains' => ['online', 'ofline', 'disconnect', 'radius_online']],
            ['group' => 'Area & Location', 'contains' => ['thana', 'upazila', 'reseller_area']],
            ['group' => 'Logs & History', 'match' => static fn (string $n): bool => (str_contains($n, 'view-') || str_contains($n, 'view_')) && (str_contains($n, 'log') || str_contains($n, 'history'))],
            ['group' => 'Network & Technical', 'contains' => ['ip-pool', 'static_manage', 'apply-bandwidth', 'bandwidth', 'network-manage', 'line-shift', 'router-remote', 'onu_information', 'add-tj-box', 'add-pon', 'ignore-mikrotik', 'enable-limit-over', 'dynamic-ip', 'port-assignment']],
        ];

        return self::$rules;
    }
}
