<?php

declare(strict_types=1);

namespace App\Support;

use Closure;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;

/**
 * 单据明细：先选商品、再选规格组合（两步录入，商品下拉展示编码等业务标签，可浏览或搜索）。
 */
final class DberpDocumentLineProductPickerFormFields
{
    public const PRODUCT_ID_FIELD = 'product_id';

    /**
     * @param  array<int|string, Component>  $afterUnitBlocks
     * @return list<Component>
     */
    public static function salableTwoStepComponents(
        Closure|bool $lineDisabled,
        string $quantityField,
        bool $restrictToBaseInventoryUnit,
        array $afterSpecBlocks = [],
        array $afterUnitBlocks = [],
    ): array {
        return self::twoStepComponents(
            lineDisabled: $lineDisabled,
            quantityField: $quantityField,
            restrictToBaseInventoryUnit: $restrictToBaseInventoryUnit,
            configureProductSelect: static fn (Select $field): Select => DberpProductSkuSelectOptions::configureSalableProductSelect($field),
            afterSpecBlocks: $afterSpecBlocks,
            afterUnitBlocks: $afterUnitBlocks,
        );
    }

    /**
     * @param  array<int|string, Component>  $afterSpecBlocks
     * @param  array<int|string, Component>  $afterUnitBlocks
     * @return list<Component>
     */
    public static function purchasableTwoStepComponents(
        Closure|bool $lineDisabled,
        string $quantityField,
        bool $restrictToBaseInventoryUnit,
        array $afterSpecBlocks = [],
        array $afterUnitBlocks = [],
    ): array {
        return self::twoStepComponents(
            lineDisabled: $lineDisabled,
            quantityField: $quantityField,
            restrictToBaseInventoryUnit: $restrictToBaseInventoryUnit,
            configureProductSelect: static fn (Select $field): Select => DberpProductSkuSelectOptions::configurePurchasableProductSelect($field),
            afterSpecBlocks: $afterSpecBlocks,
            afterUnitBlocks: $afterUnitBlocks,
        );
    }

    /**
     * @param  Closure(Select): Select  $configureProductSelect
     * @param  array<int|string, Component>  $afterSpecBlocks
     * @param  array<int|string, Component>  $afterUnitBlocks
     * @return list<Component>
     */
    private static function twoStepComponents(
        Closure|bool $lineDisabled,
        string $quantityField,
        bool $restrictToBaseInventoryUnit,
        Closure $configureProductSelect,
        array $afterSpecBlocks,
        array $afterUnitBlocks,
    ): array {
        $productField = self::PRODUCT_ID_FIELD;
        $resolveProduct = DberpDocumentLineUnitFormFields::resolveProductFromProductIdField($productField);

        return array_merge([
            DberpDocumentLineUnitFormFields::wireProductDriver(
                $configureProductSelect(
                    Select::make($productField)
                        ->label(__('dberp.fields.product'))
                        ->helperText(__('dberp.hints.document_line_pick_product_then_spec'))
                        ->required(),
                )->disabled(self::resolveDisabled($lineDisabled)),
                DberpDocumentLineUnitFormFields::resolveProductFromProductIdField($productField),
                function (Set $set, Get $get) use ($productField): void {
                    DberpLineProductSkuFormFields::syncAfterProductIdChanged($set, $get, $productField);
                },
            ),
        ], DberpLineProductSkuFormFields::specificationComboFields($productField, $lineDisabled), $afterSpecBlocks, DberpDocumentLineUnitFormFields::blocks(
            $resolveProduct,
            $restrictToBaseInventoryUnit,
            $lineDisabled,
        ), DberpDocumentLineUnitFormFields::withApproxBasePreviewAfterQuantityField(
            $afterUnitBlocks,
            $quantityField,
            $resolveProduct,
        ));
    }

    private static function resolveDisabled(Closure|bool $disabled): Closure
    {
        return $disabled instanceof Closure ? $disabled : fn (): bool => (bool) $disabled;
    }
}
