<?php

namespace App\Filament\Resources\RemoteOrderResource\Pages;

use App\Enums\OrderStatus as OrderStatusEnum;
use App\Enums\OrderType as OrderTypeEnum;
use App\Filament\Resources\OrderResource;
use App\Filament\Resources\RemoteOrderResource;
use App\Models\OrderDetail;
use App\Models\OrderFlow;
use App\Models\OrderStatusCatalog;
use App\Models\OrderTypeCatalog;
use App\Models\Province;
use App\Models\Canton;
use App\Models\District;
use App\Models\RemoteClient;
use App\Models\RemoteOrder;
use App\Models\RemoteOrderDetail;
use App\Models\Store;
use App\Repositories\OrderRepository;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Form;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\Page;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class CreateRedespacho extends Page implements HasForms
{
    use InteractsWithForms;

    protected static string $resource = RemoteOrderResource::class;

    protected static string $view = 'filament.resources.remote-order-resource.pages.create-redespacho';

    public RemoteOrder $remoteOrder;

    public ?RemoteClient $client = null;

    /** @var \Illuminate\Support\Collection<int, RemoteOrderDetail> */
    public $parentDetails;

    public OrderFlow $parentFlow;

    public float $originalCharged = 0.0;

    public array $data = [];

    public static function canAccess(array $parameters = []): bool
    {
        return auth()->user()?->can('orders.update') ?? false;
    }

    public function mount(int|string $record): void
    {
        abort_unless(static::canAccess(), 403);

        /** @var RemoteOrder $remote */
        $remote = RemoteOrder::query()
            ->with(['orderFlow', 'orderFlow.store'])
            ->findOrFail($record);

        $this->remoteOrder = $remote;

        $repo = app(OrderRepository::class);
        $this->parentFlow = $remote->orderFlow ?: $repo->ensureFlowForRemote($remote);

        if ($this->parentFlow->isTerminalState()) {
            Notification::make()
                ->title('Orden en estado final')
                ->body('Esta orden se encuentra en un estado final y no puede ser modificada.')
                ->danger()
                ->send();

            $this->redirect(RemoteOrderResource::getUrl('view', ['record' => $remote]));
            return;
        }

        $clientId = (int) ($remote->idClient ?? 0);
        $this->client = $clientId > 0 ? RemoteClient::query()->find($clientId) : null;

        $this->parentDetails = RemoteOrderDetail::query()
            ->where('order_reference', (string) ($remote->order_reference ?? ''))
            ->get();

        $this->originalCharged = (float) ($remote->total_paidTI ?? $remote->total_paidTE ?? 0);
        if ($this->originalCharged <= 0) {
            $sum = (float) $this->parentDetails
                ->sum(fn ($d) => (float) ($d->total_priceTI ?? $d->total_priceTE ?? 0));
            $this->originalCharged = $sum;
        }

        $this->form->fill([
            'store_id' => $this->parentFlow->store_id,
            'customer_name' => (string) ($this->client?->name ?? $this->parentFlow->customer_name ?? ''),
            'customer_identification' => (string) ($this->client?->cedula ?? $this->parentFlow->customer_identification ?? ''),
            'customer_email' => (string) ($this->client?->email ?? $this->parentFlow->customer_email ?? ''),
            'customer_phone' => (string) ($this->client?->phone ?? $this->parentFlow->customer_phone ?? ''),
            'customer_legal_activity' => (string) ($this->client?->actividad ?? $this->parentFlow->customer_legal_activity ?? ''),
            'postal_code' => (string) ($this->parentFlow->postal_code ?? ''),
            'province_id' => $this->parentFlow->province_id,
            'canton_id' => $this->parentFlow->canton_id,
            'district_id' => $this->parentFlow->district_id,
            'address_detail' => (string) ($this->parentFlow->address_detail ?? ''),
            'items' => $this->parentDetails
                ->map(function ($d): array {
                    $qty = (int) ($d->product_quantity ?? 0);
                    $qty = max(1, $qty);

                    $unitTI = (float) ($d->product_priceTI ?? 0);
                    $unitTE = (float) ($d->product_priceTE ?? $unitTI);

                    return [
                        'product_remote_id' => null,
                        'sku' => (string) ($d->product_reference ?? ''),
                        'name' => (string) ($d->product_name ?? ''),
                        'quantity' => $qty,
                        'unit_price_ti' => $unitTI,
                        'unit_price_te' => $unitTE,
                        'tax' => (float) ($d->product_tax ?? 0),
                    ];
                })
                ->values()
                ->all(),
            'ack_cancel_related' => false,
            'ack_irreversible' => false,
            'ack_inventory' => false,
        ]);
    }

    public function form(Form $form): Form
    {
        return $form
            ->statePath('data')
            ->schema([
                Section::make('Cliente')
                    ->columns(3)
                    ->schema([
                        Select::make('store_id')
                            ->label('Tienda')
                            ->options(fn (): array => Store::query()->orderBy('name')->pluck('name', 'id')->all())
                            ->searchable()
                            ->preload(),

                        TextInput::make('customer_name')
                            ->label('Nombre')
                            ->required()
                            ->maxLength(255),

                        TextInput::make('customer_identification')
                            ->label('Identificación')
                            ->maxLength(255),

                        TextInput::make('customer_email')
                            ->label('Correo')
                            ->email()
                            ->maxLength(255),

                        TextInput::make('customer_phone')
                            ->label('Teléfono')
                            ->maxLength(50),

                        TextInput::make('customer_legal_activity')
                            ->label('Actividad jurídica')
                            ->maxLength(50),

                        TextInput::make('postal_code')
                            ->label('Código postal')
                            ->maxLength(20),

                        Select::make('province_id')
                            ->label('Provincia')
                            ->options(fn () => Province::query()->orderBy('name')->pluck('name', 'id_province')->all())
                            ->searchable()
                            ->preload()
                            ->reactive()
                            ->afterStateUpdated(function (callable $set): void {
                                $set('canton_id', null);
                                $set('district_id', null);
                            }),

                        Select::make('canton_id')
                            ->label('Cantón')
                            ->options(function (callable $get): array {
                                $provinceId = (int) ($get('province_id') ?? 0);
                                if ($provinceId <= 0) {
                                    return [];
                                }

                                return Canton::query()
                                    ->where('province_id', $provinceId)
                                    ->orderBy('name')
                                    ->pluck('name', 'id_canton')
                                    ->all();
                            })
                            ->searchable()
                            ->reactive()
                            ->afterStateUpdated(fn (callable $set) => $set('district_id', null)),

                        Select::make('district_id')
                            ->label('Distrito')
                            ->options(function (callable $get): array {
                                $cantonId = (int) ($get('canton_id') ?? 0);
                                if ($cantonId <= 0) {
                                    return [];
                                }

                                return District::query()
                                    ->where('canton_id', $cantonId)
                                    ->orderBy('name')
                                    ->pluck('name', 'id_district')
                                    ->all();
                            })
                            ->searchable(),

                        Textarea::make('address_detail')
                            ->label('Dirección detallada')
                            ->rows(3)
                            ->columnSpanFull()
                            ->maxLength(1000),
                    ]),

                Section::make('Productos')
                    ->schema([
                        Repeater::make('items')
                            ->label('Productos del redespacho')
                            ->defaultItems(0)
                            ->addable(false)
                            ->schema([
                                TextInput::make('sku')
                                    ->label('SKU')
                                    ->disabled()
                                    ->dehydrated(),

                                TextInput::make('name')
                                    ->label('Nombre')
                                    ->disabled()
                                    ->dehydrated(),

                                TextInput::make('quantity')
                                    ->label('Cantidad')
                                    ->numeric()
                                    ->minValue(1)
                                    ->required()
                                    ->live(),

                                TextInput::make('unit_price_ti')
                                    ->label('Precio unitario (TI)')
                                    ->numeric()
                                    ->minValue(0)
                                    ->required()
                                    ->live(),

                                TextInput::make('unit_price_te')
                                    ->label('Precio unitario (TE)')
                                    ->numeric()
                                    ->minValue(0)
                                    ->required()
                                    ->live(),

                                Placeholder::make('subtotal')
                                    ->label('Subtotal')
                                    ->content(function (callable $get): string {
                                        $qty = (float) ($get('quantity') ?? 0);
                                        $unit = (float) ($get('unit_price_ti') ?? 0);
                                        return number_format($qty * $unit, 2);
                                    }),
                            ])
                            ->columns(6)
                            ->required(),
                    ]),

                Section::make('Totales')
                    ->columns(3)
                    ->schema([
                        Placeholder::make('original_charged')
                            ->label('Monto cobrado original')
                            ->content(fn (): string => number_format($this->originalCharged, 2)),

                        Placeholder::make('new_total')
                            ->label('Nuevo total redespacho')
                            ->content(function (callable $get): string {
                                $items = $get('items') ?? [];
                                return number_format($this->calculateNewTotal(is_array($items) ? $items : []), 2);
                            }),

                        Placeholder::make('balance')
                            ->label('Balance final (original - nuevo)')
                            ->content(function (callable $get): string {
                                $items = $get('items') ?? [];
                                $newTotal = $this->calculateNewTotal(is_array($items) ? $items : []);
                                return number_format($this->originalCharged - $newTotal, 2);
                            }),
                    ]),

                Section::make('Checklist final')
                    ->columns(1)
                    ->schema([
                        Checkbox::make('ack_cancel_related')
                            ->label('Al redespachar una orden, el sistema CANCELARÁ la orden relacionada si es que no se ha entregado al transporte')
                            ->accepted()
                            ->required()
                            ->columnSpanFull(),
                        Checkbox::make('ack_irreversible')
                            ->label('Esta acción NO es reversible, una vez que presione GUARDAR la cancelación de la orden no puede revertirse')
                            ->accepted()
                            ->required()
                            ->columnSpanFull(),
                        Checkbox::make('ack_inventory')
                            ->label('Al CANCELAR una orden que no ha sido PREPARADA se devolverá al inventario disponible el stock de los productos de la orden')
                            ->accepted()
                            ->required()
                            ->columnSpanFull(),
                    ]),
            ]);
    }

    private function calculateNewTotal(array $items): float
    {
        $sum = 0.0;

        foreach ($items as $item) {
            $qty = (float) Arr::get($item, 'quantity', 0);
            $unit = (float) Arr::get($item, 'unit_price_ti', 0);
            $sum += max(0, $qty) * max(0, $unit);
        }

        return $sum;
    }

    private function generateOrderReference(): string
    {
        for ($i = 0; $i < 10; $i++) {
            $ref = Str::upper(Str::random(9));
            if (! OrderFlow::query()->where('order_reference', $ref)->exists()) {
                return $ref;
            }
        }

        return Str::upper(Str::random(9));
    }

    public function create(): void
    {
        if (! static::canAccess()) {
            Notification::make()->title('No autorizado')->danger()->send();
            return;
        }

        $this->parentFlow->refresh();
        $this->parentFlow->loadMissing('statusCatalog');
        if ($this->parentFlow->isTerminalState()) {
            Notification::make()
                ->title('Orden en estado final')
                ->body('Esta orden se encuentra en un estado final y no puede ser modificada.')
                ->danger()
                ->send();
            return;
        }

        $parentStatusCode = (string) ($this->parentFlow->statusCatalog?->code ?? '');
        $shouldCancelParent = $parentStatusCode !== OrderStatusEnum::EntregadoAlTransporte->value;

        $data = $this->form->getState();

        $items = $data['items'] ?? [];
        if (! is_array($items) || $items === []) {
            Notification::make()->title('Agrega al menos un producto')->warning()->send();
            return;
        }

        $repo = app(OrderRepository::class);

        $cancelStatusId = (int) (OrderStatusCatalog::query()
            ->where('code', OrderStatusEnum::Cancelado->value)
            ->value('id_order_status') ?? 0);

        if ($cancelStatusId <= 0) {
            Notification::make()->title('No existe el estado "Cancelado" en el catálogo')->danger()->send();
            return;
        }

        $reshipmentTypeId = (int) (OrderTypeCatalog::query()
            ->where('code', OrderTypeEnum::Reshipment->value)
            ->value('id_order_type') ?? 0);

        if ($reshipmentTypeId <= 0) {
            Notification::make()->title('No existe el tipo "Redespacho" en el catálogo')->danger()->send();
            return;
        }

        $defaultStatusId = (int) ($repo->defaultStatusId() ?? 0);
        if ($defaultStatusId <= 0) {
            $defaultStatusId = (int) (OrderStatusCatalog::query()
                ->where('code', OrderStatusEnum::Ingresado->value)
                ->value('id_order_status') ?? 0);
        }

        if ($defaultStatusId <= 0) {
            Notification::make()->title('No existe el estado default "Ingresado" en el catálogo')->danger()->send();
            return;
        }

        $newTotal = $this->calculateNewTotal($items);

        DB::connection('mysql')->transaction(function () use ($data, $items, $repo, $cancelStatusId, $reshipmentTypeId, $defaultStatusId, $newTotal, $shouldCancelParent): void {
            $repo->ensureFlowForRemote($this->remoteOrder);

            if ($shouldCancelParent) {
                $repo->updateOrderStatus(
                    $this->remoteOrder,
                    $cancelStatusId,
                    'Cancelada por Redespacho',
                    auth()->id(),
                );
            }

            $orderReference = $this->generateOrderReference();

            $newOrder = new OrderFlow();
            $newOrder->forceFill([
                'remote_order_id' => null,
                'parent_remote_order_id' => (int) ($this->remoteOrder->id_order ?? 0) ?: null,
                'order_reference' => $orderReference,
                'original_order_reference' => (string) ($this->remoteOrder->order_reference ?? ''),
                'store_id' => filled($data['store_id'] ?? null) ? (int) $data['store_id'] : null,
                'store_name' => null,
                'channel_id' => $this->parentFlow->channel_id,
                'order_type_id' => $reshipmentTypeId,
                'status_id' => $defaultStatusId,
                'carrier_id' => null,
                'guide' => null,
                'codClient' => (string) ((int) ($this->remoteOrder->idClient ?? 0) ?: ($this->parentFlow->codClient ?? '')),
                'customer_name' => (string) ($data['customer_name'] ?? ''),
                'customer_identification' => filled($data['customer_identification'] ?? null) ? (string) $data['customer_identification'] : null,
                'customer_email' => filled($data['customer_email'] ?? null) ? (string) $data['customer_email'] : null,
                'customer_phone' => filled($data['customer_phone'] ?? null) ? (string) $data['customer_phone'] : null,
                'customer_legal_activity' => filled($data['customer_legal_activity'] ?? null) ? (string) $data['customer_legal_activity'] : null,
                'customer_total_orders' => 1,
                'payment' => null,
                'total_productsTI' => $newTotal,
                'total_productsTE' => $newTotal,
                'total_discounts' => 0,
                'total_shipping' => 0,
                'total_paidTI' => $newTotal,
                'total_paidTE' => $newTotal,
                'date_order' => now(),
                'postal_code' => filled($data['postal_code'] ?? null) ? (string) $data['postal_code'] : null,
                'province_id' => filled($data['province_id'] ?? null) ? (int) $data['province_id'] : null,
                'canton_id' => filled($data['canton_id'] ?? null) ? (int) $data['canton_id'] : null,
                'district_id' => filled($data['district_id'] ?? null) ? (int) $data['district_id'] : null,
                'address_detail' => filled($data['address_detail'] ?? null) ? (string) $data['address_detail'] : null,
            ]);

            if ($newOrder->store_id) {
                $name = (string) (Store::query()->whereKey((int) $newOrder->store_id)->value('name') ?? '');
                $newOrder->store_name = $name !== '' ? $name : null;
            }

            $newOrder->save();

            foreach ($items as $item) {
                $qty = (int) Arr::get($item, 'quantity', 0);
                $qty = max(1, $qty);

                $priceTI = (float) Arr::get($item, 'unit_price_ti', 0);
                $priceTE = (float) Arr::get($item, 'unit_price_te', $priceTI);

                $detail = new OrderDetail();
                $detail->forceFill([
                    'order_reference' => $orderReference,
                    'product_remote_id' => filled(Arr::get($item, 'product_remote_id')) ? (int) Arr::get($item, 'product_remote_id') : null,
                    'product_reference' => (string) Arr::get($item, 'sku', ''),
                    'product_name' => (string) Arr::get($item, 'name', ''),
                    'product_quantity' => $qty,
                    'product_tax' => (float) Arr::get($item, 'tax', 0),
                    'product_priceTI' => $priceTI,
                    'product_priceTE' => $priceTE,
                    'total_priceTI' => $qty * $priceTI,
                    'total_priceTE' => $qty * $priceTE,
                    'warehouse_assigned' => null,
                ])->save();
            }
        });

        Notification::make()
            ->title('Redespacho creado')
            ->body($shouldCancelParent ? 'La orden relacionada fue cancelada.' : 'La orden relacionada no fue cancelada porque ya está "Entregado al transporte".')
            ->success()
            ->send();

        $this->redirect(OrderResource::getUrl('index'));
    }
}
