<?php

namespace App\Modules\Sales\Controllers;

use App\Http\Controllers\Controller;
use App\Modules\Inventory\Stock\Exceptions\InsufficientStockException;
use App\Modules\Sales\Exceptions\SalesOperationalException;
use App\Modules\Sales\Models\SalesOrder;
use App\Modules\Sales\Requests\AllocateSalesOrderRequest;
use App\Modules\Sales\Requests\CancelSalesOrderRequest;
use App\Modules\Sales\Requests\FulfillSalesOrderRequest;
use App\Modules\Sales\Services\SalesOperationalWebExceptionMapper;
use App\Modules\Sales\Services\SalesOrderLifecycleService;
use App\Modules\Sales\Services\SalesOrderUiBatchOperationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Str;

class SalesOrderOperationalController extends Controller
{
    public function __construct(
        private readonly SalesOrderUiBatchOperationService $batch,
        private readonly SalesOrderLifecycleService $lifecycle,
    ) {}

    public function allocate(AllocateSalesOrderRequest $request, SalesOrder $salesOrder): RedirectResponse
    {
        $this->authorize('allocate', $salesOrder);
        $tenantId = (int) $request->user()->tenant_id;
        $rows = $this->normalizeLineRows($request->input('lines', []));

        try {
            $this->batch->allocateLines(
                $tenantId,
                (int) $salesOrder->id,
                $rows,
                (string) $request->input('operation_batch'),
                (int) $request->user()->id,
            );
        } catch (SalesOperationalException|InsufficientStockException $e) {
            return $this->operationalFailure($salesOrder, $e);
        }

        return redirect()
            ->route('sales.orders.show', $salesOrder)
            ->with('status', __('sales.flash.allocated'));
    }

    public function fulfill(FulfillSalesOrderRequest $request, SalesOrder $salesOrder): RedirectResponse
    {
        $this->authorize('fulfill', $salesOrder);
        $tenantId = (int) $request->user()->tenant_id;
        $rows = $this->normalizeLineRows($request->input('lines', []));

        try {
            $this->batch->fulfillLines(
                $tenantId,
                (int) $salesOrder->id,
                $rows,
                (string) $request->input('operation_batch'),
                (int) $request->user()->id,
            );
        } catch (SalesOperationalException|InsufficientStockException $e) {
            return $this->operationalFailure($salesOrder, $e);
        }

        return redirect()
            ->route('sales.orders.show', $salesOrder)
            ->with('status', __('sales.flash.fulfilled'));
    }

    public function cancel(CancelSalesOrderRequest $request, SalesOrder $salesOrder): RedirectResponse
    {
        $this->authorize('cancel', $salesOrder);
        $tenantId = (int) $request->user()->tenant_id;

        try {
            $this->lifecycle->cancelOrder($tenantId, (int) $salesOrder->id, (string) Str::uuid());
        } catch (SalesOperationalException $e) {
            return $this->operationalFailure($salesOrder, $e);
        }

        return redirect()
            ->route('sales.orders.show', $salesOrder)
            ->with('status', __('sales.flash.cancelled'));
    }

    /**
     * @param  list<array<string, mixed>>  $lines
     * @return list<array{line_id:int, quantity:string}>
     */
    private function normalizeLineRows(array $lines): array
    {
        $out = [];
        foreach ($lines as $row) {
            $out[] = [
                'line_id' => (int) ($row['line_id'] ?? 0),
                'quantity' => (string) ($row['quantity'] ?? ''),
            ];
        }

        return $out;
    }

    private function operationalFailure(SalesOrder $salesOrder, \Throwable $e): RedirectResponse
    {
        return redirect()
            ->route('sales.orders.show', $salesOrder)
            ->withErrors(['operational' => SalesOperationalWebExceptionMapper::toOperationalMessage($e)]);
    }
}
