<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class PricingService
{
    public function createQuote(int $cartId, int $userId): array
    {
        return DB::transaction(function () use ($cartId, $userId) {
            $cart = DB::table('carts')->where('id', $cartId)->where('user_id', $userId)->first();

            if (! $cart) {
                throw new \RuntimeException('Cart not found.');
            }

            $items = DB::table('cart_items')
                ->join('services', 'services.id', '=', 'cart_items.service_id')
                ->leftJoin('service_prices', function ($join) {
                    $join->on('service_prices.service_id', '=', 'cart_items.service_id')
                        ->whereNull('service_prices.variant_id');
                })
                ->where('cart_items.cart_id', $cartId)
                ->select(
                    'cart_items.*',
                    'services.name as service_name',
                    'services.default_duration_minutes',
                    DB::raw('COALESCE(service_prices.base_price, 199) as unit_price'),
                    'service_prices.price_book_id'
                )
                ->get();

            if ($items->isEmpty()) {
                throw new \RuntimeException('Cart is empty.');
            }

            $subtotal = 0;
            $duration = 0;
            $snapshotItems = [];
            $priceBookId = null;

            foreach ($items as $item) {
                $line = (float) $item->unit_price * (int) $item->quantity;
                $subtotal += $line;
                $duration += ((int) ($item->duration_minutes ?: $item->default_duration_minutes)) * (int) $item->quantity;
                $priceBookId = $priceBookId ?: $item->price_book_id;
                $snapshotItems[] = [
                    'cart_item_id' => $item->id,
                    'service_id' => $item->service_id,
                    'service_name' => $item->service_name,
                    'quantity' => $item->quantity,
                    'duration_minutes' => $item->duration_minutes ?: $item->default_duration_minutes,
                    'unit_price' => (float) $item->unit_price,
                    'line_total' => $line,
                ];
            }

            $feesTotal = $this->calculateFees($subtotal);
            $discountTotal = $this->calculateDiscount($cart->coupon_code, $subtotal);
            $walletApplied = $this->calculateWallet($cart->use_wallet, $userId, $subtotal + $feesTotal - $discountTotal);
            $taxTotal = $this->calculateTaxes(max(0, $subtotal + $feesTotal - $discountTotal));
            $payableTotal = max(0, $subtotal + $feesTotal - $discountTotal - $walletApplied + $taxTotal);

            $quoteId = DB::table('quotes')->insertGetId([
                'cart_id' => $cartId,
                'user_id' => $userId,
                'quote_number' => 'QT-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)),
                'price_book_id' => $priceBookId,
                'pricing_snapshot_json' => json_encode([
                    'items' => $snapshotItems,
                    'booking_type' => $cart->booking_type,
                    'duration_minutes' => $duration,
                    'coupon_code' => $cart->coupon_code,
                    'generated_at' => now()->toIso8601String(),
                ]),
                'subtotal' => $subtotal,
                'fees_total' => $feesTotal,
                'discount_total' => $discountTotal,
                'wallet_applied' => $walletApplied,
                'tax_total' => $taxTotal,
                'payable_total' => $payableTotal,
                'expires_at' => now()->addMinutes(15),
                'status' => 'active',
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            DB::table('carts')->where('id', $cartId)->update([
                'status' => 'quoted',
                'updated_at' => now(),
            ]);

            return (array) DB::table('quotes')->where('id', $quoteId)->first();
        });
    }

    private function calculateFees(float $subtotal): float
    {
        return (float) DB::table('fee_rules')
            ->where('status', 'active')
            ->get()
            ->sum(function ($fee) use ($subtotal) {
                return $fee->calculation_type === 'percent'
                    ? round($subtotal * ((float) $fee->value / 100), 2)
                    : (float) $fee->value;
            });
    }

    private function calculateDiscount(?string $couponCode, float $subtotal): float
    {
        if (! $couponCode) {
            return 0;
        }

        $coupon = DB::table('coupons')
            ->where('code', strtoupper($couponCode))
            ->where('status', 'active')
            ->first();

        if (! $coupon || ($coupon->min_order_amount && $subtotal < (float) $coupon->min_order_amount)) {
            return 0;
        }

        $discount = $coupon->discount_type === 'percent'
            ? round($subtotal * ((float) $coupon->discount_value / 100), 2)
            : (float) $coupon->discount_value;

        return $coupon->max_discount ? min($discount, (float) $coupon->max_discount) : $discount;
    }

    private function calculateWallet(bool $useWallet, int $userId, float $amount): float
    {
        if (! $useWallet) {
            return 0;
        }

        $wallet = DB::table('wallet_accounts')->where('user_id', $userId)->first();

        return $wallet ? min((float) $wallet->balance, max(0, $amount)) : 0;
    }

    private function calculateTaxes(float $taxableAmount): float
    {
        $percent = (float) DB::table('tax_rules')->where('status', 'active')->sum('percent');

        return round($taxableAmount * ($percent / 100), 2);
    }
}
