<?php

declare(strict_types=1);

namespace App\Filament\Support;

use App\Support\DberpNumberFormatter;
use Filament\Forms\Components\TextInput;
use Filament\Tables\Columns\TextColumn;

/**
 * Filament 表单/表格数字展示与输入，统一遵循 {@see DberpNumberFormatter} 与系统档案小数位。
 */
final class DberpFilamentNumberFields
{
    /** 商品档案辅助单位换算数量持久化为 decimal(18,6)，与单据数量小数位配置独立。 */
    private const PRODUCT_AUXILIARY_CONVERSION_DECIMALS = 6;

    /**
     * 只读金额展示（详情页禁用字段等），不参与提交。
     */
    public static function displayMoney(TextInput $field): TextInput
    {
        return $field->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::money($state),
        );
    }

    public static function displayPrice(TextInput $field): TextInput
    {
        return $field->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::price($state),
        );
    }

    public static function displayQuantity(TextInput $field): TextInput
    {
        return $field->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::quantity($state),
        );
    }

    /**
     * 可编辑金额输入：避免 HTML step 触发浏览器原生小数位校验；服务端按系统档案金额小数位校验（最多 N 位）。
     *
     * @param  float|null  $minValue  为 null 时使用大于 0 的最小有效金额；业务允许为 0 时传入 0。
     */
    public static function moneyInput(TextInput $field, bool $withHelperText = true, ?float $minValue = null): TextInput
    {
        $decimals = DberpNumberFormatter::amountDecimalPlaces();
        $min = $minValue ?? self::positiveMoneyMinValue($decimals);

        self::applyDecimalNumericInput($field, $decimals, $min);

        if ($withHelperText) {
            $field->helperText(self::moneyInputHelperText($decimals));
        }

        return $field;
    }

    /**
     * 可编辑数量输入：避免 HTML step 触发浏览器原生小数位校验；服务端按小数位校验（最多 N 位，不要求凑满 N 位）。
     *
     * @param  float|null  $minValue  为 null 时使用系统默认最小正数；业务允许为 0 时传入 0。
     * @param  int|null  $decimalPlaces  为 null 时读取系统档案数量小数位。
     */
    public static function quantityInput(
        TextInput $field,
        bool $withHelperText = true,
        ?float $minValue = null,
        ?int $decimalPlaces = null,
    ): TextInput {
        $decimals = $decimalPlaces ?? DberpNumberFormatter::quantityDecimalPlaces();
        $min = $minValue ?? self::quantityMinValue($decimals);

        self::applyDecimalNumericInput($field, $decimals, $min);

        if ($withHelperText) {
            $field->helperText(self::quantityInputHelperText($decimals));
        }

        return $field;
    }

    /**
     * 商品档案辅助单位换算数量（decimal 6 位，须 live 刷新换算预览）。
     */
    public static function productAuxiliaryConversionQuantityInput(TextInput $field): TextInput
    {
        return self::quantityInput(
            $field,
            withHelperText: false,
            decimalPlaces: self::PRODUCT_AUXILIARY_CONVERSION_DECIMALS,
        )->live();
    }

    /**
     * 单据明细录入数量：在 {@see quantityInput()} 基础上联动刷新主单位约算预览。
     *
     * @param  float|null  $minValue  为 null 时使用系统默认最小正数；业务允许为 0 时传入 0。
     */
    public static function documentLineQuantityInput(TextInput $field, bool $withHelperText = true, ?float $minValue = null): TextInput
    {
        return self::quantityInput($field, $withHelperText, $minValue)->live();
    }

    public static function moneyColumn(TextColumn $column): TextColumn
    {
        return $column->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::money($state),
        );
    }

    public static function priceColumn(TextColumn $column): TextColumn
    {
        return $column->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::price($state),
        );
    }

    public static function quantityColumn(TextColumn $column): TextColumn
    {
        return $column->formatStateUsing(
            fn ($state): string => DberpNumberFormatter::quantity($state),
        );
    }

    public static function positiveMoneyMinValue(?int $decimals = null): float
    {
        $decimals ??= DberpNumberFormatter::amountDecimalPlaces();

        if ($decimals === 0) {
            return 1.0;
        }

        return 10 ** (-$decimals);
    }

    private static function applyDecimalNumericInput(TextInput $field, int $decimals, float $min): void
    {
        if ($decimals === 0) {
            $field->integer()->minValue((int) $min);

            return;
        }

        $field
            ->numeric()
            ->minValue($min)
            ->rule("decimal:0,{$decimals}");
    }

    private static function quantityMinValue(int $decimals): float
    {
        if ($decimals === 0) {
            return 1.0;
        }

        return 10 ** (-$decimals);
    }

    private static function moneyInputHelperText(int $decimals): string
    {
        if ($decimals === 0) {
            return (string) __('dberp.hints.money_entry_amount_integer');
        }

        return (string) __('dberp.hints.money_entry_amount', ['decimal' => $decimals]);
    }

    private static function quantityInputHelperText(int $decimals): string
    {
        if ($decimals === 0) {
            return (string) __('dberp.hints.document_line_entry_quantity_integer');
        }

        return (string) __('dberp.hints.document_line_entry_quantity', ['decimal' => $decimals]);
    }
}
