<?php

namespace App\Services;

use App\Models\CustomerRewardRedemption;
use App\Models\Order;
use App\Models\Product;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class OrderService
{
    public function __construct(
        private readonly StockService $stockService,
        private readonly BonusService $bonusService,
        private readonly PaymentService $paymentService,
        private readonly ProgressService $progressService,
    ) {
    }

    public function checkout(array $payload, User $cashier): Order
    {
        return DB::transaction(function () use ($payload, $cashier): Order {
            $branchId = (int) ($payload['branch_id'] ?? $cashier->branch_id);
            $items = collect($payload['items'] ?? []);

            if ($items->isEmpty()) {
                throw ValidationException::withMessages(['items' => 'Keranjang masih kosong.']);
            }

            $order = Order::query()->create([
                'branch_id' => $branchId,
                'customer_id' => $payload['customer_id'] ?? null,
                'user_id' => $cashier->id,
                'invoice_no' => $this->nextInvoiceNo($branchId),
                'status' => 'baru',
                'payment_status' => 'pending',
                'received_at' => now(),
                'estimated_done_at' => $payload['estimated_done_at'] ?? null,
                'delivery_fee' => (float) ($payload['delivery_fee'] ?? 0),
                'note' => $payload['note'] ?? null,
            ]);

            $subtotal = 0;
            $discountTotal = (float) ($payload['discount_total'] ?? 0);
            $rewardUsed = false;

            foreach ($items as $item) {
                $product = Product::query()->lockForUpdate()->findOrFail($item['product_id']);
                $quantity = (float) $item['quantity'];
                $discount = (float) ($item['discount'] ?? 0);
                $isReward = (bool) ($item['is_reward_free_item'] ?? false);

                if ($isReward) {
                    $this->guardRewardAvailable($payload['customer_id'] ?? null);
                    $rewardUsed = true;
                }

                $unitPrice = $isReward ? 0 : (float) $product->price;
                $lineTotal = max(0, ($unitPrice * $quantity) - $discount);

                $this->stockService->assertAvailable($branchId, $product, $quantity);

                $detail = $order->details()->create([
                    'product_id' => $product->id,
                    'staff_id' => $item['staff_id'] ?? $cashier->id,
                    'product_name' => $product->name,
                    'product_type' => $product->type,
                    'quantity' => $quantity,
                    'unit_price' => $unitPrice,
                    'discount' => $discount,
                    'line_total' => $lineTotal,
                    'is_reward_free_item' => $isReward,
                ]);

                $detail->update(['bonus_amount' => $this->bonusService->calculateDetailBonus($detail->load('product'))]);
                $this->stockService->decrease($branchId, $product, $quantity, 'order_checkout', $order->id, $cashier);
                $subtotal += $lineTotal;

                if ($isReward) {
                    CustomerRewardRedemption::query()->create([
                        'customer_id' => $payload['customer_id'],
                        'order_id' => $order->id,
                        'order_detail_id' => $detail->id,
                        'paid_invoice_count' => 5,
                    ]);
                }
            }

            $grandTotal = max(0, $subtotal - $discountTotal + (float) $order->delivery_fee);
            $order->update([
                'subtotal' => $subtotal,
                'discount_total' => $discountTotal,
                'grand_total' => $grandTotal,
                'balance_due' => $grandTotal,
            ]);

            if (($payload['paid_amount'] ?? 0) > 0) {
                $order = $this->paymentService->recordPayment($order, (float) $payload['paid_amount'], $payload['payment_method'] ?? 'cash', $cashier);
            }

            $this->progressService->updateProgress($order, 'order_diterima', $cashier);

            return $order->load(['details', 'customer', 'payments']);
        });
    }

    public function paidInvoiceCountForReward(int $customerId): int
    {
        $redeemed = CustomerRewardRedemption::query()->where('customer_id', $customerId)->count() * 5;

        return max(0, Order::query()
            ->where('customer_id', $customerId)
            ->where('payment_status', 'lunas')
            ->count() - $redeemed);
    }

    private function guardRewardAvailable(?int $customerId): void
    {
        if (! $customerId || $this->paidInvoiceCountForReward($customerId) < 5) {
            throw ValidationException::withMessages(['reward' => 'Reward gratis 1 item jasa belum tersedia.']);
        }
    }

    private function nextInvoiceNo(int $branchId): string
    {
        return 'INV-'.now()->format('Ymd').'-'.$branchId.'-'.Str::upper(Str::random(6));
    }
}
