<?php

declare(strict_types=1);

namespace App\Filament\Support;

use App\Services\DberpPermissionService;
use BackedEnum;
use Filament\Pages\Page as FilamentPage;
use Filament\Resources\Resource as FilamentResource;
use Illuminate\Contracts\Auth\Authenticatable;
use UnitEnum;

/**
 * 预置后台快捷入口元数据（无用户偏好存储；权限在 {@see DberpShortcutItem::isAccessible} 中判定）。
 * 文案存翻译键，避免注册表单例缓存后切换语言仍显示旧语言；导航发现的项可走 Filament 导航标题。
 */
final readonly class DberpShortcutItem
{
    /**
     * @param  class-string<FilamentResource>|null  $resourceClass
     * @param  class-string<FilamentPage>|null  $pageClass
     * @param  list<string>|null  $permissionCodesAny  无 Resource/Page 时任意命中即视为可访问
     * @param  list<string>  $placements
     * @param  bool  $openResourceCreate  为 true 时指向 Resource 新建页，权限用 {@see FilamentResource::canCreate()}
     */
    public function __construct(
        public string $key,
        public string $labelLangKey,
        public ?string $descriptionLangKey,
        public ?string $resourceClass,
        public ?string $pageClass,
        public ?array $permissionCodesAny,
        public string $group,
        public int $sort,
        public array $placements,
        public bool $openResourceCreate = false,
        public bool $usesFilamentNavigationLabel = false,
        public ?string $navigationUiGroup = null,
    ) {}

    public function localizedLabel(): string
    {
        if ($this->usesFilamentNavigationLabel) {
            if ($this->resourceClass !== null) {
                return (string) $this->resourceClass::getNavigationLabel();
            }

            if ($this->pageClass !== null) {
                return (string) $this->pageClass::getNavigationLabel();
            }
        }

        return (string) __($this->labelLangKey);
    }

    public function localizedDescription(): ?string
    {
        return $this->descriptionLangKey !== null
            ? (string) __($this->descriptionLangKey)
            : null;
    }

    /**
     * 常用功能候选分组：与主导航一致，优先取 Resource/Page 的 {@see FilamentResource::getNavigationGroup()}；
     * 无类元数据时回退到导航发现写入的 {@see $navigationUiGroup} 或预置 `group` 映射。
     */
    public function sectionLabel(): string
    {
        if ($this->resourceClass !== null || $this->pageClass !== null) {
            $fromFilament = $this->resolveFilamentNavigationGroupLabel();

            return $fromFilament ?? (string) __('dberp.nav.system');
        }

        if ($this->navigationUiGroup !== null && $this->navigationUiGroup !== '') {
            return $this->navigationUiGroup;
        }

        return match ($this->group) {
            'master_data' => (string) __('dberp.nav.basic_data'),
            'inventory' => (string) __('dberp.nav.warehouse'),
            'purchase' => (string) __('dberp.nav.purchase'),
            'sales' => (string) __('dberp.nav.sales'),
            'settlement' => (string) __('dberp.nav.settlement'),
            'pinnable_resource', 'pinnable_page' => $this->groupLabel(),
            default => $this->groupLabel(),
        };
    }

    /**
     * 与侧栏菜单项顺序对齐：有 Resource/Page 时用其 {@see FilamentResource::getNavigationSort()}。
     */
    public function sidebarNavigationSort(): int
    {
        if ($this->resourceClass !== null) {
            return (int) ($this->resourceClass::getNavigationSort() ?? $this->sort);
        }

        if ($this->pageClass !== null) {
            return (int) ($this->pageClass::getNavigationSort() ?? $this->sort);
        }

        return $this->sort;
    }

    private function resolveFilamentNavigationGroupLabel(): ?string
    {
        $raw = null;
        if ($this->resourceClass !== null) {
            $raw = $this->resourceClass::getNavigationGroup();
        } elseif ($this->pageClass !== null) {
            $raw = $this->pageClass::getNavigationGroup();
        }

        return self::normalizeFilamentNavigationGroupLabel($raw);
    }

    private static function normalizeFilamentNavigationGroupLabel(UnitEnum|string|null $group): ?string
    {
        if ($group === null) {
            return null;
        }

        if ($group instanceof BackedEnum) {
            $value = (string) $group->value;

            return $value !== '' ? $value : null;
        }

        if ($group instanceof UnitEnum) {
            return $group->name;
        }

        $string = (string) $group;

        return $string !== '' ? $string : null;
    }

    /**
     * 延迟解析，避免在 Filament 默认面板尚未注册时（如 Composer 脚本）调用 Resource::getUrl。
     */
    public function resolveUrl(): string
    {
        if ($this->resourceClass !== null) {
            /** @var class-string<FilamentResource> $class */
            $class = $this->resourceClass;

            return $this->openResourceCreate
                ? $class::getUrl('create')
                : $class::getUrl('index');
        }

        if ($this->pageClass !== null) {
            /** @var class-string<FilamentPage> $class */
            $class = $this->pageClass;

            return $class::getUrl();
        }

        return '#';
    }

    public function groupLabel(): string
    {
        return (string) __('dberp.shortcuts.groups.'.$this->group);
    }

    /**
     * 与 Filament 列表/页面入口一致：优先 Resource::canViewAny、Page::canAccess；否则按权限码任意满足。
     */
    public function isAccessible(?Authenticatable $user): bool
    {
        if ($this->resourceClass !== null) {
            /** @var class-string<FilamentResource> $class */
            $class = $this->resourceClass;

            return $this->openResourceCreate
                ? $class::canCreate()
                : $class::canViewAny();
        }

        if ($this->pageClass !== null) {
            /** @var class-string<FilamentPage> $class */
            $class = $this->pageClass;

            return $class::canAccess();
        }

        if ($this->permissionCodesAny !== null && $this->permissionCodesAny !== []) {
            return app(DberpPermissionService::class)->allowsAny($user, $this->permissionCodesAny);
        }

        return false;
    }

    public function hasPlacement(string $placement): bool
    {
        return in_array($placement, $this->placements, true);
    }
}
