<?php

namespace App\Services;

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

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

    /**
     * 부서의 권한 매트릭스 조회 (permission_overrides 테이블 사용)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     * @return array 메뉴별 권한 상태 매트릭스
     */
    public function getDepartmentPermissionMatrix(int $departmentId, ?int $tenantId = null, string $guardName = 'api'): array
    {
        $now = now();

        $query = DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->where('po.model_type', Department::class)
            ->where('po.model_id', $departmentId)
            ->where('po.effect', 1) // ALLOW만 조회
            ->where('p.guard_name', $guardName)
            ->where('p.name', 'like', 'menu:%')
            ->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);
        }

        $departmentPermissions = $query->pluck('p.name')->toArray();

        $permissions = [];
        foreach ($departmentPermissions as $permName) {
            if (preg_match('/^menu:(\d+)\.(\w+)$/', $permName, $matches)) {
                $menuId = (int) $matches[1];
                $type = $matches[2];

                if (! isset($permissions[$menuId])) {
                    $permissions[$menuId] = [];
                }

                $permissions[$menuId][$type] = true;
            }
        }

        return $permissions;
    }

    /**
     * 특정 메뉴의 특정 권한 토글 (permission_overrides 테이블 사용)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     * @return bool 토글 후 상태 (true: 허용, false: 거부)
     */
    public function togglePermission(int $departmentId, int $menuId, string $permissionType, ?int $tenantId = null, string $guardName = 'api'): bool
    {
        $permissionName = "menu:{$menuId}.{$permissionType}";

        // 권한 생성 또는 조회
        $permission = Permission::firstOrCreate(
            ['name' => $permissionName, 'guard_name' => $guardName],
            ['tenant_id' => null, 'created_by' => auth()->id()]
        );

        $now = now();

        // 현재 권한 상태 확인 (유효한 ALLOW 오버라이드가 있는지)
        $exists = DB::table('permission_overrides')
            ->where('model_type', Department::class)
            ->where('model_id', $departmentId)
            ->where('permission_id', $permission->id)
            ->where('tenant_id', $tenantId)
            ->where('effect', 1)
            ->whereNull('deleted_at')
            ->where(function ($w) use ($now) {
                $w->whereNull('effective_from')->orWhere('effective_from', '<=', $now);
            })
            ->where(function ($w) use ($now) {
                $w->whereNull('effective_to')->orWhere('effective_to', '>=', $now);
            })
            ->exists();

        if ($exists) {
            // 권한 제거 (soft delete)
            DB::table('permission_overrides')
                ->where('model_type', Department::class)
                ->where('model_id', $departmentId)
                ->where('permission_id', $permission->id)
                ->where('tenant_id', $tenantId)
                ->where('effect', 1)
                ->update([
                    'deleted_at' => now(),
                    'deleted_by' => auth()->id(),
                ]);

            $newValue = false;
        } else {
            // 기존에 삭제된 레코드가 있으면 복원, 없으면 생성
            $existingRecord = DB::table('permission_overrides')
                ->where('model_type', Department::class)
                ->where('model_id', $departmentId)
                ->where('permission_id', $permission->id)
                ->where('tenant_id', $tenantId)
                ->where('effect', 1)
                ->first();

            if ($existingRecord) {
                // 기존 레코드 복원
                DB::table('permission_overrides')
                    ->where('id', $existingRecord->id)
                    ->update([
                        'deleted_at' => null,
                        'deleted_by' => null,
                        'updated_at' => now(),
                        'updated_by' => auth()->id(),
                    ]);
            } else {
                // 새 레코드 생성
                DB::table('permission_overrides')->insert([
                    'tenant_id' => $tenantId,
                    'model_type' => Department::class,
                    'model_id' => $departmentId,
                    'permission_id' => $permission->id,
                    'effect' => 1, // ALLOW
                    'reason' => null,
                    'effective_from' => null,
                    'effective_to' => null,
                    'created_at' => now(),
                    'created_by' => auth()->id(),
                    'updated_at' => now(),
                    'updated_by' => auth()->id(),
                ]);
            }

            $newValue = true;
        }

        // 하위 부서에 권한 전파
        $this->propagateToChildren($departmentId, $menuId, $permissionType, $newValue, $tenantId, $guardName);

        return $newValue;
    }

    /**
     * 하위 부서에 권한 전파 (permission_overrides 테이블 사용)
     *
     * @param  int  $parentDepartmentId  부모 부서 ID
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형
     * @param  bool  $value  권한 값 (true: 허용, false: 거부)
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     */
    protected function propagateToChildren(int $parentDepartmentId, int $menuId, string $permissionType, bool $value, ?int $tenantId = null, string $guardName = 'api'): void
    {
        $children = Department::where('parent_id', $parentDepartmentId)->get();
        $now = now();

        foreach ($children as $child) {
            $permissionName = "menu:{$menuId}.{$permissionType}";
            $permission = Permission::firstOrCreate(
                ['name' => $permissionName, 'guard_name' => $guardName],
                ['tenant_id' => null, 'created_by' => auth()->id()]
            );

            // 현재 권한 상태 확인 (유효한 ALLOW 오버라이드가 있는지)
            $exists = DB::table('permission_overrides')
                ->where('model_type', Department::class)
                ->where('model_id', $child->id)
                ->where('permission_id', $permission->id)
                ->where('tenant_id', $tenantId)
                ->where('effect', 1)
                ->whereNull('deleted_at')
                ->where(function ($w) use ($now) {
                    $w->whereNull('effective_from')->orWhere('effective_from', '<=', $now);
                })
                ->where(function ($w) use ($now) {
                    $w->whereNull('effective_to')->orWhere('effective_to', '>=', $now);
                })
                ->exists();

            if ($value) {
                // 권한 추가
                if (! $exists) {
                    // 기존에 삭제된 레코드가 있으면 복원, 없으면 생성
                    $existingRecord = DB::table('permission_overrides')
                        ->where('model_type', Department::class)
                        ->where('model_id', $child->id)
                        ->where('permission_id', $permission->id)
                        ->where('tenant_id', $tenantId)
                        ->where('effect', 1)
                        ->first();

                    if ($existingRecord) {
                        DB::table('permission_overrides')
                            ->where('id', $existingRecord->id)
                            ->update([
                                'deleted_at' => null,
                                'deleted_by' => null,
                                'updated_at' => now(),
                                'updated_by' => auth()->id(),
                            ]);
                    } else {
                        DB::table('permission_overrides')->insert([
                            'tenant_id' => $tenantId,
                            'model_type' => Department::class,
                            'model_id' => $child->id,
                            'permission_id' => $permission->id,
                            'effect' => 1, // ALLOW
                            'reason' => null,
                            'effective_from' => null,
                            'effective_to' => null,
                            'created_at' => now(),
                            'created_by' => auth()->id(),
                            'updated_at' => now(),
                            'updated_by' => auth()->id(),
                        ]);
                    }
                }
            } else {
                // 권한 제거 (soft delete)
                if ($exists) {
                    DB::table('permission_overrides')
                        ->where('model_type', Department::class)
                        ->where('model_id', $child->id)
                        ->where('permission_id', $permission->id)
                        ->where('tenant_id', $tenantId)
                        ->where('effect', 1)
                        ->update([
                            'deleted_at' => now(),
                            'deleted_by' => auth()->id(),
                        ]);
                }
            }

            // 재귀적으로 하위 부서 처리
            $this->propagateToChildren($child->id, $menuId, $permissionType, $value, $tenantId, $guardName);
        }
    }

    /**
     * 모든 권한 허용 (permission_overrides 테이블 사용)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     */
    public function allowAllPermissions(int $departmentId, ?int $tenantId = null, string $guardName = 'api'): void
    {
        $query = Menu::where('is_active', 1);
        if ($tenantId) {
            $query->where('tenant_id', $tenantId);
        }
        $menus = $query->get();

        $now = now();

        foreach ($menus as $menu) {
            foreach ($this->permissionTypes as $type) {
                $permissionName = "menu:{$menu->id}.{$type}";
                $permission = Permission::firstOrCreate(
                    ['name' => $permissionName, 'guard_name' => $guardName],
                    ['tenant_id' => null, 'created_by' => auth()->id()]
                );

                // 이미 유효한 ALLOW 오버라이드가 있는지 확인
                $exists = DB::table('permission_overrides')
                    ->where('model_type', Department::class)
                    ->where('model_id', $departmentId)
                    ->where('permission_id', $permission->id)
                    ->where('tenant_id', $tenantId)
                    ->where('effect', 1)
                    ->whereNull('deleted_at')
                    ->where(function ($w) use ($now) {
                        $w->whereNull('effective_from')->orWhere('effective_from', '<=', $now);
                    })
                    ->where(function ($w) use ($now) {
                        $w->whereNull('effective_to')->orWhere('effective_to', '>=', $now);
                    })
                    ->exists();

                if (! $exists) {
                    // 기존에 삭제된 레코드가 있으면 복원, 없으면 생성
                    $existingRecord = DB::table('permission_overrides')
                        ->where('model_type', Department::class)
                        ->where('model_id', $departmentId)
                        ->where('permission_id', $permission->id)
                        ->where('tenant_id', $tenantId)
                        ->where('effect', 1)
                        ->first();

                    if ($existingRecord) {
                        DB::table('permission_overrides')
                            ->where('id', $existingRecord->id)
                            ->update([
                                'deleted_at' => null,
                                'deleted_by' => null,
                                'updated_at' => now(),
                                'updated_by' => auth()->id(),
                            ]);
                    } else {
                        DB::table('permission_overrides')->insert([
                            'tenant_id' => $tenantId,
                            'model_type' => Department::class,
                            'model_id' => $departmentId,
                            'permission_id' => $permission->id,
                            'effect' => 1, // ALLOW
                            'reason' => null,
                            'effective_from' => null,
                            'effective_to' => null,
                            'created_at' => now(),
                            'created_by' => auth()->id(),
                            'updated_at' => now(),
                            'updated_by' => auth()->id(),
                        ]);
                    }
                }
            }
        }
    }

    /**
     * 모든 권한 거부 (permission_overrides 테이블에서 삭제)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     */
    public function denyAllPermissions(int $departmentId, ?int $tenantId = null, string $guardName = 'api'): void
    {
        // menu: 접두사를 가진 해당 가드의 모든 권한 ID 조회
        $query = DB::table('permissions')
            ->where('guard_name', $guardName)
            ->where('name', 'like', 'menu:%');

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

        $permissionIds = $query->pluck('id');

        if ($permissionIds->isNotEmpty()) {
            // Soft delete all ALLOW overrides for this department
            DB::table('permission_overrides')
                ->where('model_type', Department::class)
                ->where('model_id', $departmentId)
                ->whereIn('permission_id', $permissionIds)
                ->where('tenant_id', $tenantId)
                ->where('effect', 1)
                ->whereNull('deleted_at')
                ->update([
                    'deleted_at' => now(),
                    'deleted_by' => auth()->id(),
                ]);
        }
    }

    /**
     * 기본 권한으로 초기화 (view만 허용)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int|null  $tenantId  테넌트 ID
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     */
    public function resetToDefaultPermissions(int $departmentId, ?int $tenantId = null, string $guardName = 'api'): void
    {
        // 1. 먼저 모든 권한 제거
        $this->denyAllPermissions($departmentId, $tenantId, $guardName);

        // 2. view 권한만 허용
        $query = Menu::where('is_active', 1);
        if ($tenantId) {
            $query->where('tenant_id', $tenantId);
        }
        $menus = $query->get();

        $now = now();

        foreach ($menus as $menu) {
            $permissionName = "menu:{$menu->id}.view";
            $permission = Permission::firstOrCreate(
                ['name' => $permissionName, 'guard_name' => $guardName],
                ['tenant_id' => null, 'created_by' => auth()->id()]
            );

            // 이미 유효한 ALLOW 오버라이드가 있는지 확인
            $exists = DB::table('permission_overrides')
                ->where('model_type', Department::class)
                ->where('model_id', $departmentId)
                ->where('permission_id', $permission->id)
                ->where('tenant_id', $tenantId)
                ->where('effect', 1)
                ->whereNull('deleted_at')
                ->where(function ($w) use ($now) {
                    $w->whereNull('effective_from')->orWhere('effective_from', '<=', $now);
                })
                ->where(function ($w) use ($now) {
                    $w->whereNull('effective_to')->orWhere('effective_to', '>=', $now);
                })
                ->exists();

            if (! $exists) {
                // 기존에 삭제된 레코드가 있으면 복원, 없으면 생성
                $existingRecord = DB::table('permission_overrides')
                    ->where('model_type', Department::class)
                    ->where('model_id', $departmentId)
                    ->where('permission_id', $permission->id)
                    ->where('tenant_id', $tenantId)
                    ->where('effect', 1)
                    ->first();

                if ($existingRecord) {
                    DB::table('permission_overrides')
                        ->where('id', $existingRecord->id)
                        ->update([
                            'deleted_at' => null,
                            'deleted_by' => null,
                            'updated_at' => now(),
                            'updated_by' => auth()->id(),
                        ]);
                } else {
                    DB::table('permission_overrides')->insert([
                        'tenant_id' => $tenantId,
                        'model_type' => Department::class,
                        'model_id' => $departmentId,
                        'permission_id' => $permission->id,
                        'effect' => 1, // ALLOW
                        'reason' => null,
                        'effective_from' => null,
                        'effective_to' => null,
                        'created_at' => now(),
                        'created_by' => auth()->id(),
                        'updated_at' => now(),
                        'updated_by' => auth()->id(),
                    ]);
                }
            }
        }
    }

    /**
     * 메뉴 트리 조회 (권한 매트릭스 표시용)
     *
     * @param  int|null  $tenantId  테넌트 ID
     * @return \Illuminate\Support\Collection 메뉴 트리
     */
    public function getMenuTree(?int $tenantId = null): \Illuminate\Support\Collection
    {
        $query = Menu::with('parent')
            ->where('is_active', 1);

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

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

        // depth 계산하여 플랫한 구조로 변환
        return $this->flattenMenuTree($allMenus);
    }

    /**
     * 트리 구조를 플랫한 배열로 변환 (depth 정보 포함)
     *
     * @param  \Illuminate\Support\Collection  $menus  메뉴 컬렉션
     * @param  int|null  $parentId  부모 메뉴 ID
     * @param  int  $depth  현재 깊이
     */
    private function flattenMenuTree(\Illuminate\Support\Collection $menus, ?int $parentId = null, int $depth = 0): \Illuminate\Support\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;
    }

    /**
     * 특정 부서의 활성 메뉴 권한 확인 (permission_overrides 테이블 사용)
     *
     * @param  int  $departmentId  부서 ID
     * @param  int  $menuId  메뉴 ID
     * @param  string  $permissionType  권한 유형
     * @param  string  $guardName  Guard 이름 (api 또는 web)
     * @return bool 권한 존재 여부
     */
    public function hasPermission(int $departmentId, int $menuId, string $permissionType, string $guardName = 'api'): bool
    {
        $permissionName = "menu:{$menuId}.{$permissionType}";
        $now = now();

        return DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->where('po.model_type', Department::class)
            ->where('po.model_id', $departmentId)
            ->where('po.effect', 1)
            ->where('p.name', $permissionName)
            ->where('p.guard_name', $guardName)
            ->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);
            })
            ->exists();
    }
}
