<?php

namespace App\Services;

use App\Models\Archives\ArchivedRecord;
use App\Models\Archives\ArchivedRecordRelation;
use App\Models\Commons\GlobalMenu;
use App\Models\Commons\Menu;
use App\Models\Permission;
use App\Models\Tenants\Tenant;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class MenuService
{
    // =========================================================================
    // 게시판 메뉴 연동 헬퍼
    // =========================================================================

    /**
     * 게시판 연동 URL 패턴인지 확인
     * - /customer-center/* : 시스템 게시판 (고객센터)
     * - /boards/* : 테넌트 게시판
     */
    public function isBoardMenuUrl(?string $url): bool
    {
        if (empty($url)) {
            return false;
        }

        return str_starts_with($url, '/customer-center/') || str_starts_with($url, '/boards/');
    }

    /**
     * 게시판 연동 메뉴인지 확인 (Menu 또는 GlobalMenu)
     */
    public function isBoardMenu(Menu|GlobalMenu $menu): bool
    {
        return $this->isBoardMenuUrl($menu->url);
    }

    /**
     * 게시판 메뉴 URL 수동 생성/수정 방지 검증
     *
     * @throws \InvalidArgumentException 게시판 URL 패턴 사용 시
     */
    public function validateNotBoardUrl(?string $url): void
    {
        if ($this->isBoardMenuUrl($url)) {
            throw new \InvalidArgumentException(
                '게시판 연동 URL 패턴(/customer-center/*, /boards/*)은 직접 사용할 수 없습니다. 게시판 관리에서 생성해주세요.'
            );
        }
    }

    // =========================================================================
    // 메뉴 목록 조회
    // =========================================================================

    /**
     * 메뉴 목록 조회 (페이지네이션) - 트리 구조로 정렬
     */
    public function getMenus(array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $tenantId = session('selected_tenant_id');
        $query = Menu::query()->withTrashed();

        // 테넌트 필터링
        if ($tenantId) {
            // 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
            $query->where('tenant_id', $tenantId);
        } else {
            // 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
            $query->whereNull('tenant_id');
        }

        // Soft Delete 필터
        if (isset($filters['trashed'])) {
            if ($filters['trashed'] === 'only') {
                $query->onlyTrashed();
            } elseif ($filters['trashed'] === 'with') {
                $query->withTrashed();
            }
        }

        // 검색 필터
        if (! empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('url', 'like', "%{$search}%");
            });
        }

        // 활성 상태 필터
        if (isset($filters['is_active'])) {
            $query->where('is_active', $filters['is_active']);
        }

        // 부모 메뉴 필터 (트리 구조)
        if (isset($filters['parent_id'])) {
            if ($filters['parent_id'] === 'null') {
                $query->whereNull('parent_id');
            } else {
                $query->where('parent_id', $filters['parent_id']);
            }
        }

        // 모든 메뉴 가져오기 (트리 구조 정렬을 위해)
        $allMenus = $query->with(['parent', 'tenant'])->orderBy('sort_order')->orderBy('id')->get();

        // 트리 구조로 정렬 후 플랫한 배열로 변환
        $flattenedMenus = $this->flattenMenuTree($allMenus);

        // 수동 페이지네이션
        $currentPage = request()->input('page', 1);
        $offset = ($currentPage - 1) * $perPage;
        $items = $flattenedMenus->slice($offset, $perPage)->values();

        return new \Illuminate\Pagination\LengthAwarePaginator(
            $items,
            $flattenedMenus->count(),
            $perPage,
            $currentPage,
            ['path' => request()->url(), 'query' => request()->query()]
        );
    }

    /**
     * 트리 구조를 플랫한 배열로 변환 (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;
    }

    /**
     * 메뉴 상세 조회
     */
    public function getMenuById(int $id): ?Menu
    {
        return Menu::with(['parent', 'children'])->find($id);
    }

    /**
     * 메뉴 트리 구조로 조회 (전체)
     */
    public function getMenuTree(?int $tenantId = null): Collection
    {
        $tenantId = $tenantId ?? session('selected_tenant_id');

        $query = Menu::query()
            ->where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('id');

        if ($tenantId) {
            // 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
            $query->where('tenant_id', $tenantId);
        } else {
            // 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
            $query->whereNull('tenant_id');
        }

        $allMenus = $query->get();

        // 부모 메뉴만 필터링하고 자식 메뉴를 재귀적으로 연결
        return $allMenus->where('parent_id', null)->map(function ($menu) use ($allMenus) {
            $menu->children = $this->buildChildren($menu, $allMenus);

            return $menu;
        });
    }

    /**
     * 재귀적으로 자식 메뉴 구성
     */
    private function buildChildren(Menu $parent, Collection $allMenus): Collection
    {
        $children = $allMenus->where('parent_id', $parent->id);

        return $children->map(function ($child) use ($allMenus) {
            $child->children = $this->buildChildren($child, $allMenus);

            return $child;
        });
    }

    /**
     * 부모 메뉴 목록 조회 (드롭다운용) - 트리 구조 순서로 정렬, depth 정보 포함
     */
    public function getParentMenus(?int $tenantId = null): Collection
    {
        $tenantId = $tenantId ?? session('selected_tenant_id');

        $query = Menu::query()
            ->where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('id');

        if ($tenantId) {
            // 특정 테넌트 선택 시: 해당 테넌트의 메뉴만
            $query->where('tenant_id', $tenantId);
        } else {
            // 전체 선택 시: tenant_id가 NULL인 마스터 메뉴만
            $query->whereNull('tenant_id');
        }

        $allMenus = $query->get();

        // 트리 구조로 정렬 (depth 정보 포함)
        return $this->flattenMenuTree($allMenus);
    }

    /**
     * 메뉴 생성
     *
     * @throws \InvalidArgumentException 게시판 URL 패턴 사용 시
     */
    public function createMenu(array $data): Menu
    {
        // 게시판 URL 패턴 수동 생성 방지
        $this->validateNotBoardUrl($data['url'] ?? null);

        $tenantId = session('selected_tenant_id');

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // hidden 처리
        $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';

        // is_external 처리
        $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';

        // 생성자 정보
        $data['created_by'] = auth()->id();

        // 테넌트 정보
        if ($tenantId) {
            $data['tenant_id'] = $tenantId;
        }

        // parent_id null 처리
        if (empty($data['parent_id'])) {
            $data['parent_id'] = null;
        }

        return Menu::create($data);
    }

    /**
     * 메뉴 수정
     *
     * @throws \InvalidArgumentException 게시판 메뉴 수정 또는 게시판 URL 패턴 사용 시
     */
    public function updateMenu(int $id, array $data): bool
    {
        $menu = $this->getMenuById($id);
        if (! $menu) {
            return false;
        }

        // 게시판 연동 메뉴는 수정 불가 (활성/숨김 토글만 허용)
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 수정할 수 없습니다. 게시판 관리에서 수정해주세요.'
            );
        }

        // 새 URL이 게시판 URL 패턴이면 거부
        $this->validateNotBoardUrl($data['url'] ?? null);

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // hidden 처리
        $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';

        // is_external 처리
        $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';

        // 수정자 정보
        $data['updated_by'] = auth()->id();

        // parent_id null 처리
        if (empty($data['parent_id'])) {
            $data['parent_id'] = null;
        }

        // 자기 자신을 부모로 설정하는 것 방지
        if (isset($data['parent_id']) && $data['parent_id'] == $id) {
            return false;
        }

        return $menu->update($data);
    }

    /**
     * 메뉴 삭제 (Soft Delete)

     *
     * @throws \InvalidArgumentException 게시판 메뉴 삭제 시도 시
     */
    public function deleteMenu(int $id): bool
    {
        $menu = $this->getMenuById($id);
        if (! $menu) {
            return false;
        }

        // 게시판 연동 메뉴는 삭제 불가
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 삭제할 수 없습니다. 게시판 관리에서 삭제해주세요.'
            );
        }

        // 자식 메뉴가 있는 경우 삭제 불가
        if ($menu->children()->count() > 0) {
            return false;
        }

        $menu->deleted_by = auth()->id();
        $menu->save();

        return $menu->delete();
    }

    /**
     * 메뉴 복원
     */
    public function restoreMenu(int $id): bool
    {
        $menu = Menu::onlyTrashed()->findOrFail($id);

        return $menu->restore();
    }

    /**
     * 메뉴 영구 삭제 (슈퍼관리자 전용)
     * - 연관 권한(permissions)도 함께 삭제
     * - role_has_permissions, model_has_permissions는 FK CASCADE로 자동 삭제
     * - 삭제 정보를 archived_records에 저장
     *
     * @return array{success: bool, message: string, deleted_permissions: array}
     *
     * @throws \InvalidArgumentException 게시판 메뉴 영구 삭제 시도 시
     */
    public function forceDeleteMenu(int $id): array
    {
        $menu = Menu::withTrashed()->findOrFail($id);

        // 게시판 연동 메뉴는 영구 삭제 불가
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 영구 삭제할 수 없습니다. 게시판 관리에서 삭제해주세요.'
            );
        }

        // 자식 메뉴가 있는 경우 영구 삭제 불가
        if ($menu->children()->withTrashed()->count() > 0) {
            return [
                'success' => false,
                'message' => '자식 메뉴가 있어 삭제할 수 없습니다.',
                'deleted_permissions' => [],
            ];
        }

        return DB::transaction(function () use ($menu) {
            // 연관 권한 조회 (삭제 전 기록용)
            $permissions = Permission::where('name', 'like', "menu:{$menu->id}.%")->get();
            $permissionData = $permissions->map(fn ($p) => [
                'id' => $p->id,
                'name' => $p->name,
                'guard_name' => $p->guard_name,
                'tenant_id' => $p->tenant_id,
            ])->toArray();

            // 역할-권한 연결 정보 조회
            $rolePermissions = DB::table('role_has_permissions')
                ->join('roles', 'role_has_permissions.role_id', '=', 'roles.id')
                ->whereIn('role_has_permissions.permission_id', $permissions->pluck('id'))
                ->select('roles.id as role_id', 'roles.name as role_name', 'role_has_permissions.permission_id')
                ->get()
                ->toArray();

            // 아카이브 레코드 생성
            $batchId = (string) Str::uuid();
            $archivedRecord = ArchivedRecord::create([
                'batch_id' => $batchId,
                'batch_description' => "메뉴 영구 삭제: {$menu->name} (ID: {$menu->id})",
                'record_type' => 'menu',
                'tenant_id' => $menu->tenant_id,
                'original_id' => $menu->id,
                'main_data' => $menu->toArray(),
                'schema_version' => '1.0',
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
                'notes' => "메뉴 영구 삭제 - 연관 권한 {$permissions->count()}개 함께 삭제",
            ]);

            // 연관 권한 정보 저장
            if ($permissions->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'permissions',
                    'data' => $permissionData,
                    'record_count' => $permissions->count(),
                ]);
            }

            // 역할-권한 연결 정보 저장
            if (! empty($rolePermissions)) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'role_has_permissions',
                    'data' => $rolePermissions,
                    'record_count' => count($rolePermissions),
                ]);
            }

            // 연관 권한 삭제 (FK CASCADE로 role_has_permissions, model_has_permissions 자동 삭제)
            Permission::where('name', 'like', "menu:{$menu->id}.%")->delete();

            // 메뉴 영구 삭제
            $menu->forceDelete();

            return [
                'success' => true,
                'message' => "메뉴 '{$menu->name}'와 연관 권한 {$permissions->count()}개가 삭제되었습니다.",
                'deleted_permissions' => $permissionData,
                'batch_id' => $batchId,
            ];
        });
    }

    /**
     * 메뉴 활성 상태 토글
     */
    public function toggleActive(int $id): bool
    {
        $menu = $this->getMenuById($id);
        if (! $menu) {
            return false;
        }

        $menu->is_active = ! $menu->is_active;
        $menu->updated_by = auth()->id();

        return $menu->save();
    }

    /**
     * 메뉴 숨김 상태 토글
     */
    public function toggleHidden(int $id): bool
    {
        $menu = $this->getMenuById($id);
        if (! $menu) {
            return false;
        }

        $menu->hidden = ! $menu->hidden;
        $menu->updated_by = auth()->id();

        return $menu->save();
    }

    /**
     * 메뉴 순서 변경 (드래그앤드롭)
     * 같은 parent_id 내에서만 순서 변경
     */
    public function reorderMenus(array $items): bool
    {
        return \DB::transaction(function () use ($items) {
            foreach ($items as $item) {
                Menu::where('id', $item['id'])
                    ->update([
                        'sort_order' => $item['sort_order'],
                        'updated_by' => auth()->id(),
                    ]);
            }

            return true;
        });
    }

    /**
     * 메뉴 이동 (계층 구조 변경)
     * - 다른 부모 아래로 이동 가능
     * - 하위 메뉴는 자동으로 따라감
     * - 순환 참조 방지
     */
    public function moveMenu(int $menuId, ?int $newParentId, int $sortOrder): array
    {
        $menu = Menu::find($menuId);
        if (! $menu) {
            return ['success' => false, 'message' => '메뉴를 찾을 수 없습니다.'];
        }

        // 순환 참조 방지: 자신의 하위 메뉴로 이동 불가
        if ($newParentId !== null && $this->isDescendant($menuId, $newParentId)) {
            return ['success' => false, 'message' => '자신의 하위 메뉴로 이동할 수 없습니다.'];
        }

        // 자기 자신을 부모로 설정 방지
        if ($newParentId === $menuId) {
            return ['success' => false, 'message' => '자기 자신을 부모로 설정할 수 없습니다.'];
        }

        return \DB::transaction(function () use ($menu, $newParentId, $sortOrder) {
            $oldParentId = $menu->parent_id;

            // 부모 변경
            $menu->parent_id = $newParentId;
            $menu->sort_order = $sortOrder;
            $menu->updated_by = auth()->id();
            $menu->save();

            // 같은 부모의 다른 메뉴들 순서 재정렬
            $this->reorderSiblings($newParentId, $menu->id, $sortOrder);

            // 이전 부모의 메뉴들도 순서 재정렬 (빈 자리 채우기)
            if ($oldParentId !== $newParentId) {
                $this->compactSiblings($oldParentId);
            }

            return ['success' => true, 'message' => '메뉴가 이동되었습니다.'];
        });
    }

    /**
     * 특정 메뉴가 다른 메뉴의 하위인지 확인 (순환 참조 방지)
     */
    private function isDescendant(int $ancestorId, int $menuId): bool
    {
        $menu = Menu::find($menuId);
        while ($menu) {
            if ($menu->id === $ancestorId) {
                return true;
            }
            $menu = $menu->parent;
        }

        return false;
    }

    /**
     * 같은 부모의 형제 메뉴들 순서 재정렬
     */
    private function reorderSiblings(?int $parentId, int $excludeId, int $insertAt): void
    {
        $siblings = Menu::where('parent_id', $parentId)
            ->where('id', '!=', $excludeId)
            ->orderBy('sort_order')
            ->get();

        $order = 1;
        foreach ($siblings as $sibling) {
            if ($order === $insertAt) {
                $order++; // 삽입 위치 건너뛰기
            }
            if ($sibling->sort_order !== $order) {
                $sibling->update(['sort_order' => $order]);
            }
            $order++;
        }
    }

    /**
     * 형제 메뉴들 순서 압축 (빈 자리 채우기)
     */
    private function compactSiblings(?int $parentId): void
    {
        $siblings = Menu::where('parent_id', $parentId)
            ->orderBy('sort_order')
            ->get();

        $order = 1;
        foreach ($siblings as $sibling) {
            if ($sibling->sort_order !== $order) {
                $sibling->update(['sort_order' => $order]);
            }
            $order++;
        }
    }

    /**
     * 글로벌 부모 메뉴 목록 조회 (드롭다운용) - 트리 구조 순서로 정렬, depth 정보 포함
     */
    public function getGlobalParentMenus(): Collection
    {
        $allMenus = GlobalMenu::query()
            ->where('is_active', true)
            ->orderBy('sort_order')
            ->orderBy('id')
            ->get();

        // 트리 구조로 정렬 (depth 정보 포함)
        return $this->flattenMenuTree($allMenus);
    }

    /**
     * 글로벌 메뉴 상세 조회
     */
    public function getGlobalMenuById(int $id): ?GlobalMenu
    {
        return GlobalMenu::with(['parent', 'children'])->find($id);
    }

    /**
     * 글로벌 메뉴 목록 조회 (페이지네이션) - 트리 구조로 정렬
     */
    public function getGlobalMenus(array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $query = GlobalMenu::query()->withTrashed();

        // Soft Delete 필터
        if (isset($filters['trashed'])) {
            if ($filters['trashed'] === 'only') {
                $query->onlyTrashed();
            } elseif ($filters['trashed'] === 'with') {
                $query->withTrashed();
            }
        }

        // 검색 필터
        if (! empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('url', 'like', "%{$search}%");
            });
        }

        // 활성 상태 필터
        if (isset($filters['is_active'])) {
            $query->where('is_active', $filters['is_active']);
        }

        // 모든 메뉴 가져오기 (트리 구조 정렬을 위해)
        $allMenus = $query->with(['parent'])->orderBy('sort_order')->orderBy('id')->get();

        // 트리 구조로 정렬 후 플랫한 배열로 변환
        $flattenedMenus = $this->flattenMenuTree($allMenus);

        // 수동 페이지네이션
        $currentPage = request()->input('page', 1);
        $offset = ($currentPage - 1) * $perPage;
        $items = $flattenedMenus->slice($offset, $perPage)->values();

        return new \Illuminate\Pagination\LengthAwarePaginator(
            $items,
            $flattenedMenus->count(),
            $perPage,
            $currentPage,
            ['path' => request()->url(), 'query' => request()->query()]
        );
    }

    /**
     * 글로벌 메뉴 생성
     *
     * @throws \InvalidArgumentException 게시판 URL 패턴 사용 시
     */
    public function createGlobalMenu(array $data): GlobalMenu
    {
        // 게시판 URL 패턴 수동 생성 방지
        $this->validateNotBoardUrl($data['url'] ?? null);

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // hidden 처리
        $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';

        // is_external 처리
        $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';

        // parent_id null 처리
        if (empty($data['parent_id'])) {
            $data['parent_id'] = null;
        }

        return GlobalMenu::create($data);
    }

    /**
     * 글로벌 메뉴 수정
     *
     * @throws \InvalidArgumentException 게시판 메뉴 수정 또는 게시판 URL 패턴 사용 시
     */
    public function updateGlobalMenu(int $id, array $data): bool
    {
        $menu = $this->getGlobalMenuById($id);
        if (! $menu) {
            return false;
        }

        // 게시판 연동 메뉴는 수정 불가 (활성/숨김 토글만 허용)
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 수정할 수 없습니다. 게시판 관리에서 수정해주세요.'
            );
        }

        // 새 URL이 게시판 URL 패턴이면 거부
        $this->validateNotBoardUrl($data['url'] ?? null);

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // hidden 처리
        $data['hidden'] = isset($data['hidden']) && $data['hidden'] == '1';

        // is_external 처리
        $data['is_external'] = isset($data['is_external']) && $data['is_external'] == '1';

        // parent_id null 처리
        if (empty($data['parent_id'])) {
            $data['parent_id'] = null;
        }

        // 자기 자신을 부모로 설정하는 것 방지
        if (isset($data['parent_id']) && $data['parent_id'] == $id) {
            return false;
        }

        return $menu->update($data);
    }

    /**
     * 글로벌 메뉴 삭제 (Soft Delete)
     *
     * @throws \InvalidArgumentException 게시판 연동 메뉴인 경우
     */
    public function deleteGlobalMenu(int $id): bool
    {
        $menu = $this->getGlobalMenuById($id);
        if (! $menu) {
            return false;
        }

        // 게시판 연동 메뉴는 삭제 불가
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 삭제할 수 없습니다. 게시판 관리에서 삭제해주세요.'
            );
        }

        // 자식 메뉴가 있는 경우 삭제 불가
        if ($menu->children()->count() > 0) {
            return false;
        }

        return $menu->delete();
    }

    /**
     * 글로벌 메뉴 복원
     */
    public function restoreGlobalMenu(int $id): bool
    {
        $menu = GlobalMenu::onlyTrashed()->findOrFail($id);

        return $menu->restore();
    }

    /**
     * 글로벌 메뉴 영구 삭제
     * - 연관 권한(permissions)도 함께 삭제
     * - 이 글로벌 메뉴를 참조하는 테넌트 메뉴들의 global_menu_id도 null 처리
     * - 삭제 정보를 archived_records에 저장
     *
     * @return array{success: bool, message: string, deleted_permissions: array}
     *
     * @throws \InvalidArgumentException 게시판 연동 메뉴인 경우
     */
    public function forceDeleteGlobalMenu(int $id): array
    {
        $menu = GlobalMenu::withTrashed()->findOrFail($id);

        // 게시판 연동 메뉴는 영구 삭제 불가
        if ($this->isBoardMenu($menu)) {
            throw new \InvalidArgumentException(
                '게시판 연동 메뉴는 영구 삭제할 수 없습니다. 게시판 관리에서 삭제해주세요.'
            );
        }

        // 자식 메뉴가 있는 경우 영구 삭제 불가
        if ($menu->children()->withTrashed()->count() > 0) {
            return [
                'success' => false,
                'message' => '자식 메뉴가 있어 삭제할 수 없습니다.',
                'deleted_permissions' => [],
            ];
        }

        return DB::transaction(function () use ($menu) {
            // 연관 권한 조회 (삭제 전 기록용)
            $permissions = Permission::where('name', 'like', "global_menu:{$menu->id}.%")->get();
            $permissionData = $permissions->map(fn ($p) => [
                'id' => $p->id,
                'name' => $p->name,
                'guard_name' => $p->guard_name,
                'tenant_id' => $p->tenant_id,
            ])->toArray();

            // 역할-권한 연결 정보 조회
            $rolePermissions = DB::table('role_has_permissions')
                ->join('roles', 'role_has_permissions.role_id', '=', 'roles.id')
                ->whereIn('role_has_permissions.permission_id', $permissions->pluck('id'))
                ->select('roles.id as role_id', 'roles.name as role_name', 'role_has_permissions.permission_id')
                ->get()
                ->toArray();

            // 참조하는 테넌트 메뉴 조회
            $referencingMenus = Menu::withTrashed()
                ->where('global_menu_id', $menu->id)
                ->get(['id', 'tenant_id', 'name'])
                ->toArray();

            // 아카이브 레코드 생성
            $batchId = (string) Str::uuid();
            $archivedRecord = ArchivedRecord::create([
                'batch_id' => $batchId,
                'batch_description' => "글로벌 메뉴 영구 삭제: {$menu->name} (ID: {$menu->id})",
                'record_type' => 'global_menu',
                'tenant_id' => null,
                'original_id' => $menu->id,
                'main_data' => $menu->toArray(),
                'schema_version' => '1.0',
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
                'notes' => "글로벌 메뉴 영구 삭제 - 연관 권한 {$permissions->count()}개, 참조 테넌트 메뉴 ".count($referencingMenus).'개 해제',
            ]);

            // 연관 권한 정보 저장
            if ($permissions->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'permissions',
                    'data' => $permissionData,
                    'record_count' => $permissions->count(),
                ]);
            }

            // 역할-권한 연결 정보 저장
            if (! empty($rolePermissions)) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'role_has_permissions',
                    'data' => $rolePermissions,
                    'record_count' => count($rolePermissions),
                ]);
            }

            // 참조 테넌트 메뉴 정보 저장
            if (! empty($referencingMenus)) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'menus (referencing)',
                    'data' => $referencingMenus,
                    'record_count' => count($referencingMenus),
                ]);
            }

            // 연관 권한 삭제
            Permission::where('name', 'like', "global_menu:{$menu->id}.%")->delete();

            // 이 글로벌 메뉴를 참조하는 테넌트 메뉴들의 참조 해제
            Menu::withTrashed()
                ->where('global_menu_id', $menu->id)
                ->update(['global_menu_id' => null, 'is_customized' => true]);

            // 글로벌 메뉴 영구 삭제
            $menu->forceDelete();

            return [
                'success' => true,
                'message' => "글로벌 메뉴 '{$menu->name}'와 연관 권한 {$permissions->count()}개가 삭제되었습니다.",
                'deleted_permissions' => $permissionData,
                'referencing_menus_unlinked' => count($referencingMenus),
                'batch_id' => $batchId,
            ];
        });
    }

    /**
     * 글로벌 메뉴 활성 상태 토글
     */
    public function toggleGlobalActive(int $id): bool
    {
        $menu = $this->getGlobalMenuById($id);
        if (! $menu) {
            return false;
        }

        $menu->is_active = ! $menu->is_active;

        return $menu->save();
    }

    /**
     * 글로벌 메뉴 숨김 상태 토글
     */
    public function toggleGlobalHidden(int $id): bool
    {
        $menu = $this->getGlobalMenuById($id);
        if (! $menu) {
            return false;
        }

        $menu->hidden = ! $menu->hidden;

        return $menu->save();
    }

    /**
     * 글로벌 메뉴 순서 변경
     */
    public function reorderGlobalMenus(array $items): bool
    {
        return \DB::transaction(function () use ($items) {
            foreach ($items as $item) {
                GlobalMenu::where('id', $item['id'])
                    ->update(['sort_order' => $item['sort_order']]);
            }

            return true;
        });
    }

    /**
     * 글로벌 메뉴 이동 (계층 구조 변경 - 인덴트/아웃덴트)
     * - 다른 부모 아래로 이동 가능
     * - 하위 메뉴는 자동으로 따라감
     * - 순환 참조 방지
     */
    public function moveGlobalMenu(int $menuId, ?int $newParentId, int $sortOrder): array
    {
        $menu = GlobalMenu::find($menuId);
        if (! $menu) {
            return ['success' => false, 'message' => '글로벌 메뉴를 찾을 수 없습니다.'];
        }

        // 순환 참조 방지: 자신의 하위 메뉴로 이동 불가
        if ($newParentId !== null && $this->isGlobalDescendant($menuId, $newParentId)) {
            return ['success' => false, 'message' => '자신의 하위 메뉴로 이동할 수 없습니다.'];
        }

        // 자기 자신을 부모로 설정 방지
        if ($newParentId === $menuId) {
            return ['success' => false, 'message' => '자기 자신을 부모로 설정할 수 없습니다.'];
        }

        return \DB::transaction(function () use ($menu, $newParentId, $sortOrder) {
            $oldParentId = $menu->parent_id;

            // 부모 변경
            $menu->parent_id = $newParentId;
            $menu->sort_order = $sortOrder;
            $menu->save();

            // 같은 부모의 다른 메뉴들 순서 재정렬
            $this->reorderGlobalSiblings($newParentId, $menu->id, $sortOrder);

            // 이전 부모의 메뉴들도 순서 재정렬 (빈 자리 채우기)
            if ($oldParentId !== $newParentId) {
                $this->compactGlobalSiblings($oldParentId);
            }

            return ['success' => true, 'message' => '글로벌 메뉴가 이동되었습니다.'];
        });
    }

    /**
     * 특정 글로벌 메뉴가 다른 메뉴의 하위인지 확인 (순환 참조 방지)
     */
    private function isGlobalDescendant(int $ancestorId, int $menuId): bool
    {
        $menu = GlobalMenu::find($menuId);
        while ($menu) {
            if ($menu->id === $ancestorId) {
                return true;
            }
            $menu = $menu->parent;
        }

        return false;
    }

    /**
     * 같은 부모의 글로벌 형제 메뉴들 순서 재정렬
     */
    private function reorderGlobalSiblings(?int $parentId, int $excludeId, int $insertAt): void
    {
        $siblings = GlobalMenu::where('parent_id', $parentId)
            ->where('id', '!=', $excludeId)
            ->orderBy('sort_order')
            ->get();

        $order = 1;
        foreach ($siblings as $sibling) {
            if ($order === $insertAt) {
                $order++; // 삽입 위치 건너뛰기
            }
            if ($sibling->sort_order !== $order) {
                $sibling->update(['sort_order' => $order]);
            }
            $order++;
        }
    }

    /**
     * 글로벌 형제 메뉴들 순서 압축 (빈 자리 채우기)
     */
    private function compactGlobalSiblings(?int $parentId): void
    {
        $siblings = GlobalMenu::where('parent_id', $parentId)
            ->orderBy('sort_order')
            ->get();

        $order = 1;
        foreach ($siblings as $sibling) {
            if ($sibling->sort_order !== $order) {
                $sibling->update(['sort_order' => $order]);
            }
            $order++;
        }
    }

    /**
     * 글로벌 메뉴 목록 조회 (가져오기 상태 포함)
     * - 모든 글로벌 메뉴 반환
     * - 이미 가져온 메뉴는 is_imported=true로 표시
     */
    public function getAllGlobalMenusWithStatus(int $tenantId): Collection
    {
        // 글로벌 메뉴 전체 조회 (global_menus 테이블에서)
        $globalMenus = GlobalMenu::query()
            ->where('is_active', true)
            ->orderBy('parent_id')
            ->orderBy('sort_order')
            ->get();

        // 현재 테넌트에 이미 복사된 메뉴의 global_menu_id 목록
        $existingGlobalIds = Menu::where('tenant_id', $tenantId)
            ->whereNotNull('global_menu_id')
            ->pluck('global_menu_id')
            ->toArray();

        // 각 메뉴에 is_imported 속성 추가
        $globalMenus->each(function ($menu) use ($existingGlobalIds) {
            $menu->is_imported = in_array($menu->id, $existingGlobalIds);
        });

        // 트리 구조로 정렬 (depth 정보 포함)
        return $this->flattenMenuTree($globalMenus);
    }

    /**
     * 선택한 글로벌 메뉴를 현재 테넌트로 복사
     */
    public function copyFromGlobal(int $tenantId, array $menuIds): array
    {
        if (empty($menuIds)) {
            return ['success' => false, 'message' => '복사할 메뉴를 선택해주세요.', 'copied' => 0];
        }

        // 선택된 글로벌 메뉴 조회 (global_menus 테이블에서)
        $globalMenus = GlobalMenu::query()
            ->whereIn('id', $menuIds)
            ->orderBy('parent_id')  // 부모 먼저 복사하기 위해
            ->orderBy('sort_order')
            ->get();

        if ($globalMenus->isEmpty()) {
            return ['success' => false, 'message' => '유효한 글로벌 메뉴가 없습니다.', 'copied' => 0];
        }

        $copied = 0;

        return DB::transaction(function () use ($globalMenus, $tenantId, &$copied) {
            // global_menu_id → 새로 생성된 tenant menu id 매핑
            $idMapping = [];

            foreach ($globalMenus as $globalMenu) {
                // 이미 복사된 메뉴인지 확인
                $exists = Menu::where('tenant_id', $tenantId)
                    ->where('global_menu_id', $globalMenu->id)
                    ->exists();

                if ($exists) {
                    continue;
                }

                // 부모 메뉴 매핑 (글로벌 → 테넌트)
                $newParentId = null;
                if ($globalMenu->parent_id) {
                    // 이번 복사에서 생성된 부모가 있는지 확인
                    if (isset($idMapping[$globalMenu->parent_id])) {
                        $newParentId = $idMapping[$globalMenu->parent_id];
                    } else {
                        // 기존에 복사된 부모 메뉴가 있는지 확인
                        $parentTenantMenu = Menu::where('tenant_id', $tenantId)
                            ->where('global_menu_id', $globalMenu->parent_id)
                            ->first();
                        $newParentId = $parentTenantMenu?->id;
                    }
                }

                // 새 테넌트 메뉴 생성
                $newMenu = Menu::create([
                    'tenant_id' => $tenantId,
                    'parent_id' => $newParentId,
                    'global_menu_id' => $globalMenu->id,
                    'name' => $globalMenu->name,
                    'url' => $globalMenu->url,
                    'icon' => $globalMenu->icon,
                    'sort_order' => $globalMenu->sort_order,
                    'is_active' => $globalMenu->is_active,
                    'hidden' => $globalMenu->hidden,
                    'is_external' => $globalMenu->is_external,
                    'external_url' => $globalMenu->external_url,
                    'is_customized' => false,
                    'created_by' => auth()->id(),
                    'updated_by' => auth()->id(),
                ]);

                // ID 매핑 저장 (자식 메뉴 복사 시 참조용)
                $idMapping[$globalMenu->id] = $newMenu->id;
                $copied++;
            }

            return [
                'success' => true,
                'message' => "{$copied}개 메뉴가 복사되었습니다.",
                'copied' => $copied,
            ];
        });
    }

    // =========================================================================
    // 게시판 메뉴 자동 생성
    // =========================================================================

    /**
     * URL로 부모 메뉴 찾기 (우선순위: /customer-center → /boards or /system-boards → null)
     *
     * @param  bool  $isSystem  시스템 게시판 여부
     * @param  int|null  $tenantId  테넌트 ID (시스템 게시판이면 null)
     */
    public function findParentMenuForBoard(bool $isSystem, ?int $tenantId = null): ?int
    {
        if ($isSystem) {
            // 시스템 게시판: global_menus에서 찾기
            // 우선순위: /customer-center → /system-boards → /boards → null
            $priorityUrls = ['/customer-center', '/system-boards', '/boards'];

            foreach ($priorityUrls as $url) {
                $menu = GlobalMenu::where('url', $url)
                    ->where('is_active', true)
                    ->first();

                if ($menu) {
                    return $menu->id;
                }
            }

            return null; // 최상위로 추가
        } else {
            // 테넌트 게시판: menus에서 찾기
            // 우선순위: /boards → /customer-center → null
            $priorityUrls = ['/boards', '/customer-center'];

            foreach ($priorityUrls as $url) {
                $query = Menu::where('url', $url)
                    ->where('is_active', true);

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

                $menu = $query->first();

                if ($menu) {
                    return $menu->id;
                }
            }

            return null; // 최상위로 추가
        }
    }

    /**
     * 게시판에 대한 메뉴 자동 생성
     *
     * @param  array  $boardData  게시판 정보 (board_code, name, is_system, tenant_id)
     * @return GlobalMenu|Menu|null 생성된 메뉴 또는 null
     */
    public function createMenuForBoard(array $boardData): GlobalMenu|Menu|null
    {
        $isSystem = $boardData['is_system'] ?? false;
        $tenantId = $boardData['tenant_id'] ?? null;
        $boardCode = $boardData['board_code'];
        $boardName = $boardData['name'];

        // 부모 메뉴 찾기
        $parentId = $this->findParentMenuForBoard($isSystem, $tenantId);

        if ($isSystem) {
            // 시스템 게시판 → global_menus + 모든 테넌트 menus에 추가
            $url = '/customer-center/'.$boardCode;

            // 1. GlobalMenu 조회 또는 생성
            $globalMenu = GlobalMenu::where('url', $url)->first();
            if (! $globalMenu) {
                $maxOrder = GlobalMenu::where('parent_id', $parentId)->max('sort_order') ?? 0;

                $globalMenu = GlobalMenu::create([
                    'parent_id' => $parentId,
                    'name' => $boardName,
                    'url' => $url,
                    'icon' => 'document-text',
                    'sort_order' => $maxOrder + 1,
                    'is_active' => true,
                    'hidden' => false,
                    'is_external' => false,
                ]);
            }

            // 2. 모든 테넌트의 Menu에 추가 (global_menu_id 연결)
            $tenants = Tenant::active()->get();
            foreach ($tenants as $tenant) {
                $menuParentId = $this->findParentMenuForBoard(false, $tenant->id);
                $menuExists = Menu::where('url', $url)->where('tenant_id', $tenant->id)->exists();
                if (! $menuExists) {
                    $maxMenuOrder = Menu::where('parent_id', $menuParentId)
                        ->where('tenant_id', $tenant->id)
                        ->max('sort_order') ?? 0;

                    Menu::create([
                        'tenant_id' => $tenant->id,
                        'parent_id' => $menuParentId,
                        'global_menu_id' => $globalMenu->id,
                        'name' => $boardName,
                        'url' => $url,
                        'icon' => 'document-text',
                        'sort_order' => $maxMenuOrder + 1,
                        'is_active' => true,
                        'hidden' => false,
                        'is_external' => false,
                        'is_customized' => false,
                        'created_by' => auth()->id(),
                    ]);
                }
            }

            return $globalMenu;
        } else {
            // 테넌트 게시판 → menus에 추가
            $url = '/boards/'.$boardCode;

            // 중복 체크
            $query = Menu::where('url', $url);
            if ($tenantId) {
                $query->where('tenant_id', $tenantId);
            } else {
                $query->whereNull('tenant_id');
            }

            if ($query->exists()) {
                return null;
            }

            // 정렬 순서 계산
            $maxOrderQuery = Menu::where('parent_id', $parentId);
            if ($tenantId) {
                $maxOrderQuery->where('tenant_id', $tenantId);
            } else {
                $maxOrderQuery->whereNull('tenant_id');
            }
            $maxOrder = $maxOrderQuery->max('sort_order') ?? 0;

            return Menu::create([
                'tenant_id' => $tenantId,
                'parent_id' => $parentId,
                'name' => $boardName,
                'url' => $url,
                'icon' => 'document-text', // 기본 아이콘
                'sort_order' => $maxOrder + 1,
                'is_active' => true,
                'hidden' => false,
                'is_external' => false,
                'created_by' => auth()->id(),
            ]);
        }
    }

    /**
     * 게시판 삭제 시 연결된 메뉴도 삭제
     *
     * @param  string  $boardCode  게시판 코드
     * @param  bool  $isSystem  시스템 게시판 여부
     * @param  int|null  $tenantId  테넌트 ID
     */
    public function deleteMenuForBoard(string $boardCode, bool $isSystem, ?int $tenantId = null, bool $forceDelete = false): bool
    {
        if ($isSystem) {
            $url = '/customer-center/'.$boardCode;

            // 1. GlobalMenu 삭제
            $globalMenu = $forceDelete
                ? GlobalMenu::withTrashed()->where('url', $url)->first()
                : GlobalMenu::where('url', $url)->first();

            if ($globalMenu) {
                $forceDelete ? $globalMenu->forceDelete() : $globalMenu->delete();
            }

            // 2. 모든 테넌트의 Menu 삭제
            $menusQuery = $forceDelete
                ? Menu::withTrashed()->where('url', $url)
                : Menu::where('url', $url);

            $menus = $menusQuery->get();
            foreach ($menus as $menu) {
                if (! $forceDelete) {
                    $menu->deleted_by = auth()->id();
                    $menu->save();
                }
                $forceDelete ? $menu->forceDelete() : $menu->delete();
            }

            return true;
        } else {
            $url = '/boards/'.$boardCode;
            $query = $forceDelete
                ? Menu::withTrashed()->where('url', $url)
                : Menu::where('url', $url);

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

            $menu = $query->first();

            if ($menu) {
                if (! $forceDelete) {
                    $menu->deleted_by = auth()->id();
                    $menu->save();
                }

                return $forceDelete ? $menu->forceDelete() : $menu->delete();
            }
        }

        return false;
    }

    /**
     * 게시판 복원 시 연결된 메뉴도 복원
     * - soft-deleted 메뉴가 있으면 복원
     * - 활성 메뉴가 이미 있으면 아무것도 안함
     * - 둘 다 없으면 새로 생성
     *
     * @param  string  $boardCode  게시판 코드
     * @param  string  $boardName  게시판 이름 (메뉴 생성 시 필요)
     * @param  bool  $isSystem  시스템 게시판 여부
     * @param  int|null  $tenantId  테넌트 ID
     */
    public function restoreMenuForBoard(string $boardCode, string $boardName, bool $isSystem, ?int $tenantId = null): bool
    {
        if ($isSystem) {
            $url = '/customer-center/'.$boardCode;

            // === GlobalMenu 복원 또는 생성 ===
            $globalMenu = GlobalMenu::withTrashed()->where('url', $url)->first();
            if ($globalMenu && $globalMenu->trashed()) {
                $globalMenu->restore();
                $globalMenu->deleted_by = null;
                $globalMenu->save();
            } elseif (! $globalMenu) {
                // 없으면 생성
                $parentId = $this->findParentMenuForBoard(true, null);
                $maxOrder = GlobalMenu::where('parent_id', $parentId)->max('sort_order') ?? 0;
                $globalMenu = GlobalMenu::create([
                    'parent_id' => $parentId,
                    'name' => $boardName,
                    'url' => $url,
                    'icon' => 'document-text',
                    'sort_order' => $maxOrder + 1,
                    'is_active' => true,
                    'hidden' => false,
                    'is_external' => false,
                ]);
            }

            // === 모든 활성 테넌트의 Menu 복원/생성 (global_menu_id 연결) ===
            $tenants = Tenant::active()->get();
            foreach ($tenants as $tenant) {
                $menuTrashed = Menu::onlyTrashed()->where('url', $url)->where('tenant_id', $tenant->id)->first();
                if ($menuTrashed) {
                    $menuTrashed->restore();
                    $menuTrashed->deleted_by = null;
                    $menuTrashed->global_menu_id = $globalMenu->id;
                    $menuTrashed->save();
                } elseif (! Menu::where('url', $url)->where('tenant_id', $tenant->id)->exists()) {
                    // 없으면 생성
                    $menuParentId = $this->findParentMenuForBoard(false, $tenant->id);
                    $maxMenuOrder = Menu::where('parent_id', $menuParentId)->where('tenant_id', $tenant->id)->max('sort_order') ?? 0;
                    Menu::create([
                        'tenant_id' => $tenant->id,
                        'parent_id' => $menuParentId,
                        'global_menu_id' => $globalMenu->id,
                        'name' => $boardName,
                        'url' => $url,
                        'icon' => 'document-text',
                        'sort_order' => $maxMenuOrder + 1,
                        'is_active' => true,
                        'hidden' => false,
                        'is_external' => false,
                        'is_customized' => false,
                        'created_by' => auth()->id(),
                    ]);
                }
            }

            return true;
        } else {
            $url = '/boards/'.$boardCode;

            // 1. soft-deleted 메뉴 확인
            $trashedQuery = Menu::onlyTrashed()->where('url', $url);
            if ($tenantId) {
                $trashedQuery->where('tenant_id', $tenantId);
            } else {
                $trashedQuery->whereNull('tenant_id');
            }
            $trashedMenu = $trashedQuery->first();

            if ($trashedMenu) {
                $restored = $trashedMenu->restore();
                if ($restored) {
                    $trashedMenu->deleted_by = null;
                    $trashedMenu->save();
                }

                return $restored;
            }

            // 2. 활성 메뉴가 이미 있는지 확인
            $activeQuery = Menu::where('url', $url);
            if ($tenantId) {
                $activeQuery->where('tenant_id', $tenantId);
            } else {
                $activeQuery->whereNull('tenant_id');
            }
            $activeMenu = $activeQuery->first();

            if ($activeMenu) {
                return true; // 이미 존재, 아무것도 안함
            }

            // 3. 둘 다 없으면 새로 생성
            $created = $this->createMenuForBoard([
                'board_code' => $boardCode,
                'name' => $boardName,
                'is_system' => false,
                'tenant_id' => $tenantId,
            ]);

            return $created !== null;
        }
    }

    /**
     * 게시판 코드 변경 시 연결된 메뉴 URL도 업데이트
     *
     * @param  string  $oldCode  기존 게시판 코드
     * @param  string  $newCode  새 게시판 코드
     * @param  string  $newName  새 게시판 이름
     * @param  bool  $isSystem  시스템 게시판 여부
     * @param  int|null  $tenantId  테넌트 ID (테넌트 게시판인 경우)
     */
    public function updateMenuForBoard(string $oldCode, string $newCode, string $newName, bool $isSystem, ?int $tenantId = null): bool
    {
        if ($isSystem) {
            // 시스템 게시판: global_menus + 연결된 모든 menus 업데이트
            $oldUrl = '/customer-center/'.$oldCode;
            $newUrl = '/customer-center/'.$newCode;

            // 1. GlobalMenu 업데이트
            $globalMenu = GlobalMenu::where('url', $oldUrl)->first();
            if ($globalMenu) {
                $globalMenu->update([
                    'url' => $newUrl,
                    'name' => $newName,
                ]);

                // 2. 해당 global_menu_id를 참조하는 모든 menus 업데이트
                Menu::where('global_menu_id', $globalMenu->id)
                    ->update([
                        'url' => $newUrl,
                        'name' => $newName,
                    ]);
            }

            return true;
        } else {
            // 테넌트 게시판: 해당 테넌트의 menus만 업데이트
            $oldUrl = '/boards/'.$oldCode;
            $newUrl = '/boards/'.$newCode;

            $query = Menu::where('url', $oldUrl);
            if ($tenantId) {
                $query->where('tenant_id', $tenantId);
            } else {
                $query->whereNull('tenant_id');
            }

            $query->update([
                'url' => $newUrl,
                'name' => $newName,
            ]);

            return true;
        }
    }
}
