<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreCustomerOrderRequest;
use App\Http\Requests\UpdateCustomerOrderItemRequest;
use App\Http\Requests\UpdateCustomerOrderRequest;
use App\Models\Customer;
use App\Models\CustomerOrder;
use App\Models\CustomerOrderItem;
use App\Models\FactoryProduct;
use App\Support\CashSaleCustomer;
use App\Support\CustomerOrderFulfillment;
use App\Support\CustomerOrderItemStatus;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;

class CustomerOrderController extends Controller
{
    private const PER_PAGE_OPTIONS = [5, 10, 25, 50];

    public function index(Request $request): Response
    {
        $perPage = $request->integer('per_page', 10);
        if (! in_array($perPage, self::PER_PAGE_OPTIONS, true)) {
            $perPage = 10;
        }

        $orders = CustomerOrder::query()
            ->with(['customer:id,full_name', 'items'])
            ->orderByDesc('ordered_at')
            ->orderByDesc('id')
            ->paginate($perPage)
            ->withQueryString()
            ->through(fn (CustomerOrder $order): array => [
                'id' => $order->id,
                'order_number' => $order->order_number,
                'customer_id' => $order->customer_id,
                'customer_name' => $order->customer?->full_name,
                'ordered_at' => $order->ordered_at?->toDateString(),
                'status' => $order->status,
                'items_count' => $order->items->count(),
                'can_edit' => CustomerOrderFulfillment::canEdit($order),
                'can_cancel' => CustomerOrderFulfillment::canCancel($order),
            ]);

        return Inertia::render('customer-orders/index', [
            'orders' => $orders,
            'filters' => ['per_page' => $perPage],
        ]);
    }

    public function create(): Response
    {
        return Inertia::render('customer-orders/create', [
            'customers' => $this->customerOptionsForForm(),
            'products' => FactoryProduct::optionsForForm(),
        ]);
    }

    public function store(StoreCustomerOrderRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        CustomerOrderFulfillment::createOrder(
            (int) $validated['customer_id'],
            $validated['ordered_at'],
            $validated['notes'] ?? null,
            $validated['items'],
        );

        return redirect()
            ->route('customer-orders.index')
            ->with('status', __('Customer order recorded.'));
    }

    public function show(CustomerOrder $customerOrder): Response
    {
        $customerOrder->load([
            'customer:id,full_name,contact_number,address',
            'items.product.unit:id,name',
            'items.productionOrders',
        ]);

        return Inertia::render('customer-orders/show', [
            'order' => [
                'id' => $customerOrder->id,
                'order_number' => $customerOrder->order_number,
                'customer_id' => $customerOrder->customer_id,
                'customer_name' => $customerOrder->customer?->full_name,
                'customer_contact' => $customerOrder->customer?->contact_number,
                'customer_address' => $customerOrder->customer?->address,
                'ordered_at' => $customerOrder->ordered_at?->toDateString(),
                'status' => $customerOrder->status,
                'notes' => $customerOrder->notes,
                'can_edit' => CustomerOrderFulfillment::canEdit($customerOrder),
                'can_cancel' => CustomerOrderFulfillment::canCancel($customerOrder),
                'items' => $customerOrder->items->map(fn (CustomerOrderItem $item): array => [
                    'id' => $item->id,
                    'factory_product_id' => $item->factory_product_id,
                    'product_name' => $item->product?->name,
                    'unit_name' => $item->product?->unit?->name,
                    'quantity' => (string) $item->quantity,
                    'fulfilled_quantity' => (string) $item->fulfilled_quantity,
                    'sold_quantity' => (string) $item->sold_quantity,
                    'status' => $item->status,
                    'can_edit' => CustomerOrderFulfillment::canEditItem($item),
                    'can_cancel' => CustomerOrderFulfillment::canCancelItem($item),
                    'pending_production_count' => $item->productionOrders
                        ->where('status', 'pending')
                        ->count(),
                ])->values()->all(),
            ],
        ]);
    }

    public function edit(CustomerOrder $customerOrder): Response|RedirectResponse
    {
        $this->authorizeManage();

        if (! CustomerOrderFulfillment::canEdit($customerOrder)) {
            return redirect()
                ->route('customer-orders.show', $customerOrder)
                ->with('status', __('This order cannot be edited.'));
        }

        $customerOrder->load('items');

        $productIds = $customerOrder->items->pluck('factory_product_id')->all();

        return Inertia::render('customer-orders/edit', [
            'order' => [
                'id' => $customerOrder->id,
                'customer_id' => $customerOrder->customer_id,
                'ordered_at' => $customerOrder->ordered_at?->toDateString(),
                'notes' => $customerOrder->notes ?? '',
                'items' => $customerOrder->items->map(fn (CustomerOrderItem $item): array => [
                    'factory_product_id' => $item->factory_product_id,
                    'quantity' => (string) $item->quantity,
                ])->values()->all(),
            ],
            'customers' => $this->customerOptionsForForm($customerOrder->customer_id),
            'products' => FactoryProduct::optionsForForm($productIds),
        ]);
    }

    public function update(
        UpdateCustomerOrderRequest $request,
        CustomerOrder $customerOrder,
    ): RedirectResponse {
        $this->authorizeManage();

        $validated = $request->validated();

        CustomerOrderFulfillment::updateOrder(
            $customerOrder,
            (int) $validated['customer_id'],
            $validated['ordered_at'],
            $validated['notes'] ?? null,
            $validated['items'],
        );

        return redirect()
            ->route('customer-orders.show', $customerOrder)
            ->with('status', __('Customer order updated.'));
    }

    public function cancel(CustomerOrder $customerOrder): RedirectResponse
    {
        $this->authorizeManage();

        if (! CustomerOrderFulfillment::canCancel($customerOrder)) {
            return redirect()
                ->back()
                ->withErrors(['order' => __('This order cannot be cancelled.')]);
        }

        CustomerOrderFulfillment::cancelOrder($customerOrder);

        return redirect()
            ->route('customer-orders.index')
            ->with('status', __('Customer order cancelled.'));
    }

    public function deliverItem(CustomerOrderItem $customerOrderItem): RedirectResponse
    {
        $this->authorizeManage();

        if ($customerOrderItem->status !== CustomerOrderItemStatus::READY) {
            return redirect()
                ->back()
                ->withErrors(['item' => __('Only ready items can be marked as delivered.')]);
        }

        CustomerOrderFulfillment::markItemDelivered($customerOrderItem);

        return redirect()
            ->route('customer-orders.show', $customerOrderItem->customer_order_id)
            ->with('status', __('Order item marked as delivered.'));
    }

    public function editItem(CustomerOrderItem $customerOrderItem): Response|RedirectResponse
    {
        $this->authorizeManage();

        $customerOrderItem->load(['order', 'product.unit']);

        if (! CustomerOrderFulfillment::canEditItem($customerOrderItem)) {
            return redirect()
                ->route('customer-orders.show', $customerOrderItem->customer_order_id)
                ->with('status', __('This line cannot be edited.'));
        }

        return Inertia::render('customer-orders/items/edit', [
            'order' => [
                'id' => $customerOrderItem->order?->id,
            ],
            'item' => [
                'id' => $customerOrderItem->id,
                'factory_product_id' => $customerOrderItem->factory_product_id,
                'product_name' => $customerOrderItem->product?->name,
                'quantity' => (string) $customerOrderItem->quantity,
            ],
            'products' => FactoryProduct::optionsForForm([$customerOrderItem->factory_product_id]),
        ]);
    }

    public function updateItem(
        UpdateCustomerOrderItemRequest $request,
        CustomerOrderItem $customerOrderItem,
    ): RedirectResponse {
        $this->authorizeManage();

        $validated = $request->validated();

        CustomerOrderFulfillment::updateItem(
            $customerOrderItem,
            (int) $validated['factory_product_id'],
            (float) $validated['quantity'],
        );

        return redirect()
            ->route('customer-orders.show', $customerOrderItem->customer_order_id)
            ->with('status', __('Order line updated.'));
    }

    public function cancelItem(CustomerOrderItem $customerOrderItem): RedirectResponse
    {
        $this->authorizeManage();

        if (! CustomerOrderFulfillment::canCancelItem($customerOrderItem)) {
            return redirect()
                ->back()
                ->withErrors(['item' => __('This line cannot be cancelled.')]);
        }

        $orderId = $customerOrderItem->customer_order_id;

        CustomerOrderFulfillment::cancelItem($customerOrderItem);

        return redirect()
            ->route('customer-orders.show', $orderId)
            ->with('status', __('Order line cancelled.'));
    }

    private function authorizeManage(): void
    {
        $user = auth()->user();

        abort_unless(
            $user !== null
                && ($user->can('customer-orders.update') || $user->can('customer-orders.create')),
            403,
        );
    }

    /**
     * @return list<array{id: int, full_name: string}>
     */
    private function customerOptionsForForm(?int $includeCustomerId = null): array
    {
        return Customer::query()
            ->when(
                $includeCustomerId !== null,
                fn ($query) => $query->where(function ($query) use ($includeCustomerId): void {
                    $query->where('is_active', true)
                        ->orWhere('id', $includeCustomerId);
                }),
                fn ($query) => $query->where('is_active', true),
            )
            ->tap(fn ($query) => CashSaleCustomer::applyRegisteredScope($query))
            ->orderBy('full_name')
            ->get(['id', 'full_name'])
            ->map(fn (Customer $customer): array => [
                'id' => $customer->id,
                'full_name' => $customer->full_name,
            ])
            ->values()
            ->all();
    }
}
