<?php

declare(strict_types=1);

namespace App\Support;

use App\Support\Enums\DberpDocumentLineBarcodeDuplicateMode;
use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Validation\ValidationException;

/**
 * 单据明细条码快录：可合并行查找、数量累加与结果通知。
 */
final class DberpDocumentLineBarcodeQuickAddSupport
{
    /**
     * @param  Relation<Model, Model, *>|Builder<Model>  $itemsQuery
     * @param  callable(Builder<Model>): void|null  $extraConstraints  批次、来源行等不可合并字段约束
     */
    public static function findMergeableLine(
        Relation|Builder $itemsQuery,
        int $productId,
        int $productSkuId,
        int $unitId,
        ?callable $extraConstraints = null,
    ): ?Model {
        $query = $itemsQuery instanceof Relation ? $itemsQuery->getQuery() : $itemsQuery;

        $query->where('product_id', $productId)
            ->where('product_sku_id', $productSkuId)
            ->where('unit_id', $unitId);

        if ($extraConstraints !== null) {
            $extraConstraints($query);
        }

        /** @var Model|null $line */
        $line = $query->orderBy('sort_order')->orderBy('id')->first();

        return $line;
    }

    /**
     * 其他出库等含批次/序列号字段时，仅合并「无批次、无序列号」的空白行。
     *
     * @param  Builder<Model>  $query
     */
    public static function constrainMergeableInventoryRefinementLine(Builder $query): void
    {
        $query->where(function (Builder $inner): void {
            $inner->whereNull('batch_no')->orWhere('batch_no', '');
        });

        if ($query->getModel()->isFillable('source_batch_id')) {
            $query->whereNull('source_batch_id');
        }

        if ($query->getModel()->isFillable('serial_numbers')) {
            $query->where(function (Builder $inner): void {
                $inner->whereNull('serial_numbers')
                    ->orWhere('serial_numbers', '[]')
                    ->orWhere('serial_numbers', json_encode([]));
            });
        }
    }

    public static function incrementLineQuantity(Model $line, string $addQuantity, string $quantityColumn = 'quantity'): void
    {
        $current = number_format((float) (string) ($line->getAttribute($quantityColumn) ?? '0'), 4, '.', '');
        $add = number_format((float) $addQuantity, 4, '.', '');
        $line->setAttribute($quantityColumn, number_format((float) $current + (float) $add, 4, '.', ''));
        $line->save();
    }

    public static function handleDuplicateMode(
        DberpDocumentLineBarcodeDuplicateMode $mode,
        ?Model $existingLine,
        string $quantity,
        string $quantityColumn,
        callable $createLine,
    ): string {
        if ($existingLine === null) {
            $createLine();

            return 'created';
        }

        return match ($mode) {
            DberpDocumentLineBarcodeDuplicateMode::IncreaseQuantity => self::applyIncrease($existingLine, $quantity, $quantityColumn),
            DberpDocumentLineBarcodeDuplicateMode::SkipExisting => 'skipped',
            DberpDocumentLineBarcodeDuplicateMode::AddNewLine => self::applyAddNewLine($createLine),
        };
    }

    private static function applyIncrease(Model $line, string $quantity, string $quantityColumn): string
    {
        self::incrementLineQuantity($line, $quantity, $quantityColumn);

        return 'increased';
    }

    private static function applyAddNewLine(callable $createLine): string
    {
        $createLine();

        return 'created';
    }

    public static function notifyOutcome(string $outcome): void
    {
        match ($outcome) {
            'created' => Notification::make()
                ->title((string) __('dberp.notifications.barcode_quick_add_line_added'))
                ->success()
                ->send(),
            'increased' => Notification::make()
                ->title((string) __('dberp.notifications.barcode_quick_add_quantity_increased'))
                ->success()
                ->send(),
            'skipped' => Notification::make()
                ->title((string) __('dberp.notifications.barcode_quick_add_line_skipped'))
                ->warning()
                ->send(),
            default => null,
        };
    }

    public static function notifyValidation(ValidationException $exception): void
    {
        $message = collect($exception->errors())->flatten()->first()
            ?? (string) __('dberp.errors.barcode_lookup_not_found');

        Notification::make()
            ->title((string) $message)
            ->danger()
            ->send();
    }
}
