<?php

namespace App\Services;

use App\Models\BinaryLedger;
use App\Models\FranchiseTransaction;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\PaymentTransaction;
use App\Models\Product;
use App\Models\User;
use App\Models\Franchise;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use RuntimeException;

class FranchiseSalesService
{
    public function __construct(
        private readonly FranchiseStockService $stockService,
        private readonly FranchiseCommissionService $commissionService,
        private readonly BinaryLedgerService $binaryLedgerService,
        private readonly WalletService $walletService,
        private readonly UPIVerificationService $upiVerificationService
    ) {
    }

    /**
     * @param  array<int, array<string, mixed>>  $items
     * @param  array<string, mixed>  $payload
     */
    public function createSale(Franchise $franchise, User $customer, array $items, string $paymentMode, array $payload = []): Order
    {
        if ($items === []) {
            throw new RuntimeException('Please add at least one product.');
        }

        $productIds = collect($items)->pluck('product_id')->unique()->all();
        $products = Product::query()->whereIn('id', $productIds)->get()->keyBy('id');

        $lines = [];
        $subtotal = 0;
        $totalBv = 0;
        foreach ($items as $item) {
            $product = $products[$item['product_id']] ?? null;
            if (! $product) {
                throw new RuntimeException('Product not found.');
            }
            $qty = (float) $item['quantity'];
            if ($qty <= 0) {
                continue;
            }
            $lineTotal = round(((float) $product->dp_price) * $qty, 2);
            $lineBv = round(((float) $product->bv) * $qty, 4);
            $lines[] = [
                'product' => $product,
                'quantity' => $qty,
                'line_total' => $lineTotal,
                'line_bv' => $lineBv,
            ];
            $subtotal += $lineTotal;
            $totalBv += $lineBv;
        }

        if ($lines === []) {
            throw new RuntimeException('Please add valid quantities.');
        }

        $subtotal = round($subtotal, 2);
        $total = $subtotal;

        return DB::transaction(function () use ($franchise, $customer, $lines, $subtotal, $total, $totalBv, $paymentMode, $payload): Order {
            $order = Order::query()->create([
                'order_number' => 'FS'.strtoupper(bin2hex(random_bytes(4))),
                'user_id' => $customer->id,
                'franchise_id' => $franchise->id,
                'order_type' => 'franchise',
                'status' => in_array($paymentMode, ['cash', 'wallet'], true) ? 'paid' : 'pending_payment',
                'payment_status' => in_array($paymentMode, ['cash', 'wallet'], true) ? 'paid' : 'unpaid',
                'subtotal' => $subtotal,
                'discount' => 0,
                'tax' => 0,
                'shipping' => 0,
                'total' => $total,
                'total_bv' => $totalBv,
                'payment_method' => $paymentMode,
                'approved_by' => null,
                'approved_at' => null,
            ]);

            foreach ($lines as $line) {
                $product = $line['product'];

                OrderItem::query()->create([
                    'order_id' => $order->id,
                    'product_id' => $product->id,
                    'product_name' => $product->name,
                    'sku' => $product->sku,
                    'quantity' => $line['quantity'],
                    'unit_price' => $product->dp_price,
                    'unit_bv' => $product->bv,
                    'gst_percent' => $product->gst_percent,
                    'line_total' => $line['line_total'],
                    'line_bv' => $line['line_bv'],
                ]);
            }

            $payment = PaymentTransaction::query()->create([
                'user_id' => $customer->id,
                'order_id' => $order->id,
                'gateway' => $paymentMode,
                'merchant_order_ref' => $order->order_number,
                'amount' => $total,
                'payment_for' => 'order',
                'status' => in_array($paymentMode, ['cash', 'wallet'], true) ? 'success' : 'pending',
                'request_payload' => $payload,
            ]);

            if ($paymentMode === 'wallet') {
                $this->walletService->debit(
                    $customer->id,
                    'repurchase',
                    $total,
                    'franchise_order',
                    $order->id,
                    [
                        'reference' => 'FS-WALLET-'.$order->id,
                        'display_reference' => $order->order_number,
                        'narration' => 'Franchise purchase',
                    ]
                );
            }

            if ($paymentMode === 'upi') {
                $utr = strtoupper(trim((string) ($payload['utr'] ?? '')));
                if ($utr === '') {
                    throw ValidationException::withMessages(['utr' => 'UTR is required for UPI payments.']);
                }
                $upi = $this->upiVerificationService->submitUTR($customer->id, [
                    'purpose' => 'order',
                    'utr' => $utr,
                    'amount' => $total,
                    'remark' => 'Franchise order',
                ]);
                $upi->payment_transaction_id = $payment->id;
                $upi->save();
            }

            if (in_array($paymentMode, ['cash', 'wallet'], true)) {
                $this->approveOrder($order, $payload['approved_by'] ?? null);
            }

            return $order;
        });
    }

    public function approveOrder(Order $order, ?int $approvedBy = null): void
    {
        if ($order->status === 'approved') {
            return;
        }

        DB::transaction(function () use ($order, $approvedBy): void {
            $order->status = 'approved';
            $order->payment_status = 'paid';
            $order->approved_at = now();
            $order->approved_by = $approvedBy;
            $order->save();

            $items = $order->items()->get();
            foreach ($items as $item) {
                $this->stockService->removeStock(
                    $order->franchise_id ?? 0,
                    $item->product_id,
                    (float) $item->quantity,
                    (float) $item->unit_price,
                    (float) $item->unit_bv,
                    'stock_out',
                    [
                        'order_id' => $order->id,
                        'reference' => $order->order_number,
                        'narration' => 'Stock sold',
                        'created_by' => $approvedBy,
                    ]
                );
            }

            FranchiseTransaction::query()->create([
                'franchise_id' => $order->franchise_id,
                'order_id' => $order->id,
                'transaction_type' => 'sale',
                'amount' => (float) $order->total,
                'bv' => (float) $order->total_bv,
                'quantity' => 0,
                'reference' => $order->order_number,
                'narration' => 'Franchise sale',
                'transacted_at' => now(),
                'created_by' => $approvedBy,
            ]);

            $franchise = Franchise::query()->find($order->franchise_id);
            if ($franchise) {
                $this->commissionService->creditForOrder($franchise, $order);
            }

            if (! BinaryLedger::query()
                ->where('reference_type', 'order_approved')
                ->where('reference_id', $order->id)
                ->exists()
            ) {
                $user = $order->user;
                if ($user) {
                    $this->binaryLedgerService->postRepurchaseUpline(
                        $user,
                        (float) $order->total_bv,
                        'order_approved',
                        $order->id
                    );
                }
            }
        });
    }
}

