<?php

declare(strict_types=1);

namespace App\Services\Billing;

use App\Models\CartItem;
use App\Models\Plan;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;

class CartService
{
    /**
     * Add an item to the cart.
     */
    public function addItem(
        User|string $userOrSession,
        Plan $plan,
        string $cycle,
        int $qty = 1,
        ?array $config = null,
        ?array $provisioningConfig = null,
        ?string $couponCode = null,
    ): CartItem {
        $attributes = $this->resolveOwnerAttributes($userOrSession);

        // Check if the same plan + cycle already exists in the cart
        $existing = CartItem::query()
            ->where($attributes)
            ->where('plan_id', $plan->id)
            ->where('billing_cycle', $cycle)
            ->first();

        if ($existing) {
            $existing->update(['quantity' => $existing->quantity + $qty]);

            return $existing;
        }

        return CartItem::create(array_merge($attributes, [
            'plan_id' => $plan->id,
            'billing_cycle' => $cycle,
            'quantity' => $qty,
            'config_selections' => $config,
            'provisioning_config' => $provisioningConfig,
            'coupon_code' => $couponCode,
        ]));
    }

    /**
     * Remove an item from the cart.
     */
    public function removeItem(int $cartItemId): void
    {
        CartItem::query()->where('id', $cartItemId)->delete();
    }

    /**
     * Update the quantity of a cart item.
     */
    public function updateQuantity(int $cartItemId, int $qty): void
    {
        if ($qty <= 0) {
            $this->removeItem($cartItemId);

            return;
        }

        CartItem::query()->where('id', $cartItemId)->update(['quantity' => $qty]);
    }

    /**
     * Get all cart items for a user or session.
     *
     * @return Collection<int, CartItem>
     */
    public function getItems(User|string $userOrSession): Collection
    {
        $attributes = $this->resolveOwnerAttributes($userOrSession);

        return CartItem::query()
            ->where($attributes)
            ->with('plan.prices')
            ->orderBy('created_at')
            ->get();
    }

    /**
     * Get the total price of all cart items.
     */
    public function getTotal(User|string $userOrSession): float
    {
        $items = $this->getItems($userOrSession);
        $total = 0.0;

        foreach ($items as $item) {
            $price = $this->getItemPrice($item);
            $total += $price * $item->quantity;
        }

        return round($total, 2);
    }

    /**
     * Get the number of items in the cart.
     */
    public function getItemCount(User|string $userOrSession): int
    {
        $attributes = $this->resolveOwnerAttributes($userOrSession);

        return (int) CartItem::query()->where($attributes)->sum('quantity');
    }

    /**
     * Merge session cart items into a user's cart on login.
     */
    public function mergeSessionToUser(string $sessionId, User $user): void
    {
        $sessionItems = CartItem::query()
            ->where('session_id', $sessionId)
            ->whereNull('user_id')
            ->get();

        foreach ($sessionItems as $sessionItem) {
            $existing = CartItem::query()
                ->where('user_id', $user->id)
                ->where('plan_id', $sessionItem->plan_id)
                ->where('billing_cycle', $sessionItem->billing_cycle)
                ->first();

            if ($existing) {
                $existing->update(['quantity' => $existing->quantity + $sessionItem->quantity]);
                $sessionItem->delete();
            } else {
                $sessionItem->update([
                    'user_id' => $user->id,
                    'session_id' => null,
                ]);
            }
        }
    }

    /**
     * Clear all cart items for a user or session.
     */
    public function clear(User|string $userOrSession): void
    {
        $attributes = $this->resolveOwnerAttributes($userOrSession);

        CartItem::query()->where($attributes)->delete();
    }

    /**
     * Get the price for a single cart item based on its billing cycle.
     */
    public function getItemPrice(CartItem $item): float
    {
        $plan = $item->plan;

        if (! $plan) {
            return 0.0;
        }

        $planPrice = $plan->priceForCycle($item->billing_cycle);

        if ($planPrice) {
            return (float) $planPrice->price;
        }

        return (float) $plan->price;
    }

    /**
     * Resolve owner attributes for querying (user_id or session_id).
     *
     * @return array<string, mixed>
     */
    private function resolveOwnerAttributes(User|string $userOrSession): array
    {
        if ($userOrSession instanceof User) {
            return ['user_id' => $userOrSession->id];
        }

        return ['session_id' => $userOrSession];
    }
}
