<?php

declare(strict_types=1);

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

namespace App\Filament\Resources\DberpSalesOrders;

use App\Business\Sales\Enums\CreditCheckResult;
use App\Business\Sales\Enums\PriceSourceType;
use App\Business\Sales\Enums\SalesOrderStatus;
use App\Business\Sales\Enums\SalesSourceType;
use App\Filament\Concerns\InteractsWithDberpPermissions;
use App\Filament\Contracts\DberpFilamentCreateSaveFirstGuidance;
use App\Filament\Contracts\DberpFilamentFormEditability;
use App\Filament\Resources\Concerns\HasDberpDocumentAddressSnapshots;
use App\Filament\Resources\DberpSalesOrders\Concerns\ProvidesDberpSalesOrderOverviewFormSections;
use App\Filament\Resources\DberpSalesOrders\Pages\CreateDberpSalesOrder;
use App\Filament\Resources\DberpSalesOrders\Pages\EditDberpSalesOrder;
use App\Filament\Resources\DberpSalesOrders\Pages\ListDberpSalesOrders;
use App\Filament\Resources\DberpSalesOrders\RelationManagers\ItemsRelationManager;
use App\Filament\Resources\DberpSalesQuotes\DberpSalesQuoteResource;
use App\Models\DberpCustomer;
use App\Models\DberpEmployee;
use App\Models\DberpSalesOrder;
use App\Services\DberpPermissionService;
use App\Support\DberpFilamentMasterDataSelectOptions;
use App\Support\DberpNumberFormatter;
use App\Support\DberpProductSkuSelectOptions;
use App\Support\DberpSalesOrderFormSupport;
use App\Support\DberpSystemProfileBusinessDefaults;
use App\Support\PluginIntegration\InvoiceAssistantFilamentBridge;
use App\Support\PluginIntegration\LightApprovalFilamentBridge;
use BackedEnum;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\HtmlString;
use UnitEnum;

class DberpSalesOrderResource extends Resource implements DberpFilamentCreateSaveFirstGuidance, DberpFilamentFormEditability
{
    use HasDberpDocumentAddressSnapshots;
    use InteractsWithDberpPermissions;
    use ProvidesDberpSalesOrderOverviewFormSections;

    protected static ?string $model = DberpSalesOrder::class;

    protected static ?string $recordTitleAttribute = 'code';

    protected static ?int $navigationSort = 20;

    protected static bool $isGloballySearchable = true;

    protected static ?int $globalSearchSort = 50;

    protected static int $globalSearchResultsLimit = 30;

    /**
     * @return array<int, string>
     */
    public static function getGloballySearchableAttributes(): array
    {
        return [
            'code',
            'customer.name',
            'customer_contact_person_snapshot',
            'customer_mobile_snapshot',
            'customer_phone_snapshot',
        ];
    }

    public static function getGlobalSearchEloquentQuery(): Builder
    {
        return parent::getEloquentQuery();
    }

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()->with(['customer', 'handlerEmployee']);
    }

    /**
     * @return array<string, string>
     */
    public static function getGlobalSearchResultDetails(Model $record): array
    {
        if (! $record instanceof DberpSalesOrder) {
            return [];
        }

        $details = [
            (string) __('dberp.global_search.detail_document_code') => (string) $record->code,
        ];

        $customerName = (string) ($record->customer?->name ?? '');
        if ($customerName !== '') {
            $details[(string) __('dberp.global_search.detail_counterparty')] = $customerName;
        }

        return array_filter($details, static fn (string $v): bool => $v !== '');
    }

    public static function modifyGlobalSearchQuery(Builder $query, string $search): void
    {
        $query->with(['customer']);
    }

    public static function isRecordFormEditableForFilament(Model $record): bool
    {
        return $record instanceof DberpSalesOrder
            && $record->status === SalesOrderStatus::Draft->value;
    }

    public static function createSaveFirstGuidanceTranslationKey(): ?string
    {
        return 'dberp.messages.save_document_first_then_manage_lines';
    }

    public static function form(Schema $schema): Schema
    {
        $isDraftForm = fn (?DberpSalesOrder $record): bool => $record === null
            || $record->status === SalesOrderStatus::Draft->value;

        return $schema
            ->components([
                ...static::salesOrderOverviewFormSections(),
                ...LightApprovalFilamentBridge::salesOrderFormSections(),
                Section::make(__('dberp.sections.sales_document'))
                    ->schema([
                        TextInput::make('code')
                            ->label(__('dberp.fields.sales_order_code'))
                            ->maxLength(64)
                            ->unique(ignoreRecord: true)
                            ->helperText(__('dberp.hints.auto_code'))
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        Select::make('customer_id')
                            ->label(__('dberp.fields.customer'))
                            ->options(fn (): array => DberpCustomer::query()
                                ->where('is_enabled', true)
                                ->orderBy('sort_order')
                                ->orderBy('id')
                                ->pluck('name', 'id')
                                ->all())
                            ->searchable()
                            ->preload()
                            ->required()
                            ->live()
                            ->afterStateUpdated(function (?int $state, Set $set, Get $get): void {
                                if (! filled($state) || filled($get('payment_term_days'))) {
                                    return;
                                }

                                $defaultDays = DberpCustomer::query()->whereKey($state)->value('default_payment_term_days');

                                if ($defaultDays !== null) {
                                    $set('payment_term_days', (int) $defaultDays);
                                }
                            })
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        Select::make('source_type')
                            ->label(__('dberp.fields.source_type'))
                            ->options(SalesSourceType::options())
                            ->default(fn (): string => DberpSystemProfileBusinessDefaults::defaultSalesSourceType())
                            ->required()
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        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()
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        DateTimePicker::make('business_time')
                            ->label(__('dberp.fields.business_time'))
                            ->default(now())
                            ->required()
                            ->seconds(false)
                            ->live()
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        TextInput::make('discount_rate')
                            ->label(__('dberp.fields.discount_rate'))
                            ->numeric()
                            ->minValue(0)
                            ->default(0)
                            ->helperText(__('dberp.hints.sales_order_discount_rate'))
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                        TextInput::make('payment_term_days')
                            ->label(__('dberp.fields.payment_term_days'))
                            ->numeric()
                            ->minValue(0)
                            ->live()
                            ->helperText(__('dberp.hints.sales_payment_term_days'))
                            ->disabled(fn (?DberpSalesOrder $record): bool => ! $isDraftForm($record)),
                        Placeholder::make('_projected_due_date')
                            ->label(__('dberp.fields.due_date'))
                            ->content(function (Get $get, ?DberpSalesOrder $record): string {
                                $projected = DberpSalesOrderFormSupport::formatProjectedDueDate($get, $record);

                                if ($projected === null) {
                                    return (string) __('dberp.hints.sales_order_due_date_pending');
                                }

                                $saved = $record?->due_date?->toDateString();

                                if ($saved !== null && $saved !== '' && $saved !== $projected) {
                                    return (string) __('dberp.messages.sales_order_due_date_preview_saved', [
                                        'projected' => $projected,
                                        'saved' => $saved,
                                    ]);
                                }

                                return (string) __('dberp.messages.sales_order_due_date_preview', [
                                    'date' => $projected,
                                ]);
                            })
                            ->visible(fn (?DberpSalesOrder $record): bool => $isDraftForm($record)),
                        DatePicker::make('due_date')
                            ->label(__('dberp.fields.due_date'))
                            ->disabled()
                            ->dehydrated(false)
                            ->visible(fn (?DberpSalesOrder $record): bool => $record !== null && ! $isDraftForm($record)),
                        Textarea::make('remark')
                            ->label(__('dberp.fields.remark'))
                            ->rows(3)
                            ->columnSpanFull()
                            ->disabled(fn (?DberpSalesOrder $record): bool => $record !== null && $record->status !== SalesOrderStatus::Draft->value),
                    ])
                    ->columns(2),
                Section::make(__('dberp.sections.sales_order_quote_source'))
                    ->visible(fn (?DberpSalesOrder $record): bool => $record !== null
                        && filled($record->quote_id)
                        && filled($record->quote_code))
                    ->schema([
                        Placeholder::make('_source_quote_link')
                            ->label(__('dberp.fields.quote_code'))
                            ->content(function (?DberpSalesOrder $record): Htmlable|string {
                                if (! $record instanceof DberpSalesOrder || ! filled($record->quote_id)) {
                                    return '-';
                                }

                                $url = DberpSalesQuoteResource::getUrl('edit', ['record' => $record->quote_id]);
                                $code = e((string) $record->quote_code);

                                return new HtmlString(
                                    '<a href="'.e($url).'" class="text-primary-600 hover:underline font-medium">'.$code.'</a>',
                                );
                            }),
                    ])
                    ->columns(1),
                Section::make(__('dberp.sections.sales_order_credit'))
                    ->description(__('dberp.hints.sales_order_credit_after_confirm'))
                    ->visible(fn (?DberpSalesOrder $record): bool => $record !== null && filled($record->credit_check_result))
                    ->collapsed()
                    ->schema([
                        TextInput::make('credit_check_result')
                            ->label(__('dberp.fields.credit_check_result'))
                            ->disabled()
                            ->dehydrated(false)
                            ->formatStateUsing(fn (?string $state): string => $state
                                ? (__('dberp.options.credit_check_result.'.$state) ?: $state)
                                : '-'),
                        Textarea::make('credit_override_reason')
                            ->label(__('dberp.fields.credit_override_reason'))
                            ->disabled()
                            ->dehydrated(false)
                            ->rows(2)
                            ->visible(fn (?DberpSalesOrder $record): bool => filled($record?->credit_override_reason)),
                        Placeholder::make('_credit_checked_at')
                            ->label(__('dberp.fields.credit_checked_at'))
                            ->content(fn (?DberpSalesOrder $record): string => $record?->credit_checked_at?->format('Y-m-d H:i') ?? '-'),
                    ])
                    ->columns(2),
                static::getDberpCounterpartySnapshotSection(__('dberp.fields.customer_address_snapshot'), 'customer'),
            ])->columns(1);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->defaultSort('business_time', 'desc')
            ->columns([
                TextColumn::make('code')
                    ->label(__('dberp.fields.sales_order_code'))
                    ->searchable(),
                TextColumn::make('customer.name')
                    ->label(__('dberp.fields.customer'))
                    ->searchable(),
                TextColumn::make('source_type')
                    ->label(__('dberp.fields.source_type'))
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => SalesSourceType::tryFrom((string) $state)?->label() ?? (string) $state)
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('handlerEmployee.name')
                    ->label(__('dberp.fields.handler_employee'))
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('status')
                    ->label(__('dberp.fields.status'))
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => SalesOrderStatus::tryFrom((string) $state)?->label() ?? (string) $state),
                TextColumn::make('quote_code')
                    ->label(__('dberp.fields.quote_code'))
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('price_source_type')
                    ->label(__('dberp.fields.price_source_type'))
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => PriceSourceType::tryFrom((string) $state)?->label() ?? ((string) $state === '' ? '-' : (string) $state))
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('total_quantity')
                    ->label(__('dberp.fields.quantity'))
                    ->formatStateUsing(fn ($state): string => DberpNumberFormatter::quantity($state)),
                TextColumn::make('total_amount')
                    ->label(__('dberp.fields.line_amount'))
                    ->formatStateUsing(fn ($state): string => DberpNumberFormatter::money($state))
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('discount_amount')
                    ->label(__('dberp.fields.discount_amount'))
                    ->formatStateUsing(fn ($state): string => DberpNumberFormatter::money($state))
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('final_amount')
                    ->label(__('dberp.fields.final_amount'))
                    ->formatStateUsing(fn ($state): string => DberpNumberFormatter::money($state)),
                TextColumn::make('business_time')
                    ->label(__('dberp.fields.business_time'))
                    ->dateTime(),
                TextColumn::make('due_date')
                    ->label(__('dberp.fields.due_date'))
                    ->date()
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('credit_check_result')
                    ->label(__('dberp.fields.credit_check_result'))
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => $state ? (__('dberp.options.credit_check_result.'.$state) ?: $state) : '-')
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('confirmed_at')
                    ->label(__('dberp.fields.confirmed_at'))
                    ->dateTime()
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('closed_at')
                    ->label(__('dberp.fields.closed_at'))
                    ->dateTime()
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('voided_at')
                    ->label(__('dberp.fields.voided_at'))
                    ->dateTime()
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters([
                SelectFilter::make('status')
                    ->label(__('dberp.fields.status'))
                    ->options(__('dberp.options.sales_order_status')),
                SelectFilter::make('customer_id')
                    ->label(__('dberp.fields.customer'))
                    ->relationship('customer', 'name'),
                SelectFilter::make('source_type')
                    ->label(__('dberp.fields.source_type'))
                    ->options(SalesSourceType::options()),
                SelectFilter::make('credit_check_result')
                    ->label(__('dberp.fields.credit_check_result'))
                    ->options(CreditCheckResult::options()),
                SelectFilter::make('handler_employee_id')
                    ->label(__('dberp.fields.handler_employee'))
                    ->options(fn (): array => DberpEmployee::selectOptionsForFilament())
                    ->searchable()
                    ->preload(),
                SelectFilter::make('attention_status')
                    ->label(__('dberp.fields.attention_status'))
                    ->options(__('dberp.options.sales_order_attention_status'))
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        if (! is_string($value) || $value === '') {
                            return $query;
                        }

                        return match ($value) {
                            'released_by_override' => $query->where('credit_check_result', CreditCheckResult::ReleasedByOverride->value),
                            default => $query,
                        };
                    }),
                Filter::make('product_id')
                    ->schema([
                        Select::make('product_id')
                            ->label(__('dberp.fields.product'))
                            ->options(fn (): array => DberpFilamentMasterDataSelectOptions::products(
                                enabledOnly: true,
                                scope: DberpProductSkuSelectOptions::PRODUCT_SCOPE_SALABLE,
                            ))
                            ->searchable()
                            ->preload(),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        return $query->when($data['product_id'] ?? null, fn (Builder $builder, $productId): Builder => $builder->whereHas('items', fn (Builder $itemQuery): Builder => $itemQuery->where('product_id', $productId)));
                    }),
                Filter::make('business_time_range')
                    ->schema([
                        DatePicker::make('from')->label(__('dberp.fields.date_from')),
                        DatePicker::make('until')->label(__('dberp.fields.date_until')),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        return $query
                            ->when($data['from'] ?? null, fn (Builder $builder, $date): Builder => $builder->whereDate('business_time', '>=', $date))
                            ->when($data['until'] ?? null, fn (Builder $builder, $date): Builder => $builder->whereDate('business_time', '<=', $date));
                    }),
            ]);
    }

    public static function canViewAny(): bool
    {
        return static::allowsPermission(DberpPermissionService::VIEW_SALES_ORDER);
    }

    public static function canView(Model $record): bool
    {
        return static::canViewAny();
    }

    public static function canCreate(): bool
    {
        return static::allowsPermission(DberpPermissionService::CREATE_SALES_ORDER);
    }

    public static function canEdit(Model $record): bool
    {
        return static::allowsPermission(DberpPermissionService::EDIT_SALES_ORDER);
    }

    public static function canDelete(Model $record): bool
    {
        return static::allowsPermission(DberpPermissionService::DELETE_SALES_ORDER);
    }

    public static function getRelations(): array
    {
        return [
            ItemsRelationManager::class,
            ...InvoiceAssistantFilamentBridge::salesOrderRelationManagers(),
            ...LightApprovalFilamentBridge::salesOrderRelationManagers(),
        ];
    }

    public static function getPages(): array
    {
        return [
            'index' => ListDberpSalesOrders::route('/'),
            'create' => CreateDberpSalesOrder::route('/create'),
            'edit' => EditDberpSalesOrder::route('/{record}/edit'),
        ];
    }

    public static function getNavigationGroup(): string|UnitEnum|null
    {
        return __('dberp.nav.sales');
    }

    public static function getNavigationIcon(): string|BackedEnum|Htmlable|null
    {
        return null;
    }

    public static function getModelLabel(): string
    {
        return __('dberp.resources.sales_order.label');
    }

    public static function getPluralModelLabel(): string
    {
        return __('dberp.resources.sales_order.plural');
    }
}
