<?php

namespace App\Services;

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

class SidebarMenuService
{
    /**
     * 현재 사용자가 접근 가능한 메뉴 트리 조회
     */
    public function getUserMenuTree(?User $user = null): Collection
    {
        $user = $user ?? auth()->user();
        // 로그인한 사용자의 tenant_id만 사용 (session 값 무시)
        $tenantId = $user?->tenant_id ?? 1;

        // 테넌트의 모든 활성 메뉴 조회
        $allMenus = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('is_active', true)
            ->where('hidden', false)
            ->orderBy('sort_order')
            ->get();

        // 슈퍼관리자는 모든 메뉴 표시
        if ($user && $user->is_super_admin) {
            return $this->buildMenuTree($allMenus);
        }

        // 일반 사용자: 부서 권한 기반 메뉴 ID 조회
        $permittedMenuIds = $this->getPermittedMenuIds($user, $tenantId);

        // 역할 기반 필터링 + 부서 권한 필터링
        $filteredMenus = $allMenus->filter(function ($menu) use ($user, $permittedMenuIds) {
            $requiredRole = $menu->getRequiresRole();

            // super_admin 역할 필요시 슈퍼관리자만
            if ($requiredRole === 'super_admin') {
                return $user && $user->is_super_admin;
            }

            // 기타 역할 체크
            if ($requiredRole && ! ($user && $user->hasRole($requiredRole))) {
                return false;
            }

            // 단체 파트너에게 숨길 메뉴 체크
            if ($menu->getOption('hide_for_group_partner') && $user?->salesPartner?->isGroup()) {
                return false;
            }

            // 부서 권한 체크: 허용된 메뉴 ID만 표시
            return in_array($menu->id, $permittedMenuIds);
        });

        return $this->buildMenuTree($filteredMenus);
    }

    /**
     * 사용자가 접근 가능한 메뉴 ID 목록 조회 (부서 권한 기반)
     */
    private function getPermittedMenuIds(?User $user, int $tenantId): array
    {
        if (! $user) {
            return [];
        }

        // 사용자의 부서 ID 조회
        $departmentIds = DB::table('department_user')
            ->where('user_id', $user->id)
            ->pluck('department_id')
            ->toArray();

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

        $now = now();

        // permission_overrides 테이블에서 부서에 ALLOW된 menu:*.view 권한 조회
        $permittedMenuIds = DB::table('permission_overrides as po')
            ->join('permissions as p', 'p.id', '=', 'po.permission_id')
            ->where('po.model_type', Department::class)
            ->whereIn('po.model_id', $departmentIds)
            ->where('po.tenant_id', $tenantId)
            ->where('po.effect', 1) // ALLOW
            ->where('p.name', 'like', 'menu:%.view')
            ->whereNull('po.deleted_at')
            ->where(function ($query) use ($now) {
                $query->whereNull('po.effective_from')
                    ->orWhere('po.effective_from', '<=', $now);
            })
            ->where(function ($query) use ($now) {
                $query->whereNull('po.effective_to')
                    ->orWhere('po.effective_to', '>=', $now);
            })
            ->pluck('p.name')
            ->map(function ($name) {
                // menu:{id}.view에서 id 추출
                if (preg_match('/^menu:(\d+)\.view$/', $name, $matches)) {
                    return (int) $matches[1];
                }

                return null;
            })
            ->filter()
            ->unique()
            ->toArray();

        return $permittedMenuIds;
    }

    /**
     * 섹션별 메뉴 조회 (main, tools, labs)
     */
    public function getMenusBySection(?User $user = null): array
    {
        $menuTree = $this->getUserMenuTree($user);

        return [
            'main' => $menuTree->filter(fn ($m) => $m->getSection() === 'main')->values(),
            'tools' => $menuTree->filter(fn ($m) => $m->getSection() === 'tools')->values(),
            'labs' => $menuTree->filter(fn ($m) => $m->getSection() === 'labs')->values(),
        ];
    }

    /**
     * 메뉴 트리 구성
     */
    private function buildMenuTree(Collection $menus, ?int $parentId = null): Collection
    {
        return $menus->where('parent_id', $parentId)
            ->map(function ($menu) use ($menus) {
                $menu->menuChildren = $this->buildMenuTree($menus, $menu->id);

                return $menu;
            })
            ->values();
    }

    /**
     * 현재 라우트가 메뉴와 일치하는지 확인
     */
    public function isMenuActive(Menu $menu): bool
    {
        $routeName = $menu->getRouteName();

        if ($routeName) {
            // 라우트 패턴 매칭 (예: pm.* → pm.index, pm.projects.index 등)
            if (str_ends_with($routeName, '.*')) {
                $prefix = substr($routeName, 0, -2);

                return request()->routeIs($prefix.'*');
            }

            return request()->routeIs($routeName);
        }

        // URL 매칭
        if ($menu->url) {
            $currentPath = '/'.ltrim(request()->path(), '/');

            // /boards/{id}/posts 패턴인 경우 게시판 기반 메뉴 매칭
            $boardMenuUrl = $this->getBoardMenuUrl();
            if ($boardMenuUrl) {
                return $menu->url === $boardMenuUrl;
            }

            // 정확한 URL 매칭
            if ($currentPath === $menu->url) {
                return true;
            }

            // prefix 매칭: 더 구체적인 메뉴가 있으면 덜 구체적인 prefix 매칭 비활성화
            // (예: /esign/templates/5/fields 접속 시, /esign/templates 메뉴만 활성화, /esign 메뉴는 비활성)
            if (str_starts_with($currentPath, $menu->url.'/')) {
                return ! self::hasExactMenuMatch($currentPath)
                    && ! self::hasMoreSpecificPrefixMenu($currentPath, $menu->url);
            }

            return false;
        }

        return false;
    }

    /**
     * 현재 /boards/{code}/posts 경로인 경우 해당 게시판의 메뉴 URL 반환
     */
    private function getBoardMenuUrl(): ?string
    {
        static $cachedUrl = null;
        static $calculated = false;

        if ($calculated) {
            return $cachedUrl;
        }

        $calculated = true;
        $currentPath = request()->path();

        // /boards/{code}/posts 패턴 확인 (code는 영문자로 시작하는 문자열)
        if (! preg_match('#^boards/([a-zA-Z][a-zA-Z0-9_-]*)(/posts.*)?$#', $currentPath, $matches)) {
            return null;
        }

        $boardCode = $matches[1];

        // 게시판 조회 (board_code로)
        $board = Board::withoutGlobalScopes()->where('board_code', $boardCode)->first();
        if (! $board) {
            // 게시판 없으면 게시판 관리 메뉴
            $cachedUrl = '/boards';

            return $cachedUrl;
        }

        // 시스템 게시판: /customer-center/{board_code}
        // 테넌트 게시판: /boards/{board_code}
        if ($board->is_system) {
            $expectedUrl = '/customer-center/'.$board->board_code;
        } else {
            $expectedUrl = '/boards/'.$board->board_code;
        }

        // 해당 URL의 메뉴가 존재하는지 확인
        $menuExists = Menu::withoutGlobalScopes()
            ->where('url', $expectedUrl)
            ->where('is_active', true)
            ->exists();

        if ($menuExists) {
            $cachedUrl = $expectedUrl;
        } else {
            // 메뉴 없으면 게시판 관리 메뉴
            $cachedUrl = '/boards';
        }

        return $cachedUrl;
    }

    /**
     * 현재 URL에 정확히 매칭되는 메뉴가 존재하는지 확인 (캐시)
     */
    private static ?bool $exactMatchCache = null;

    private static function hasExactMenuMatch(string $currentPath): bool
    {
        if (self::$exactMatchCache !== null) {
            return self::$exactMatchCache;
        }

        $tenantId = auth()->user()?->tenant_id ?? 1;

        self::$exactMatchCache = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('is_active', true)
            ->where('url', $currentPath)
            ->exists();

        return self::$exactMatchCache;
    }

    /**
     * 현재 경로에 대해 더 구체적인 prefix를 가진 메뉴가 있는지 확인
     * (예: 현재 메뉴 URL이 /esign이고, /esign/templates 메뉴도 있으면 true)
     */
    private static array $prefixMenuCache = [];

    private static function hasMoreSpecificPrefixMenu(string $currentPath, string $menuUrl): bool
    {
        $cacheKey = $currentPath.'|'.$menuUrl;
        if (isset(self::$prefixMenuCache[$cacheKey])) {
            return self::$prefixMenuCache[$cacheKey];
        }

        $tenantId = auth()->user()?->tenant_id ?? 1;

        // 현재 경로의 prefix이면서 이 메뉴 URL보다 긴 URL을 가진 활성 메뉴가 있는지 확인
        $result = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('is_active', true)
            ->whereNotNull('url')
            ->where('url', '!=', $menuUrl)
            ->whereRaw('LENGTH(url) > ?', [strlen($menuUrl)])
            ->whereRaw("? LIKE CONCAT(url, '/%')", [$currentPath])
            ->exists();

        self::$prefixMenuCache[$cacheKey] = $result;

        return $result;
    }

    /**
     * 메뉴 또는 자식 메뉴가 활성 상태인지 확인
     */
    public function isMenuOrChildActive(Menu $menu): bool
    {
        if ($this->isMenuActive($menu)) {
            return true;
        }

        // 자식 메뉴 중 활성 상태가 있는지 확인
        if (isset($menu->menuChildren) && $menu->menuChildren->isNotEmpty()) {
            foreach ($menu->menuChildren as $child) {
                if ($this->isMenuOrChildActive($child)) {
                    return true;
                }
            }
        }

        return false;
    }
}
