<?php
namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;

class RbacService
{
    /**
     * Get all permission codes for a user
     */
    public function getUserPermissions(int $userId): array
    {
        return Cache::remember("perms_{$userId}", 300, function () use ($userId) {
            return DB::table('user_has_roles as uhr')
                ->join('roles as r', 'r.id', '=', 'uhr.role_id')
                ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'r.id')
                ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
                ->where('uhr.user_id', $userId)
                ->where('uhr.status', 'active')
                ->where('rhp.status', 'active')
                ->where('p.status', 'active')
                ->whereNull('uhr.deleted_at_utc')
                ->whereNull('r.deleted_at_utc')
                ->whereNull('rhp.deleted_at_utc')
                ->whereNull('p.deleted_at_utc')
                ->pluck('p.code')
                ->unique()
                ->values()
                ->toArray();
        });
    }

    /**
     * Check if user has specific permission
     */
    public function hasPermission(int $userId, string $code): bool
    {
        return in_array($code, $this->getUserPermissions($userId));
    }

    /**
     * Get nested menu tree for user
     */
    public function getUserMenu(int $userId): array
    {
        $menuIds = Cache::remember("menu_{$userId}", 300, function () use ($userId) {
            return DB::table('user_has_roles as uhr')
                ->join('role_menu_access as rma', 'rma.role_id', '=', 'uhr.role_id')
                ->where('uhr.user_id', $userId)
                ->where('uhr.status', 'active')
                ->where('rma.can_view', true)
                ->where('rma.status', 'active')
                ->whereNull('uhr.deleted_at_utc')
                ->whereNull('rma.deleted_at_utc')
                ->pluck('rma.menu_item_id')
                ->unique()
                ->toArray();
        });

        if (empty($menuIds)) {
            return [];
        }

        $items = DB::table('menu_items as m')
            ->join('modules as mod', 'mod.id', '=', 'm.module_id')
            ->whereIn('m.id', $menuIds)
            ->where('m.is_active', true)
            ->where('m.status', 'active')
            ->whereNull('m.deleted_at_utc')
            ->whereNull('mod.deleted_at_utc')
            ->orderBy('mod.display_order')
            ->orderBy('m.display_order')
            ->select(
                'm.id',
                'm.parent_id',
                'm.name_en',
                'm.name_native',
                'm.route_path',
                'm.route_name',
                'm.component',
                'm.icon',
                'm.display_order',
                'm.is_external',
                'm.external_url',
                'm.badge_type',
                'm.badge_value',
                'm.permission_code',
                'mod.name_en as module_name',
                'mod.code as module_code'
            )
            ->get()
            ->toArray();

        return $this->buildTree($items);
    }

    /**
     * Get data scope for specific permission
     */
    public function getScope(int $userId, string $code): string
    {
        $scope = DB::table('user_has_roles as uhr')
            ->join('roles as r', 'r.id', '=', 'uhr.role_id')
            ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'r.id')
            ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
            ->where('uhr.user_id', $userId)
            ->where('p.code', $code)
            ->where('uhr.status', 'active')
            ->where('rhp.status', 'active')
            ->whereNull('uhr.deleted_at_utc')
            ->whereNull('rhp.deleted_at_utc')
            ->orderByRaw("FIELD(rhp.scope_type,'global','country','company','department','self_only')")
            ->value('rhp.scope_type');

        return $scope ?? 'self_only';
    }

    /**
     * Get all roles assigned to user
     */
    public function getUserRoles(int $userId): array
    {
        return DB::table('user_has_roles as uhr')
            ->join('roles as r', 'r.id', '=', 'uhr.role_id')
            ->where('uhr.user_id', $userId)
            ->where('uhr.status', 'active')
            ->where('r.status', 'active')
            ->whereNull('uhr.deleted_at_utc')
            ->whereNull('r.deleted_at_utc')
            ->select(
                'r.id',
                'r.name_en',
                'r.name_native',
                'r.code',
                'r.role_scope',
                'r.level',
                'r.scope_country_codes',
                'r.scope_company_ids',
                'r.scope_department_ids',
                'uhr.scope_type as user_scope_type',
                'uhr.scope_company_id',
                'uhr.scope_department_id',
                'uhr.assigned_at_utc'
            )
            ->get()
            ->toArray();
    }

    /**
     * Get user's company ID from role scope
     */
    public function getUserCompanyId(int $userId): ?int
    {
        $companyId = DB::table('user_has_roles as uhr')
            ->join('roles as r', 'r.id', '=', 'uhr.role_id')
            ->where('uhr.user_id', $userId)
            ->where('uhr.status', 'active')
            ->where('r.status', 'active')
            ->whereNotNull('uhr.scope_company_id')
            ->whereNull('uhr.deleted_at_utc')
            ->value('uhr.scope_company_id');

        return $companyId ? (int) $companyId : null;
    }

    /**
     * Clear permission and menu cache for user
     */
    public function clearCache(int $userId): void
    {
        Cache::forget("perms_{$userId}");
        Cache::forget("menu_{$userId}");
    }

    /**
     * Clear cache for all users with specific role
     */
    public function clearRoleCache(int $roleId): void
    {
        DB::table('user_has_roles')
            ->where('role_id', $roleId)
            ->where('status', 'active')
            ->pluck('user_id')
            ->each(fn($uid) => $this->clearCache($uid));
    }

    /**
     * Build nested tree from flat menu items
     */
    private function buildTree(array $items, ?int $parentId = null): array
    {
        $tree = [];
        foreach ($items as $item) {
            $item = (array) $item;
            $pid = !empty($item['parent_id']) ? (int) $item['parent_id'] : null;
            if ($pid === $parentId) {
                $children = $this->buildTree($items, (int) $item['id']);
                if ($children) {
                    $item['children'] = $children;
                }
                $tree[] = $item;
            }
        }
        return $tree;
    }
}