<?php

namespace App\Filament\Resources\SalesOrderResource\Pages;

use App\Filament\Resources\Concerns\InteractsWithSalesOrderCatalog;
use App\Filament\Resources\SalesOrderResource;
use App\Models\Sales\SalesOrder;
use App\Models\Sales\SalesOrderItem;
use App\Models\Sales\SalesOrderPayment;
use App\Services\CurrentContext;
use App\Services\SalesOrderService;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\ValidationException;
use RuntimeException;

/**
 * Orders are editable right up until they are invoiced — a customer changing
 * their mind about a bulk order two weeks before the deadline is the norm, not
 * an exception. Once converted, the record is frozen (see
 * SalesOrderResource::canEdit()) because the invoice now owns the money.
 */
class EditSalesOrder extends EditRecord
{
    use InteractsWithSalesOrderCatalog;

    protected static string $resource = SalesOrderResource::class;

    protected function getHeaderActions(): array
    {
        return [
            SalesOrderResource::addPaymentAction(),
            SalesOrderResource::convertAction(),
            SalesOrderResource::cancelAction(),
            DeleteAction::make(),
        ];
    }

    /**
     * Rehydrate the form from the order's own rows — the product grid and the
     * payments repeater both expect plain arrays, not relations.
     *
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    protected function mutateFormDataBeforeFill(array $data): array
    {
        /** @var SalesOrder $order */
        $order = $this->getRecord();

        $data['status_code'] = $order->status?->code;

        $data['items'] = $order->items->map(fn (SalesOrderItem $item): array => [
            'product_id' => $item->product_id,
            'is_custom' => (bool) $item->is_custom,
            'product_name' => $item->product_name,
            'custom_description' => $item->custom_description,
            'custom_specification' => $item->custom_specification,
            'unit_id' => $item->unit_id,
            'quantity' => (float) $item->quantity,
            'unit_price' => (float) $item->unit_price,
            'vat_rate' => (float) $item->vat_rate,
            'discount_type' => $item->discount_type,
            'discount_value' => (float) $item->discount_value,
            'batch_id' => $item->stock_batch_id,
        ])->all();

        $data['payments'] = $order->payments->map(fn (SalesOrderPayment $payment): array => [
            'method' => $payment->method,
            'amount' => (float) $payment->amount,
            'reference' => $payment->reference,
            'cheque_number' => $payment->cheque_number,
            'cheque_bank' => $payment->cheque_bank,
            'cheque_branch' => $payment->cheque_branch,
            'cheque_date' => $payment->cheque_date?->toDateString(),
            'credit_note_id' => $payment->credit_note_id,
            'custom_fields' => $payment->custom_fields,
            'received_at' => $payment->received_at?->toDateString(),
        ])->all();

        return $data;
    }

    /**
     * @param  array<string, mixed>  $data
     */
    protected function handleRecordUpdate(Model $record, array $data): Model
    {
        abort_unless(
            app(CurrentContext::class)->canAccessBranch((int) $data['branch_id']),
            403,
            'You do not have access to this branch.',
        );

        try {
            $order = app(SalesOrderService::class)->update(
                $record,
                header: [
                    'branch_id' => (int) $data['branch_id'],
                    'customer_id' => (int) $data['customer_id'],
                    'sales_rep_id' => $data['sales_rep_id'] ?? null,
                    'order_date' => $data['order_date'] ?? null,
                    'deadline_date' => $data['deadline_date'] ?? null,
                    'discount_type' => $data['discount_type'] ?? null,
                    'discount_value' => (float) ($data['discount_value'] ?? 0),
                    'note' => $data['note'] ?? null,
                ],
                lines: $data['items'] ?? [],
                payments: $data['payments'] ?? [],
            );
        } catch (RuntimeException $e) {
            throw ValidationException::withMessages(['data.items' => $e->getMessage()]);
        }

        // The status dropdown is a plain field on this form, but the transition
        // rules live in the service — apply it separately rather than letting a
        // mass update slip past them.
        if (($data['status_code'] ?? null) && $data['status_code'] !== $order->status?->code) {
            try {
                app(SalesOrderService::class)->updateStatus($order, $data['status_code']);
            } catch (RuntimeException $e) {
                throw ValidationException::withMessages(['data.status_code' => $e->getMessage()]);
            }
        }

        return $order->refresh();
    }

    protected function getRedirectUrl(): string
    {
        return SalesOrderResource::getUrl('index');
    }
}
