<?php

namespace App\Services;

use App\Models\Commons\Menu;
use App\Models\Tenants\Department;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class PermissionAnalyzeService
{
    /**
     * 권한 유형 목록
     */
    private array $permissionTypes = ['view', 'create', 'update', 'delete', 'approve', 'export', 'manage'];

    /**
     * 메뉴 트리 조회 (권한 분석용)
     *
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string|null  $search  검색어
     * @param  bool  $onlyAccessible  접근 가능한 메뉴만 필터링
     * @return Collection 메뉴 트리
     */
    public function getMenuTree(?int $tenantId = null, ?string $search = null, bool $onlyAccessible = false): Collection
    {
        $query = Menu::with('parent')
            ->where('is_active', 1);

        if ($tenantId) {
            // 특정 테넌트: 해당 테넌트 메뉴만 (각 테넌트별로 메뉴가 복제되어 있음)
            $query->where('tenant_id', $tenantId);
        } else {
            // 전체 보기: 공통 메뉴만
            $query->whereNull('tenant_id');
        }

        if ($search) {
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('url', 'like', "%{$search}%");
            });
        }

        $allMenus = $query->orderBy('sort_order', 'asc')
            ->orderBy('id', 'asc')
            ->get();

        return $this->flattenMenuTree($allMenus);
    }

    /**
     * 트리 구조를 플랫한 배열로 변환 (depth 정보 포함)
     */
    private function flattenMenuTree(Collection $menus, ?int $parentId = null, int $depth = 0): Collection
    {
        $result = collect();
        $filteredMenus = $menus->where('parent_id', $parentId)->sortBy('sort_order');

        foreach ($filteredMenus as $menu) {
            $menu->depth = $depth;
            $menu->has_children = $menus->where('parent_id', $menu->id)->count() > 0;
            $result->push($menu);

            $children = $this->flattenMenuTree($menus, $menu->id, $depth + 1);
            $result = $result->merge($children);
        }

        return $result;
    }

    /**
     * 특정 메뉴의 권한 분석 (접근 가능/불가능 사용자 목록)
     *
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형 (view, create, update, delete, approve)
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름
     * @return array 분석 결과
     */
    public function analyzeMenuPermission(int $menuId, string $permissionType = 'view', ?int $tenantId = null, string $guardName = 'api'): array
    {
        $menu = Menu::find($menuId);
        if (! $menu) {
            return ['error' => '메뉴를 찾을 수 없습니다.'];
        }

        $permissionName = "menu:{$menuId}.{$permissionType}";

        // 테넌트별 사용자 목록 조회
        $users = $this->getUsersByTenant($tenantId);

        $accessAllowed = [];
        $explicitDeny = [];

        foreach ($users as $user) {
            $analysis = $this->analyzeUserPermission($user->id, $menuId, $permissionType, $tenantId, $guardName);

            $userInfo = [
                'id' => $user->id,
                'name' => $user->name,
                'email' => $user->email,
                'departments' => $this->getUserDepartments($user->id, $tenantId),
                'roles' => $this->getUserRoles($user->id, $tenantId),
                'personal_override' => $analysis['personal'],
                'source' => $analysis['source'],
                'effective' => $analysis['effective'],
            ];

            if ($analysis['effective'] === 'allow') {
                $accessAllowed[] = $userInfo;
            } elseif ($analysis['personal'] === 'deny') {
                // 명시적 DENY만 별도 목록에 추가
                $explicitDeny[] = $userInfo;
            }
            // 권한이 없는 사용자(no permission)는 목록에 포함하지 않음
        }

        return [
            'menu' => [
                'id' => $menu->id,
                'name' => $menu->name,
                'url' => $menu->url,
            ],
            'permission_type' => $permissionType,
            'permission_rule' => 'ALLOW = 부서 OR 역할 OR (개인 ALLOW) → 개인 DENY 최우선',
            'access_allowed' => $accessAllowed,
            'explicit_deny' => $explicitDeny,
            'summary' => [
                'total_users' => count($users),
                'allowed_count' => count($accessAllowed),
                'explicit_deny_count' => count($explicitDeny),
            ],
        ];
    }

    /**
     * 특정 사용자의 특정 메뉴 권한 분석
     *
     * @param  int  $userId  사용자 ID
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름
     * @return array 분석 결과
     */
    public function analyzeUserPermission(int $userId, int $menuId, string $permissionType = 'view', ?int $tenantId = null, string $guardName = 'api'): array
    {
        $permissionName = "menu:{$menuId}.{$permissionType}";
        $now = now();

        // 1. 역할 권한 확인
        $hasRolePermission = $this->checkRolePermission($userId, $permissionName, $guardName, $tenantId);

        // 2. 부서 권한 확인
        $hasDeptPermission = $this->checkDepartmentPermission($userId, $permissionName, $tenantId, $guardName);

        // 3. 개인 오버라이드 확인
        $personalOverride = $this->checkPersonalOverride($userId, $permissionName, $tenantId, $guardName);

        // 최종 권한 계산 (AccessService와 동일한 우선순위)
        // 1) 개인 DENY → 거부
        // 2) 역할 권한 → 허용
        // 3) 부서 ALLOW → 허용
        // 4) 개인 ALLOW → 허용
        // 5) 기본 → 없음 (거부)

        $effective = null;
        $source = null;

        if ($personalOverride === 'deny') {
            $effective = 'deny';
            $source = 'personal';
        } elseif ($hasRolePermission) {
            $effective = 'allow';
            $source = 'role';
        } elseif ($hasDeptPermission) {
            $effective = 'allow';
            $source = 'department';
        } elseif ($personalOverride === 'allow') {
            $effective = 'allow';
            $source = 'personal';
        } else {
            $effective = 'deny';
            $source = null;
        }

        return [
            'effective' => $effective,
            'source' => $source,
            'role' => $hasRolePermission,
            'department' => $hasDeptPermission,
            'personal' => $personalOverride,
        ];
    }

    /**
     * 역할 권한 확인 (model_has_roles + user_roles)
     */
    private function checkRolePermission(int $userId, string $permissionName, string $guardName, ?int $tenantId = null): bool
    {
        // 1. Spatie model_has_roles 테이블에서 확인
        $hasSpatiePermission = DB::table('model_has_roles as mhr')
            ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
            ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
            ->where('mhr.model_type', User::class)
            ->where('mhr.model_id', $userId)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->exists();

        if ($hasSpatiePermission) {
            return true;
        }

        // 2. user_roles 테이블에서 확인
        $userRolesQuery = DB::table('user_roles as ur')
            ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'ur.role_id')
            ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
            ->where('ur.user_id', $userId)
            ->whereNull('ur.deleted_at')
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName);

        if ($tenantId) {
            $userRolesQuery->where('ur.tenant_id', $tenantId);
        }

        return $userRolesQuery->exists();
    }

    /**
     * 부서 권한 확인
     */
    private function checkDepartmentPermission(int $userId, string $permissionName, ?int $tenantId, string $guardName): bool
    {
        $now = now();

        $query = DB::table('department_user as du')
            ->join('permission_overrides as po', function ($j) use ($now) {
                $j->on('po.model_id', '=', 'du.department_id')
                    ->where('po.model_type', Department::class)
                    ->whereNull('po.deleted_at')
                    ->where('po.effect', 1)
                    ->where(function ($w) use ($now) {
                        $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
                    })
                    ->where(function ($w) use ($now) {
                        $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
                    });
            })
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->whereNull('du.deleted_at')
            ->where('du.user_id', $userId)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName);

        if ($tenantId) {
            $query->where('du.tenant_id', $tenantId)
                ->where('po.tenant_id', $tenantId);
        }

        return $query->exists();
    }

    /**
     * 개인 오버라이드 확인
     */
    private function checkPersonalOverride(int $userId, string $permissionName, ?int $tenantId, string $guardName): ?string
    {
        $now = now();

        $query = DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->select('po.effect')
            ->where('po.model_type', User::class)
            ->where('po.model_id', $userId)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->whereNull('po.deleted_at')
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
            })
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
            });

        if ($tenantId) {
            $query->where('po.tenant_id', $tenantId);
        }

        $override = $query->first();

        if (! $override) {
            return null;
        }

        return $override->effect == 1 ? 'allow' : 'deny';
    }

    /**
     * 테넌트별 사용자 목록 조회
     */
    private function getUsersByTenant(?int $tenantId): Collection
    {
        $query = User::where('is_active', true);

        // 일반 관리자는 슈퍼관리자를 볼 수 없음
        if (! auth()->user()?->is_super_admin) {
            $query->where('is_super_admin', false);
        }

        if ($tenantId) {
            $query->whereHas('tenants', function ($q) use ($tenantId) {
                $q->where('tenants.id', $tenantId)
                    ->where('user_tenants.is_active', true);
            });
        }

        return $query->orderBy('name')->get();
    }

    /**
     * 사용자의 부서 목록 조회
     */
    private function getUserDepartments(int $userId, ?int $tenantId): array
    {
        $query = DB::table('department_user as du')
            ->join('departments as d', 'd.id', '=', 'du.department_id')
            ->select('d.id', 'd.name', 'd.code', 'du.is_primary')
            ->where('du.user_id', $userId)
            ->whereNull('du.deleted_at')
            ->whereNull('d.deleted_at');

        if ($tenantId) {
            $query->where('du.tenant_id', $tenantId);
        }

        return $query->get()->map(function ($dept) {
            return [
                'id' => $dept->id,
                'name' => $dept->name,
                'code' => $dept->code,
                'is_primary' => $dept->is_primary,
            ];
        })->toArray();
    }

    /**
     * 사용자의 역할 목록 조회 (model_has_roles + user_roles)
     */
    private function getUserRoles(int $userId, ?int $tenantId): array
    {
        // 1. Spatie model_has_roles 테이블에서 조회
        $spatieQuery = DB::table('model_has_roles as mhr')
            ->join('roles as r', 'r.id', '=', 'mhr.role_id')
            ->select('r.id', 'r.name', 'r.description')
            ->where('mhr.model_type', User::class)
            ->where('mhr.model_id', $userId);

        if ($tenantId) {
            $spatieQuery->where('mhr.tenant_id', $tenantId);
        }

        $spatieRoles = $spatieQuery->get();

        // 2. user_roles 테이블에서 조회
        $userRolesQuery = DB::table('user_roles as ur')
            ->join('roles as r', 'r.id', '=', 'ur.role_id')
            ->select('r.id', 'r.name', 'r.description')
            ->where('ur.user_id', $userId)
            ->whereNull('ur.deleted_at');

        if ($tenantId) {
            $userRolesQuery->where('ur.tenant_id', $tenantId);
        }

        $userRoles = $userRolesQuery->get();

        // 3. 두 결과 합치기 (중복 제거)
        return $spatieRoles->merge($userRoles)->unique('id')->map(function ($role) {
            return [
                'id' => $role->id,
                'name' => $role->name,
                'display_name' => $role->description,
            ];
        })->values()->toArray();
    }

    /**
     * 사용자 역추적 - 특정 권한을 가진 모든 사용자 검색
     *
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름
     * @return array 역추적 결과
     */
    public function traceUsersWithPermission(int $menuId, string $permissionType = 'view', ?int $tenantId = null, string $guardName = 'api'): array
    {
        $permissionName = "menu:{$menuId}.{$permissionType}";
        $excludeSuperAdmin = ! auth()->user()?->is_super_admin;

        // 역할로 권한이 있는 사용자 (model_has_roles)
        $usersFromSpatieRoleQuery = DB::table('model_has_roles as mhr')
            ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
            ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
            ->join('users as u', 'u.id', '=', 'mhr.model_id')
            ->join('roles as r', 'r.id', '=', 'mhr.role_id')
            ->select('u.id as user_id', 'u.name as user_name', 'u.email', 'r.id as role_id', 'r.name as role_name', 'r.description as role_display_name')
            ->where('mhr.model_type', User::class)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->where('u.is_active', true);

        if ($excludeSuperAdmin) {
            $usersFromSpatieRoleQuery->where('u.is_super_admin', false);
        }

        // 역할로 권한이 있는 사용자 (user_roles)
        $usersFromUserRolesQuery = DB::table('user_roles as ur')
            ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'ur.role_id')
            ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
            ->join('users as u', 'u.id', '=', 'ur.user_id')
            ->join('roles as r', 'r.id', '=', 'ur.role_id')
            ->select('u.id as user_id', 'u.name as user_name', 'u.email', 'r.id as role_id', 'r.name as role_name', 'r.description as role_display_name')
            ->whereNull('ur.deleted_at')
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->where('u.is_active', true);

        if ($excludeSuperAdmin) {
            $usersFromUserRolesQuery->where('u.is_super_admin', false);
        }

        if ($tenantId) {
            $usersFromUserRolesQuery->where('ur.tenant_id', $tenantId);
        }

        // 두 쿼리 결과 합치기
        $usersFromRole = $usersFromSpatieRoleQuery->get()->merge($usersFromUserRolesQuery->get())->unique('user_id');

        // 부서로 권한이 있는 사용자
        $now = now();
        $usersFromDepartmentQuery = DB::table('department_user as du')
            ->join('permission_overrides as po', function ($j) use ($now) {
                $j->on('po.model_id', '=', 'du.department_id')
                    ->where('po.model_type', Department::class)
                    ->whereNull('po.deleted_at')
                    ->where('po.effect', 1)
                    ->where(function ($w) use ($now) {
                        $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
                    })
                    ->where(function ($w) use ($now) {
                        $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
                    });
            })
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->join('users as u', 'u.id', '=', 'du.user_id')
            ->join('departments as d', 'd.id', '=', 'du.department_id')
            ->select('u.id as user_id', 'u.name as user_name', 'u.email', 'd.id as department_id', 'd.name as department_name', 'd.code as department_code')
            ->whereNull('du.deleted_at')
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->where('u.is_active', true);

        if ($excludeSuperAdmin) {
            $usersFromDepartmentQuery->where('u.is_super_admin', false);
        }

        if ($tenantId) {
            $usersFromDepartmentQuery->where('du.tenant_id', $tenantId)
                ->where('po.tenant_id', $tenantId);
        }

        $usersFromDepartment = $usersFromDepartmentQuery->get();

        // 개인 ALLOW 오버라이드가 있는 사용자
        $usersFromPersonalQuery = DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->join('users as u', 'u.id', '=', 'po.model_id')
            ->select('u.id as user_id', 'u.name as user_name', 'u.email', 'po.effect')
            ->where('po.model_type', User::class)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->where('po.effect', 1)
            ->whereNull('po.deleted_at')
            ->where('u.is_active', true)
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
            })
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
            });

        if ($excludeSuperAdmin) {
            $usersFromPersonalQuery->where('u.is_super_admin', false);
        }

        if ($tenantId) {
            $usersFromPersonalQuery->where('po.tenant_id', $tenantId);
        }

        $usersFromPersonal = $usersFromPersonalQuery->get();

        // 개인 DENY 오버라이드가 있는 사용자
        $usersWithDenyQuery = DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->join('users as u', 'u.id', '=', 'po.model_id')
            ->select('u.id as user_id', 'u.name as user_name', 'u.email')
            ->where('po.model_type', User::class)
            ->where('p.guard_name', $guardName)
            ->where('p.name', $permissionName)
            ->where('po.effect', 0)
            ->whereNull('po.deleted_at')
            ->where('u.is_active', true)
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
            })
            ->where(function ($w) use ($now) {
                $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
            });

        if ($excludeSuperAdmin) {
            $usersWithDenyQuery->where('u.is_super_admin', false);
        }

        if ($tenantId) {
            $usersWithDenyQuery->where('po.tenant_id', $tenantId);
        }

        $usersWithDeny = $usersWithDenyQuery->get();

        return [
            'by_role' => $usersFromRole->map(function ($item) {
                return [
                    'user_id' => $item->user_id,
                    'user_name' => $item->user_name,
                    'email' => $item->email,
                    'role' => [
                        'id' => $item->role_id,
                        'name' => $item->role_name,
                        'display_name' => $item->role_display_name,
                    ],
                ];
            })->toArray(),
            'by_department' => $usersFromDepartment->map(function ($item) {
                return [
                    'user_id' => $item->user_id,
                    'user_name' => $item->user_name,
                    'email' => $item->email,
                    'department' => [
                        'id' => $item->department_id,
                        'name' => $item->department_name,
                        'code' => $item->department_code,
                    ],
                ];
            })->toArray(),
            'by_personal' => $usersFromPersonal->map(function ($item) {
                return [
                    'user_id' => $item->user_id,
                    'user_name' => $item->user_name,
                    'email' => $item->email,
                ];
            })->toArray(),
            'denied_users' => $usersWithDeny->map(function ($item) {
                return [
                    'user_id' => $item->user_id,
                    'user_name' => $item->user_name,
                    'email' => $item->email,
                ];
            })->toArray(),
        ];
    }

    /**
     * 권한 유형 목록 반환
     */
    public function getPermissionTypes(): array
    {
        return $this->permissionTypes;
    }

    /**
     * CSV 내보내기용 데이터 생성
     */
    public function exportToCsv(int $menuId, string $permissionType = 'view', ?int $tenantId = null): string
    {
        $analysis = $this->analyzeMenuPermission($menuId, $permissionType, $tenantId);

        $csv = "사용자,이메일,부서,역할,개인모드,최종,근거\n";

        foreach ($analysis['access_allowed'] as $user) {
            $departments = collect($user['departments'])->pluck('name')->join(', ');
            $roles = collect($user['roles'])->pluck('display_name')->join(', ');
            $csv .= "\"{$user['name']}\",\"{$user['email']}\",\"{$departments}\",\"{$roles}\",\"{$user['personal_override']}\",\"허용\",\"{$user['source']}\"\n";
        }

        foreach ($analysis['explicit_deny'] as $user) {
            $departments = collect($user['departments'])->pluck('name')->join(', ');
            $roles = collect($user['roles'])->pluck('display_name')->join(', ');
            $csv .= "\"{$user['name']}\",\"{$user['email']}\",\"{$departments}\",\"{$roles}\",\"{$user['personal_override']}\",\"명시적 DENY\",\"{$user['source']}\"\n";
        }

        return $csv;
    }
}
