<?php

namespace App\Services\Manage;

use App\Enums\OrderChannel;
use App\Enums\OrderStatus;
use App\Enums\PaymentType;
use App\Enums\PriceType;
use App\Enums\ProductStockQuality;
use App\Models\Customer;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\User;
use App\Services\Finance\CashService;
use App\Services\Media\MediaService;
use App\Services\System\PushNotificationService;
use App\Services\System\Setting\MarketplaceService;
use App\Support\Media\MediaPresenter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;

class OrderService
{
    private const MAX_PHOTOS = 1;

    public function __construct(
        private readonly MarketplaceService $marketplaceService,
        private readonly PushNotificationService $pushNotificationService,
        private readonly CashService $cashService,
        private readonly CuttingResultPriceResolver $cuttingResultPriceResolver,
        private readonly MediaService $mediaService,
    ) {}

    public function defaultPriceType(OrderChannel $channel): ?PriceType
    {
        return match ($channel) {
            OrderChannel::STORE => null,
            OrderChannel::SHOPEE => PriceType::SHOPEE,
            OrderChannel::TIKTOK => PriceType::TIKTOK,
        };
    }

    public function stockColumn(ProductStockQuality $quality): string
    {
        return match ($quality) {
            ProductStockQuality::GOOD => 'stock',
            ProductStockQuality::REJECT => 'reject_stock',
            ProductStockQuality::RETAIL => 'retail_stock',
        };
    }

    public function paginateForIndex(array $tableQuery, User $user): LengthAwarePaginator
    {
        $query = Order::query()
            ->with([
                'customer:id,name',
                'createdBy.profile',
                'items.productVariant.product:id,name',
                'items.productVariant:id,product_id,name',
            ])
            ->when($user->hasAnyRole(['marketing-offline', 'marketing-online']), fn (Builder $query) => $query->where('marketing_id', $user->id))
            ->when($user->hasRole('cashier'), fn (Builder $query) => $query->where('created_by_id', $user->id))
            ->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
                $search = $tableQuery['search'];
                $query->where(function (Builder $query) use ($search): void {
                    $query->where('order_number', 'like', "%{$search}%")
                        ->orWhere('tiktok_order_id', 'like', "%{$search}%")
                        ->orWhere('shopee_order_id', 'like', "%{$search}%")
                        ->orWhere('notes', 'like', "%{$search}%")
                        ->orWhereHas('customer', fn (Builder $query) => $query->where('name', 'like', "%{$search}%"))
                        ->orWhereHas('items.productVariant', function (Builder $query) use ($search): void {
                            $query->where('name', 'like', "%{$search}%")
                                ->orWhereHas('product', fn (Builder $query) => $query->where('name', 'like', "%{$search}%"));
                        });
                });
            });

        $this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);

        return $query
            ->paginate(25)
            ->withQueryString()
            ->through(function (Order $order) use ($user) {
                $actions = collect($order->status->availableActions())
                    ->filter(fn (array $action) => $user->can($action['permission']))
                    ->values()
                    ->all();

                $order->setAttribute('available_actions', $actions);
                $order->setAttribute('is_editable', $order->status->isEditable());
                $order->setAttribute(
                    'marketplace_settings_snapshot',
                    $this->enrichMarketplaceSnapshot($order->marketplace_settings_snapshot),
                );

                return $order;
            });
    }

    public function customerOptions(): array
    {
        return Customer::query()
            ->orderBy('name')
            ->get(['id', 'name'])
            ->map(fn (Customer $customer) => [
                'value' => $customer->id,
                'label' => $customer->name,
            ])
            ->all();
    }

    public function marketingOptions(): array
    {
        return User::query()
            ->active()
            ->whereHas('roles', fn (Builder $roleQuery) => $roleQuery->whereIn('name', ['marketing-offline', 'marketing-online']))
            ->with('profile:user_id,full_name')
            ->orderBy('username')
            ->get(['id', 'username'])
            ->map(fn (User $user) => [
                'value' => $user->id,
                'label' => $user->profile?->full_name ?? $user->username,
            ])
            ->all();
    }

    public function storePriceTypeOptions(): array
    {
        return collect(PriceType::cases())
            ->reject(fn (PriceType $type) => in_array($type, [PriceType::HARGA_MODAL, PriceType::SHOPEE, PriceType::TIKTOK], true))
            ->map(fn (PriceType $type) => [
                'value' => $type->value,
                'label' => $type->label(),
            ])
            ->values()
            ->all();
    }

    public function catalogItems(?Order $order = null, ?User $user = null): Collection
    {
        $orderVariantIds = $order
            ? $order->items()->pluck('product_variant_id')->all()
            : ($user ? $this->draftItemsQuery($user)->pluck('product_variant_id')->all() : []);

        $products = Product::query()
            ->with([
                'variants' => fn ($query) => $query
                    ->with('media')
                    ->orderBy('created_at'),
            ])
            ->where(function (Builder $query) use ($orderVariantIds): void {
                $query->active();

                if ($orderVariantIds !== []) {
                    $query->orWhereHas(
                        'variants',
                        fn (Builder $query) => $query->whereIn('id', $orderVariantIds),
                    );
                }
            })
            ->orderBy('name')
            ->get();

        $allVariantIds = $products
            ->flatMap(fn (Product $product) => $product->variants->pluck('id'))
            ->all();

        $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($allVariantIds);

        return $products->each(function (Product $product) use ($allPricesByVariant): void {
            $product->variants->each(function (ProductVariant $variant) use ($allPricesByVariant): void {
                $variant->setAttribute(
                    'images',
                    MediaPresenter::collection($variant, 'images'),
                );
                $variant->setAttribute(
                    'prices',
                    $this->presentVariantPricesFromCollection(
                        $variant->id,
                        $allPricesByVariant,
                    ),
                );
            });
        });
    }

    public function findForEdit(Order $order): Order
    {
        $order->load([
            'items.productVariant.product:id,name',
            'items.productVariant.media',
            'media',
        ]);

        $order->setAttribute(
            'photos',
            MediaPresenter::first($order, 'photos'),
        );

        $variantIds = $order->items
            ->filter(fn (OrderItem $item) => $item->productVariant !== null)
            ->map(fn (OrderItem $item) => $item->productVariant->id)
            ->all();

        $allPricesByVariant = $this->cuttingResultPriceResolver->latestPricesForVariants($variantIds);

        $order->items->each(function (OrderItem $item) use ($allPricesByVariant): void {
            $variant = $item->productVariant;

            if ($variant) {
                $item->setAttribute('product_name', $variant->product?->name);
                $item->setAttribute('variant_name', $variant->name);
                $variant->setAttribute(
                    'images',
                    MediaPresenter::collection($variant, 'images'),
                );
                $variant->setAttribute(
                    'prices',
                    $this->presentVariantPricesFromCollection(
                        $variant->id,
                        $allPricesByVariant,
                    ),
                );
            }
        });

        return $order;
    }

    public function findForShow(Order $order): Order
    {
        $order->load([
            'customer:id,name,phone_number,address',
            'createdBy.profile',
            'marketing.profile',
            'items.productVariant.product:id,name',
            'items.productVariant.media',
            'cashTransaction:id,amount,description,created_at',
            'media',
        ]);

        $order->setAttribute(
            'photos',
            MediaPresenter::first($order, 'photos'),
        );

        $order->items->each(function (OrderItem $item): void {
            $variant = $item->productVariant;

            if ($variant) {
                $item->setAttribute('product_name', $variant->product?->name);
                $item->setAttribute('variant_name', $variant->name);
                $variant->setAttribute(
                    'images',
                    MediaPresenter::collection($variant, 'images'),
                );
            }
        });

        $availableActions = collect($order->status->availableActions())
            ->values()
            ->all();

        $order->setAttribute('available_actions', $availableActions);
        $order->setAttribute('is_editable', $order->status->isEditable());
        $order->setAttribute(
            'marketplace_settings_snapshot',
            $this->enrichMarketplaceSnapshot($order->marketplace_settings_snapshot),
        );

        return $order;
    }

    public function draftItemsForUser(User $user): array
    {
        return $this->draftItemsQuery($user)
            ->with([
                'productVariant.product:id,name',
                'productVariant.media',
            ])
            ->get()
            ->map(fn (OrderItem $item) => $this->presentDraftItem($item))
            ->values()
            ->all();
    }

    public function syncDraftItem(array $validated, User $user): array
    {
        // Force retail price type and stock quality for cashier role
        $priceType = $user->hasRole('cashier')
            ? PriceType::RETAIL
            : PriceType::from($validated['price_type']);

        $stockQuality = $user->hasRole('cashier')
            ? ProductStockQuality::RETAIL
            : ProductStockQuality::from($validated['stock_quality']);

        $variant = ProductVariant::query()->findOrFail($validated['product_variant_id']);
        $unitPrice = $this->resolveUnitPrice($variant->id, $priceType);

        $quantity = (int) $validated['quantity'];
        $availableStock = $this->availableStock($variant, $stockQuality);

        if ($quantity > $availableStock) {
            throw ValidationException::withMessages([
                'quantity' => "Stok {$stockQuality->label()} tidak mencukupi. Stok saat ini: {$availableStock} pcs.",
            ]);
        }

        $subtotal = $unitPrice * $quantity;

        $item = OrderItem::query()->updateOrCreate(
            [
                'user_id' => $user->id,
                'product_variant_id' => $variant->id,
                'stock_quality' => $stockQuality->value,
                'order_id' => null,
            ],
            [
                'quantity' => $quantity,
                'unit_price' => $unitPrice,
                'subtotal' => $subtotal,
            ],
        );

        $item->load([
            'productVariant.product:id,name',
            'productVariant.media',
        ]);

        return $this->presentDraftItem($item);
    }

    public function removeDraftItem(User $user, ProductVariant $productVariant, ProductStockQuality $stockQuality): void
    {
        OrderItem::query()
            ->whereNull('order_id')
            ->where('user_id', $user->id)
            ->where('product_variant_id', $productVariant->id)
            ->where('stock_quality', $stockQuality->value)
            ->delete();
    }

    public function resyncDraftPrices(User $user, string $priceTypeValue): array
    {
        // Force retail price type for cashier role
        $priceType = $user->hasRole('cashier')
            ? PriceType::RETAIL
            : PriceType::from($priceTypeValue);

        $items = $this->draftItemsQuery($user)
            ->with([
                'productVariant.product:id,name',
                'productVariant.media',
            ])
            ->get();

        foreach ($items as $item) {
            $price = $this->cuttingResultPriceResolver->resolve($item->product_variant_id, $priceType);

            if ($price === null) {
                $item->delete();

                continue;
            }

            $unitPrice = (int) $price->price;
            $item->unit_price = $unitPrice;
            $item->subtotal = $unitPrice * $item->quantity;
            $item->save();
        }

        return $this->draftItemsForUser($user);
    }

    public function create(array $validated, User $user): Order
    {
        try {
            // Force cashier settings
            if ($user->hasRole('cashier')) {
                $validated['channel'] = 'store';
                $validated['price_type'] = 'retail';
                $validated['payment_type'] = 'cash';
                unset($validated['customer_id']);
            }

            $order = DB::transaction(function () use ($validated, $user): Order {
                $priceType = $this->resolvePriceType($validated['channel'], $validated['price_type']);

                $draftItems = $this->draftItemsQuery($user)
                    ->lockForUpdate()
                    ->get();

                if ($draftItems->isEmpty()) {
                    throw ValidationException::withMessages([
                        'items' => 'Tambahkan minimal satu produk ke keranjang.',
                    ]);
                }

                $this->applyDraftPrices($draftItems, $priceType);

                $subtotal = $draftItems->sum('subtotal');
                $discount = (int) ($validated['discount'] ?? 0);
                $negoPrice = isset($validated['nego_price']) && $validated['nego_price'] !== '' ? (int) $validated['nego_price'] : null;
                $totalAmount = $negoPrice !== null ? max($negoPrice, 0) : max($subtotal - $discount, 0);
                $channel = OrderChannel::from($validated['channel']);

                $status = isset($validated['status'])
                    ? OrderStatus::from($validated['status'])
                    : OrderStatus::PENDING;

                $order = Order::create([
                    'customer_id' => $validated['customer_id'] ?? null,
                    'marketing_id' => $validated['marketing_id'] ?? null,
                    'channel' => $channel,
                    'price_type' => $priceType,
                    'payment_type' => PaymentType::from($validated['payment_type']),
                    'is_affiliate' => $validated['is_affiliate'] ?? false,
                    'status' => $status,
                    'tiktok_order_id' => $validated['tiktok_order_id'] ?? null,
                    'shopee_order_id' => $validated['shopee_order_id'] ?? null,
                    'created_by_id' => $user->id,
                    'subtotal' => $subtotal,
                    'discount' => $discount,
                    'nego_price' => $negoPrice,
                    'marketplace_settings_snapshot' => $this->marketplaceService->buildOrderSnapshot(
                        $channel,
                        $totalAmount,
                        $this->lineItemsForSnapshot($draftItems),
                        $validated['is_affiliate'] ?? false,
                    ),
                    'total_amount' => $totalAmount,
                    'notes' => $validated['notes'] ?? null,
                ]);

                if ($order->payment_type === PaymentType::CASH) {
                    $cashTransaction = $this->cashService->recordIncoming(
                        $order,
                        $totalAmount,
                        "Pembayaran pesanan {$order->order_number}",
                        $user,
                    );

                    $order->cash_transaction_id = $cashTransaction->id;
                    $order->save();
                }

                $this->syncPhotos($order, $validated);

                foreach ($draftItems as $item) {
                    $variant = ProductVariant::query()->with('product')->lockForUpdate()->find($item->product_variant_id);
                    if ($variant === null) {
                        throw ValidationException::withMessages([
                            'items' => 'Varian produk tidak ditemukan.',
                        ]);
                    }

                    $stockQuality = $item->stock_quality ?? ProductStockQuality::GOOD;
                    $availableStock = $this->availableStock($variant, $stockQuality);

                    if ($availableStock < $item->quantity) {
                        throw ValidationException::withMessages([
                            'items' => "Stok {$stockQuality->label()} produk {$variant->product->name} ({$variant->name}) tidak mencukupi. Stok saat ini: {$availableStock} pcs.",
                        ]);
                    }
                    $item->order_id = $order->id;
                    $item->save();
                    $this->decrementStock($item);
                }

                return $order;
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal membuat pesanan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }

        $this->pushNotificationService->sendToRoles(
            '📦 Pesanan Baru',
            "Pesanan baru {$order->order_number} senilai {$order->total_amount_formatted} telah dibuat oleh {$user->profile?->full_name}.",
            ['owner', 'developer'],
            route('admin.manage.orders.index'),
        );

        return $order;
    }

    public function update(Order $order, array $validated): void
    {
        $order->ensureEditable();

        try {
            DB::transaction(function () use ($order, $validated): void {
                $order->load('items');

                foreach ($order->items as $item) {
                    $this->incrementStock($item);
                }

                $order->items()->delete();

                $priceType = $this->resolvePriceType($validated['channel'], $validated['price_type']);
                $lineItems = $this->buildLineItems($validated['items'], $priceType);
                $subtotal = array_sum(array_column($lineItems, 'subtotal'));
                $discount = (int) ($validated['discount'] ?? 0);
                $negoPrice = isset($validated['nego_price']) && $validated['nego_price'] !== '' ? (int) $validated['nego_price'] : null;
                $totalAmount = $negoPrice !== null ? max($negoPrice, 0) : max($subtotal - $discount, 0);
                $channel = OrderChannel::from($validated['channel']);
                $status = $order->status;

                if (isset($validated['status'])) {
                    $newStatus = OrderStatus::from($validated['status']);

                    if ($order->status->canTransitionTo($newStatus) || $newStatus === $order->status) {
                        $status = $newStatus;
                    }
                }

                $order->update([
                    'customer_id' => $validated['customer_id'] ?? null,
                    'marketing_id' => $validated['marketing_id'] ?? null,
                    'channel' => $channel,
                    'price_type' => $priceType,
                    'payment_type' => PaymentType::from($validated['payment_type']),
                    'is_affiliate' => $validated['is_affiliate'] ?? false,
                    'tiktok_order_id' => $validated['tiktok_order_id'] ?? null,
                    'shopee_order_id' => $validated['shopee_order_id'] ?? null,
                    'subtotal' => $subtotal,
                    'discount' => $discount,
                    'nego_price' => $negoPrice,
                    'marketplace_settings_snapshot' => $this->marketplaceService->buildOrderSnapshot(
                        $channel,
                        $totalAmount,
                        $this->lineItemsForSnapshot($lineItems),
                        $validated['is_affiliate'] ?? false,
                    ),
                    'total_amount' => $totalAmount,
                    'notes' => $validated['notes'] ?? null,
                    'status' => $status,
                ]);

                $this->syncPhotos($order, $validated);

                foreach ($lineItems as $itemData) {
                    $variant = ProductVariant::query()->with('product')->lockForUpdate()->find($itemData['product_variant_id']);
                    if ($variant === null) {
                        throw ValidationException::withMessages([
                            'items' => 'Varian produk tidak ditemukan.',
                        ]);
                    }

                    $stockQuality = ProductStockQuality::from($itemData['stock_quality']);
                    $availableStock = $this->availableStock($variant, $stockQuality);

                    if ($availableStock < $itemData['quantity']) {
                        throw ValidationException::withMessages([
                            'items' => "Stok {$stockQuality->label()} produk {$variant->product->name} ({$variant->name}) tidak mencukupi. Stok saat ini: {$availableStock} pcs.",
                        ]);
                    }
                    $orderItem = $order->items()->create($itemData);
                    $this->decrementStock($orderItem);
                }
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal memperbarui pesanan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }

        $this->pushNotificationService->sendToRoles(
            '✏️ Pesanan Diperbarui',
            "Pesanan {$order->order_number} senilai {$order->total_amount_formatted} telah diperbarui.",
            ['owner', 'developer'],
            route('admin.manage.orders.index'),
        );
    }

    public function delete(Order $order): void
    {
        $orderNumber = $order->order_number;
        $totalAmount = $order->total_amount;

        try {
            DB::transaction(function () use ($order): void {
                $order->load('items');

                if ($order->status->isEditable()) {
                    foreach ($order->items as $item) {
                        $this->incrementStock($item);
                    }
                }

                if ($order->cashTransaction) {
                    $this->cashService->deleteReferencedTransaction($order->cashTransaction);
                }

                $order->items()->delete();
                $order->delete();
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal menghapus pesanan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }

        $this->pushNotificationService->sendToRoles(
            '🗑️ Pesanan Dihapus',
            "Pesanan {$orderNumber} senilai {$order->total_amount_formatted} telah dihapus.",
            ['owner', 'developer'],
            route('admin.manage.orders.index'),
        );
    }

    public function transitionStatus(Order $order, OrderStatus $status): void
    {
        if (! $order->status->canTransitionTo($status)) {
            throw ValidationException::withMessages([
                'status' => 'Status pesanan tidak dapat diubah.',
            ]);
        }

        try {
            DB::transaction(function () use ($order, $status): void {
                if ($status === OrderStatus::CANCELLED) {
                    $order->load('items');

                    foreach ($order->items as $item) {
                        $this->incrementStock($item);
                    }

                    if ($order->cashTransaction) {
                        $this->cashService->deleteReferencedTransaction($order->cashTransaction);
                        $order->cash_transaction_id = null;
                    }
                }

                $order->status = $status;
                $order->save();
            });
        } catch (ValidationException $e) {
            throw $e;
        } catch (\Throwable $e) {
            Log::error('Gagal mengubah status pesanan: '.$e->getMessage(), [
                'trace' => $e->getTraceAsString(),
            ]);

            throw ValidationException::withMessages([
                'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
            ]);
        }

        $this->pushNotificationService->sendToRoles(
            '📦 Status Pesanan Diubah',
            "Pesanan {$order->order_number} diubah statusnya menjadi {$status->label()}.",
            ['owner', 'developer'],
            route('admin.manage.orders.index'),
        );
    }

    private function buildLineItems(array $items, PriceType $priceType): array
    {
        return collect($items)
            ->map(function (array $itemData, int $index) use ($priceType) {
                $variant = ProductVariant::query()->find($itemData['product_variant_id']);

                if ($variant === null) {
                    throw ValidationException::withMessages([
                        "items.{$index}.product_variant_id" => 'Varian produk tidak ditemukan.',
                    ]);
                }

                $unitPrice = $this->resolveUnitPrice($variant->id, $priceType, "items.{$index}.product_variant_id");

                $quantity = (int) $itemData['quantity'];
                $stockQuality = ProductStockQuality::from($itemData['stock_quality'] ?? ProductStockQuality::GOOD->value);

                if ($quantity < 1) {
                    throw ValidationException::withMessages([
                        "items.{$index}.quantity" => 'Jumlah minimal 1 pcs.',
                    ]);
                }

                $subtotal = $unitPrice * $quantity;

                return [
                    'product_variant_id' => $variant->id,
                    'stock_quality' => $stockQuality->value,
                    'quantity' => $quantity,
                    'unit_price' => $unitPrice,
                    'subtotal' => $subtotal,
                ];
            })
            ->all();
    }

    private function resolvePriceType(string $channel, string $priceType): PriceType
    {
        $channelEnum = OrderChannel::from($channel);
        $priceTypeEnum = PriceType::from($priceType);

        $defaultPriceType = $this->defaultPriceType($channelEnum);

        if ($defaultPriceType !== null && $priceTypeEnum !== $defaultPriceType) {
            throw ValidationException::withMessages([
                'price_type' => 'Tipe harga tidak sesuai dengan channel pesanan.',
            ]);
        }

        if ($channelEnum === OrderChannel::STORE && in_array($priceTypeEnum, [PriceType::SHOPEE, PriceType::TIKTOK], true)) {
            throw ValidationException::withMessages([
                'price_type' => 'Tipe harga marketplace tidak dapat digunakan untuk channel toko.',
            ]);
        }

        return $priceTypeEnum;
    }

    private function draftItemsQuery(User $user): Builder
    {
        return OrderItem::query()
            ->whereNull('order_id')
            ->where('user_id', $user->id);
    }

    private function presentDraftItem(OrderItem $item): array
    {
        $variant = $item->productVariant;

        return [
            'product_variant_id' => $item->product_variant_id,
            'product_name' => $variant?->product?->name ?? '',
            'variant_name' => $variant?->name ?? '',
            'stock_quality' => ($item->stock_quality ?? ProductStockQuality::GOOD)->value,
            'stock_quality_label' => ($item->stock_quality ?? ProductStockQuality::GOOD)->label(),
            'quantity' => (string) $item->quantity,
            'unit_price' => $item->unit_price,
            'images' => $variant ? MediaPresenter::collection($variant, 'images') : [],
        ];
    }

    private function applyDraftPrices(EloquentCollection $items, PriceType $priceType): void
    {
        foreach ($items as $index => $item) {
            $unitPrice = $this->resolveUnitPrice(
                $item->product_variant_id,
                $priceType,
                "items.{$index}.product_variant_id",
            );

            $item->unit_price = $unitPrice;
            $item->subtotal = $unitPrice * $item->quantity;
            $item->save();
        }
    }

    private function decrementStock(OrderItem $item): void
    {
        $stockQuality = $item->stock_quality ?? ProductStockQuality::GOOD;

        ProductVariant::query()
            ->whereKey($item->product_variant_id)
            ->decrement($this->stockColumn($stockQuality), $item->quantity);
    }

    private function incrementStock(OrderItem $item): void
    {
        $stockQuality = $item->stock_quality ?? ProductStockQuality::GOOD;

        ProductVariant::query()
            ->whereKey($item->product_variant_id)
            ->increment($this->stockColumn($stockQuality), $item->quantity);
    }

    private function availableStock(ProductVariant $variant, ProductStockQuality $stockQuality): int
    {
        return (int) $variant->{$this->stockColumn($stockQuality)};
    }

    private function syncPhotos(Order $order, array $validated): void
    {
        $paymentType = $validated['payment_type'] ?? null;
        $requiresPhoto = in_array($paymentType, ['qris', 'transfer'], true);

        $this->mediaService->syncCollection(
            $order,
            'photos',
            $validated['photos'] ?? null,
            $validated['remove_media_ids'] ?? null,
            self::MAX_PHOTOS,
            required: $requiresPhoto,
            errorKey: 'photos',
        );
    }

    private function lineItemsForSnapshot(EloquentCollection|array $items): array
    {
        return collect($items)
            ->map(fn (OrderItem|array $item) => [
                'quantity' => (int) (is_array($item) ? $item['quantity'] : $item->quantity),
                'subtotal' => (int) (is_array($item) ? $item['subtotal'] : $item->subtotal),
            ])
            ->values()
            ->all();
    }

    private function applySorting(Builder $query, string $sort, string $direction): void
    {
        if (in_array($sort, ['created_at', 'total_amount', 'discount', 'subtotal', 'order_number'], true)) {
            $query->orderBy($sort, $direction);

            return;
        }

        $query->latest();
    }

    private function resolveUnitPrice(int $variantId, PriceType $priceType, ?string $errorKey = null): int
    {
        $price = $this->cuttingResultPriceResolver->resolve($variantId, $priceType);

        if ($price === null) {
            throw ValidationException::withMessages([
                $errorKey ?? 'product_variant_id' => 'Harga untuk tipe harga ini belum diatur. Verifikasi cutting terlebih dahulu.',
            ]);
        }

        return (int) $price->price;
    }

    private function presentVariantPrices(int $variantId): array
    {
        return collect($this->cuttingResultPriceResolver->latestPricesForVariant($variantId))
            ->map(fn ($price) => [
                'type' => $price->price_type->value,
                'type_label' => $price->price_type->label(),
                'price' => (int) $price->price,
                'price_formatted' => $price->price_formatted,
                'price_input' => (string) $price->price,
                'cost_per_unit' => (int) $price->cost_per_unit,
                'cost_per_unit_formatted' => $price->cost_per_unit_formatted,
            ])
            ->values()
            ->all();
    }

    private function presentVariantPricesFromCollection(int $variantId, Collection $allPricesByVariant): array
    {
        return $allPricesByVariant
            ->get($variantId, collect())
            ->map(fn ($price) => [
                'type' => $price->price_type->value,
                'type_label' => $price->price_type->label(),
                'price' => (int) $price->price,
                'price_formatted' => $price->price_formatted,
                'price_input' => (string) $price->price,
                'cost_per_unit' => (int) $price->cost_per_unit,
                'cost_per_unit_formatted' => $price->cost_per_unit_formatted,
            ])
            ->values()
            ->all();
    }

    private function enrichMarketplaceSnapshot(?array $snapshot): ?array
    {
        if ($snapshot === null) {
            return null;
        }

        $snapshot['base_amount_formatted'] ??= 'Rp '.number_format($snapshot['base_amount'] ?? 0, 0, ',', '.');
        $snapshot['total_fee_amount_formatted'] ??= 'Rp '.number_format($snapshot['total_fee_amount'] ?? 0, 0, ',', '.');
        $snapshot['net_amount_formatted'] ??= 'Rp '.number_format($snapshot['net_amount'] ?? 0, 0, ',', '.');

        return $snapshot;
    }
}
