<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Support;

use App\Models\DberpCustomer;
use App\Models\DberpProduct;
use App\Models\DberpProductCategory;
use App\Models\DberpSupplier;
use App\Models\DberpWarehouse;
use Filament\Forms\Components\Select;
use Filament\Tables\Filters\Indicator;
use Filament\Tables\Filters\SelectFilter;

/**
 * Filament 筛选/表单主数据下拉选项；每次实时查库，保证新建/变更主数据后下拉立即可见。
 */
final class DberpFilamentMasterDataSelectOptions
{
    /**
     * @return array<int|string, string>
     */
    public static function customers(bool $enabledOnly = false): array
    {
        $query = DberpCustomer::query()->orderBy('sort_order')->orderBy('id');

        if ($enabledOnly) {
            $query->where('is_enabled', true);
        }

        return $query->pluck('name', 'id')->all();
    }

    /**
     * @return array<int|string, string>
     */
    public static function suppliers(bool $enabledOnly = false): array
    {
        $query = DberpSupplier::query()->orderBy('sort_order')->orderBy('id');

        if ($enabledOnly) {
            $query->where('is_enabled', true);
        }

        return $query->pluck('name', 'id')->all();
    }

    /**
     * @return array<int|string, string>
     */
    public static function warehouses(bool $enabledOnly = true): array
    {
        $query = DberpWarehouse::query()->orderBy('sort_order')->orderBy('id');

        if ($enabledOnly) {
            $query->where('is_enabled', true);
        }

        return $query->pluck('name', 'id')->all();
    }

    /**
     * @return array<int|string, string>
     */
    public static function productCategories(): array
    {
        return DberpProductCategory::query()
            ->orderBy('sort_order')
            ->orderBy('id')
            ->pluck('name', 'id')
            ->all();
    }

    /**
     * @param  string|null  $scope  {@see DberpProductSkuSelectOptions::PRODUCT_SCOPE_*}，为空表示全部商品（可配合 enabledOnly）
     * @return array<int|string, string>
     */
    public static function products(bool $enabledOnly = false, ?string $scope = null): array
    {
        if ($scope !== null) {
            return DberpProductSkuSelectOptions::productOptionsForScope($scope);
        }

        $query = DberpProduct::query()->orderBy('sort_order')->orderBy('id');

        if ($enabledOnly) {
            $query->where('is_enabled', true);
        }

        return DberpProductSkuSelectOptions::mapProductIterableToOptions(
            $query->with('unit')->get(),
        );
    }

    /**
     * 报表筛选主数据下拉：复用 DberpFilamentSearchableSelectOptions，点开即加载有限条选项并可搜索。
     *
     * @param  callable(): array<int|string, string>  $optionsResolver
     */
    public static function applyDeferredSearch(Select $select, callable $optionsResolver, int $limit = 50): Select
    {
        return DberpFilamentSearchableSelectOptions::configure(
            $select,
            fn (): int => count($optionsResolver()),
            fn (): array => $optionsResolver(),
            fn (string $search): array => self::filterOptionsForSearch($optionsResolver(), $search, $limit),
            fn ($value): ?string => self::resolveOptionLabel($optionsResolver, $value),
        );
    }

    /**
     * 列表筛选主数据下拉：与表单 Select 同样避免 Filament 5 可搜索下拉首屏空白，并限制大数据量首屏选项数。
     *
     * @param  callable(): array<int|string, string>  $optionsResolver
     */
    public static function applyDeferredSearchForFilter(SelectFilter $filter, callable $optionsResolver, int $limit = 50): SelectFilter
    {
        return $filter
            ->native(false)
            ->searchable()
            ->preload()
            ->options(function () use ($optionsResolver, $limit): array {
                $options = $optionsResolver();

                return count($options) < DberpProductSkuSelectOptions::SEARCHABLE_OPTION_THRESHOLD
                    ? $options
                    : self::filterOptionsForSearch($options, '', $limit);
            })
            ->getSearchResultsUsing(function (string $search) use ($optionsResolver, $limit): array {
                return self::filterOptionsForSearch($optionsResolver(), $search, $limit);
            })
            ->getOptionLabelUsing(fn ($value): ?string => self::resolveOptionLabel($optionsResolver, $value))
            ->indicateUsing(function (SelectFilter $filter, array $state) use ($optionsResolver): array {
                if ($filter->isMultiple()) {
                    return [];
                }

                if (blank($state['value'] ?? null)) {
                    return [];
                }

                $label = self::resolveOptionLabel($optionsResolver, $state['value']);

                if (blank($label)) {
                    return [];
                }

                $indicator = $filter->getIndicator();

                if (! $indicator instanceof Indicator) {
                    $indicator = Indicator::make("{$indicator}: {$label}");
                }

                return [$indicator];
            });
    }

    /**
     * @param  callable(): array<int|string, string>  $optionsResolver
     */
    private static function resolveOptionLabel(callable $optionsResolver, mixed $value): ?string
    {
        if ($value === null || $value === '') {
            return null;
        }

        $options = $optionsResolver();

        return $options[$value] ?? $options[(int) $value] ?? null;
    }

    /**
     * @param  array<int|string, string>  $options
     * @return array<int|string, string>
     */
    private static function filterOptionsForSearch(array $options, string $search, int $limit): array
    {
        $search = trim($search);

        if ($search === '') {
            return count($options) <= $limit
                ? $options
                : array_slice($options, 0, $limit, true);
        }

        $needle = mb_strtolower($search);
        $filtered = [];

        foreach ($options as $id => $label) {
            if (str_contains(mb_strtolower((string) $label), $needle)) {
                $filtered[$id] = $label;
            }

            if (count($filtered) >= $limit) {
                break;
            }
        }

        return $filtered;
    }
}
