<?php

declare(strict_types=1);

namespace App\Filament\Resources\DberpPurchaseRequests\Concerns;

use App\Business\Purchase\Enums\PurchaseRequestStatus;
use App\Business\Purchase\PurchaseInquiryGenerationService;
use App\Business\Purchase\PurchaseRequestConversionService;
use App\Filament\Pages\DberpPurchaseInquiryComparisonPage;
use App\Filament\Support\DberpFilamentNumberFields;
use App\Models\DberpEmployee;
use App\Models\DberpPurchaseRequest;
use App\Models\DberpPurchaseRequestItem;
use App\Models\DberpSupplier;
use App\Services\DberpPermissionService;
use App\Support\DberpDocumentLineSpecPresentation;
use App\Support\DberpNumberFormatter;
use Filament\Actions\Action;
use Filament\Forms\Components\Component;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Notifications\Notification;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;

trait ProvidesDberpPurchaseRequestCollaborationHeaderActions
{
    /**
     * @return list<Action>
     */
    protected function purchaseRequestCollaborationHeaderActions(): array
    {
        return [
            $this->makeGeneratePurchaseInquiryAction(),
            $this->makeConvertSelectedToPurchaseOrderAction(),
            $this->makeConvertRemainingToPurchaseOrderAction(),
            $this->makeViewPurchaseInquiryComparisonAction(),
        ];
    }

    protected function makeGeneratePurchaseInquiryAction(): Action
    {
        return Action::make('generate_purchase_inquiry')
            ->label(__('dberp.actions.generate_purchase_inquiry'))
            ->modalHeading(__('dberp.actions.generate_purchase_inquiry'))
            ->modalDescription(__('dberp.hints.purchase_request_generate_inquiry'))
            ->visible(fn (DberpPurchaseRequest $record): bool => app(DberpPermissionService::class)->allows(Auth::user(), DberpPermissionService::CREATE_PURCHASE_INQUIRY_QUOTE)
                && $this->purchaseRequestAllowsDownstreamProcessing($record)
                && $this->purchaseRequestHasInquirableLines($record))
            ->fillForm(fn (DberpPurchaseRequest $record): array => $this->buildPurchaseRequestLineSelectionFormDefaults($record, 'inquiry_quantity'))
            ->form($this->purchaseRequestInquiryGenerationFormSchema())
            ->action(function (array $data): void {
                $request = $this->getPurchaseRequestCollaborationRecord();

                $inquiry = app(PurchaseInquiryGenerationService::class)->generateFromPurchaseRequest(
                    request: $request,
                    itemQuantities: $this->extractLineQuantitiesFromForm($data),
                    supplierIds: array_values(array_map(intval(...), (array) ($data['supplier_ids'] ?? []))),
                    handlerEmployeeId: ($data['handler_employee_id'] ?? null) ? (int) $data['handler_employee_id'] : null,
                    inquiryDate: $data['inquiry_date'] ?? now()->toDateString(),
                    remark: $data['remark'] ?? null,
                );

                $this->refreshPurchaseRequestCollaborationRecord();

                Notification::make()
                    ->title((string) __('dberp.notifications.saved'))
                    ->body((string) __('dberp.messages.purchase_request_inquiry_generated_success', ['code' => $inquiry->code]))
                    ->success()
                    ->send();
            });
    }

    protected function makeConvertSelectedToPurchaseOrderAction(): Action
    {
        return Action::make('convert_selected_to_purchase_order')
            ->label(__('dberp.actions.convert_selected_to_purchase_order'))
            ->modalHeading(__('dberp.actions.convert_selected_to_purchase_order'))
            ->modalDescription(__('dberp.hints.purchase_request_convert_selected'))
            ->visible(fn (DberpPurchaseRequest $record): bool => app(DberpPermissionService::class)->allows(Auth::user(), DberpPermissionService::CONVERT_PURCHASE_REQUEST)
                && $this->purchaseRequestAllowsDownstreamProcessing($record)
                && $this->purchaseRequestHasInquirableLines($record))
            ->fillForm(fn (DberpPurchaseRequest $record): array => array_merge(
                $this->buildPurchaseRequestLineSelectionFormDefaults($record, 'convert_quantity'),
                [
                    'supplier_id' => $record->suggested_supplier_id,
                    'business_time' => now(),
                ],
            ))
            ->form($this->purchaseRequestSelectedConversionFormSchema())
            ->action(function (array $data): void {
                $request = $this->getPurchaseRequestCollaborationRecord();

                $orders = app(PurchaseRequestConversionService::class)->convertToPurchaseOrders(
                    request: $request,
                    supplierId: (int) $data['supplier_id'],
                    itemQuantities: $this->extractLineQuantitiesFromForm($data, 'convert_quantity'),
                    handlerEmployeeId: ($data['handler_employee_id'] ?? null) ? (int) $data['handler_employee_id'] : null,
                    businessTime: $data['business_time'] ?? now(),
                    remark: $data['remark'] ?? null,
                );

                $this->refreshPurchaseRequestCollaborationRecord();

                $body = count($orders) === 1
                    ? (string) __('dberp.messages.purchase_request_convert_success', ['code' => $orders[0]->code])
                    : (string) __('dberp.messages.purchase_request_convert_success_multiple', [
                        'count' => count($orders),
                        'codes' => collect($orders)->pluck('code')->implode('、'),
                    ]);

                Notification::make()
                    ->title((string) __('dberp.notifications.saved'))
                    ->body($body)
                    ->success()
                    ->send();
            });
    }

    protected function makeConvertRemainingToPurchaseOrderAction(): Action
    {
        return Action::make('convert_remaining_to_purchase_order')
            ->label(__('dberp.actions.convert_to_purchase_order'))
            ->requiresConfirmation()
            ->modalDescription(__('dberp.hints.purchase_request_convert'))
            ->visible(fn (DberpPurchaseRequest $record): bool => app(DberpPermissionService::class)->allows(Auth::user(), DberpPermissionService::CONVERT_PURCHASE_REQUEST)
                && $this->purchaseRequestAllowsDownstreamProcessing($record)
                && (float) $record->remaining_quantity > 0.00005)
            ->form([
                Select::make('supplier_id')
                    ->label(__('dberp.fields.supplier'))
                    ->options(fn (): array => DberpSupplier::query()->where('is_enabled', true)->orderBy('sort_order')->orderBy('id')->pluck('name', 'id')->all())
                    ->default(fn (): ?int => $this->getPurchaseRequestCollaborationRecord()->suggested_supplier_id)
                    ->searchable()
                    ->preload()
                    ->required(),
                Select::make('handler_employee_id')
                    ->label(__('dberp.fields.handler_employee'))
                    ->options(fn (): array => DberpEmployee::selectOptionsForFilament(fn (Builder $q) => $q->where('is_enabled', true)))
                    ->searchable()
                    ->preload(),
                DateTimePicker::make('business_time')
                    ->label(__('dberp.fields.business_time'))
                    ->default(fn () => now())
                    ->seconds(false)
                    ->required(),
                Textarea::make('remark')
                    ->label(__('dberp.fields.remark'))
                    ->rows(3),
            ])
            ->action(function (array $data): void {
                $request = $this->getPurchaseRequestCollaborationRecord();

                $orders = app(PurchaseRequestConversionService::class)->convertRemainingToPurchaseOrders(
                    request: $request,
                    supplierId: (int) $data['supplier_id'],
                    handlerEmployeeId: ($data['handler_employee_id'] ?? null) ? (int) $data['handler_employee_id'] : null,
                    businessTime: $data['business_time'] ?? now(),
                    remark: $data['remark'] ?? null,
                );

                $this->refreshPurchaseRequestCollaborationRecord();

                $body = count($orders) === 1
                    ? (string) __('dberp.messages.purchase_request_convert_success', ['code' => $orders[0]->code])
                    : (string) __('dberp.messages.purchase_request_convert_success_multiple', [
                        'count' => count($orders),
                        'codes' => collect($orders)->pluck('code')->implode('、'),
                    ]);

                Notification::make()
                    ->title((string) __('dberp.notifications.saved'))
                    ->body($body)
                    ->success()
                    ->send();
            });
    }

    protected function makeViewPurchaseInquiryComparisonAction(): Action
    {
        return Action::make('view_purchase_inquiry_comparison')
            ->label(__('dberp.actions.view_purchase_inquiry_comparison'))
            ->visible(fn (DberpPurchaseRequest $record): bool => app(DberpPermissionService::class)->allows(Auth::user(), DberpPermissionService::NAV_PURCHASE_INQUIRY_COMPARISON)
                && in_array($record->status, [
                    PurchaseRequestStatus::Pending->value,
                    PurchaseRequestStatus::PartiallyConverted->value,
                    PurchaseRequestStatus::Converted->value,
                    PurchaseRequestStatus::Closed->value,
                ], true))
            ->url(fn (DberpPurchaseRequest $record): string => DberpPurchaseInquiryComparisonPage::getUrl([
                'purchase_request_id' => $record->getKey(),
            ]))
            ->openUrlInNewTab();
    }

    /**
     * @return list<Component|\Filament\Schemas\Components\Component>
     */
    protected function purchaseRequestInquiryGenerationFormSchema(): array
    {
        return array_merge(
            $this->purchaseRequestLineSelectionRepeaterSchema('inquiry_quantity', __('dberp.fields.inquiry_quantity')),
            [
                Select::make('supplier_ids')
                    ->label(__('dberp.fields.inquiry_suppliers'))
                    ->options(fn (): array => DberpSupplier::query()->where('is_enabled', true)->orderBy('sort_order')->orderBy('id')->pluck('name', 'id')->all())
                    ->default(fn (): array => ($supplierId = $this->getPurchaseRequestCollaborationRecord()->suggested_supplier_id) ? [(int) $supplierId] : [])
                    ->multiple()
                    ->searchable()
                    ->preload()
                    ->required(),
                DatePicker::make('inquiry_date')
                    ->label(__('dberp.fields.purchase_inquiry_date'))
                    ->default(fn () => now()->toDateString())
                    ->required(),
                Select::make('handler_employee_id')
                    ->label(__('dberp.fields.handler_employee'))
                    ->options(fn (): array => DberpEmployee::selectOptionsForFilament(fn (Builder $q) => $q->where('is_enabled', true)))
                    ->default(fn (): ?int => $this->getPurchaseRequestCollaborationRecord()->applicant_employee_id)
                    ->searchable()
                    ->preload(),
                Textarea::make('remark')
                    ->label(__('dberp.fields.remark'))
                    ->rows(2),
            ],
        );
    }

    /**
     * @return list<Component|\Filament\Schemas\Components\Component>
     */
    protected function purchaseRequestSelectedConversionFormSchema(): array
    {
        return array_merge(
            $this->purchaseRequestLineSelectionRepeaterSchema('convert_quantity', __('dberp.fields.convert_quantity')),
            [
                Select::make('supplier_id')
                    ->label(__('dberp.fields.supplier'))
                    ->options(fn (): array => DberpSupplier::query()->where('is_enabled', true)->orderBy('sort_order')->orderBy('id')->pluck('name', 'id')->all())
                    ->searchable()
                    ->preload()
                    ->required(),
                Select::make('handler_employee_id')
                    ->label(__('dberp.fields.handler_employee'))
                    ->options(fn (): array => DberpEmployee::selectOptionsForFilament(fn (Builder $q) => $q->where('is_enabled', true)))
                    ->searchable()
                    ->preload(),
                DateTimePicker::make('business_time')
                    ->label(__('dberp.fields.business_time'))
                    ->default(fn () => now())
                    ->seconds(false)
                    ->required(),
                Textarea::make('remark')
                    ->label(__('dberp.fields.remark'))
                    ->rows(3),
            ],
        );
    }

    /**
     * @return list<Component|\Filament\Schemas\Components\Component>
     */
    protected function purchaseRequestLineSelectionRepeaterSchema(string $quantityField, string $quantityLabel): array
    {
        return [
            Repeater::make('lines')
                ->label(__('dberp.sections.purchase_request_line_selection'))
                ->columnSpanFull()
                ->addable(false)
                ->deletable(false)
                ->reorderable(false)
                ->defaultItems(0)
                ->schema([
                    Hidden::make('purchase_request_item_id'),
                    TextInput::make('product_label')
                        ->label(__('dberp.fields.product'))
                        ->disabled()
                        ->dehydrated(false),
                    TextInput::make('remaining_quantity')
                        ->label(__('dberp.fields.remaining_quantity'))
                        ->disabled()
                        ->dehydrated(false)
                        ->formatStateUsing(fn ($state): string => DberpNumberFormatter::quantity($state)),
                    DberpFilamentNumberFields::documentLineQuantityInput(
                        TextInput::make($quantityField)
                            ->label($quantityLabel)
                            ->required(),
                    ),
                ])
                ->columns(3),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    protected function buildPurchaseRequestLineSelectionFormDefaults(DberpPurchaseRequest $record, string $quantityField): array
    {
        $record->loadMissing(['items.product', 'items.productSku']);

        return [
            'lines' => $record->items
                ->filter(fn (DberpPurchaseRequestItem $item): bool => (float) $item->remaining_quantity > 0.00005)
                ->map(fn (DberpPurchaseRequestItem $item): array => [
                    'purchase_request_item_id' => $item->getKey(),
                    'product_label' => $this->formatPurchaseRequestItemProductLabel($item),
                    'remaining_quantity' => $item->remaining_quantity,
                    $quantityField => $item->remaining_quantity,
                ])
                ->values()
                ->all(),
        ];
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<int, float>
     */
    protected function extractLineQuantitiesFromForm(array $data, string $quantityField = 'inquiry_quantity'): array
    {
        $quantities = [];

        foreach ((array) ($data['lines'] ?? []) as $line) {
            if (! is_array($line)) {
                continue;
            }

            $itemId = (int) ($line['purchase_request_item_id'] ?? 0);
            $quantity = (float) ($line[$quantityField] ?? 0);

            if ($itemId <= 0 || $quantity <= 0) {
                continue;
            }

            $quantities[$itemId] = $quantity;
        }

        return $quantities;
    }

    protected function formatPurchaseRequestItemProductLabel(DberpPurchaseRequestItem $item): string
    {
        $item->loadMissing(DberpDocumentLineSpecPresentation::eagerLoadsForDocumentLines(['product', 'productSku']));

        $name = $item->product?->name ?? (string) __('dberp.fields.product').' #'.$item->product_id;
        $spec = DberpDocumentLineSpecPresentation::lineSpecSummary($item);

        return $spec !== '' ? $name.' · '.$spec : $name;
    }

    protected function purchaseRequestAllowsDownstreamProcessing(DberpPurchaseRequest $record): bool
    {
        return in_array($record->status, [
            PurchaseRequestStatus::Pending->value,
            PurchaseRequestStatus::PartiallyConverted->value,
        ], true);
    }

    protected function purchaseRequestHasInquirableLines(DberpPurchaseRequest $record): bool
    {
        $record->loadMissing('items');

        return $record->items->contains(
            fn (DberpPurchaseRequestItem $item): bool => (float) $item->remaining_quantity > 0.00005,
        );
    }

    abstract protected function getPurchaseRequestCollaborationRecord(): DberpPurchaseRequest;

    protected function refreshPurchaseRequestCollaborationRecord(): void
    {
        $this->getPurchaseRequestCollaborationRecord()->refresh();
    }
}
