<?php

namespace Tests\Feature;

use App\Models\Cart;
use App\Models\CartItem;
use App\Models\Coupon;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\Setting;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class CartTest extends TestCase
{
    use RefreshDatabase;

    private Product $product;

    private ProductVariant $variant;

    protected function setUp(): void
    {
        parent::setUp();
        $this->product = Product::factory()->create(['original_price' => 2000, 'sale_price' => null]);
        $this->variant = ProductVariant::factory()->for($this->product)->withStock(10)->create();
    }

    public function test_guest_can_add_to_cart(): void
    {
        $response = $this->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 2]);

        $response->assertRedirect();
        $this->assertDatabaseHas('cart_items', ['product_variant_id' => $this->variant->id, 'quantity' => 2]);
        $this->assertDatabaseHas('carts', ['user_id' => null]);
    }

    public function test_cart_page_renders_items_for_a_logged_in_customer(): void
    {
        $user = $this->customer();
        $this->actingAs($user)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 2]);

        $response = $this->actingAs($user)->get('/cart');

        $response->assertOk()->assertSee($this->product->name)->assertDontSee('Your cart is empty');
    }

    public function test_cannot_add_more_than_available_stock(): void
    {
        $response = $this->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 999]);

        $response->assertSessionHas('error');
        $this->assertDatabaseMissing('cart_items', ['product_variant_id' => $this->variant->id]);
    }

    public function test_can_update_and_remove_cart_items(): void
    {
        $user = $this->customer();
        $this->actingAs($user)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 2]);
        $item = CartItem::first();

        $this->actingAs($user)->patch(route('cart.update', $item), ['quantity' => 5])->assertRedirect();
        $this->assertSame(5, $item->fresh()->quantity);

        $this->actingAs($user)->delete(route('cart.remove', $item))->assertRedirect();
        $this->assertDatabaseMissing('cart_items', ['id' => $item->id]);
    }

    public function test_cart_add_update_and_remove_return_json_with_live_counts_and_totals_for_ajax_requests(): void
    {
        $user = $this->customer();

        $this->actingAs($user)
            ->postJson(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 2])
            ->assertOk()
            ->assertJson(['cartCount' => 2]);

        $item = CartItem::first();

        $this->actingAs($user)
            ->patchJson(route('cart.update', $item), ['quantity' => 3])
            ->assertOk()
            ->assertJson([
                'cartCount' => 3,
                'item' => ['id' => $item->id, 'quantity' => 3, 'lineTotal' => 6000.0],
                'totals' => ['subtotal' => 6000.0],
            ]);

        $this->actingAs($user)
            ->deleteJson(route('cart.remove', $item))
            ->assertOk()
            ->assertJson(['cartCount' => 0, 'totals' => ['subtotal' => 0.0]]);
    }

    public function test_cannot_modify_a_cart_item_belonging_to_someone_else(): void
    {
        $owner = $this->customer();
        $intruder = $this->customer();
        $this->actingAs($owner)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 1]);
        $item = CartItem::first();

        $this->actingAs($intruder)->delete(route('cart.remove', $item))->assertForbidden();
        $this->assertDatabaseHas('cart_items', ['id' => $item->id]);
    }

    public function test_percentage_coupon_applies_with_cap(): void
    {
        $user = $this->customer();
        Coupon::factory()->percentage(10, maxDiscount: 300)->create(['code' => 'SAVE10']);
        $this->actingAs($user)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 3]); // 6000

        $this->actingAs($user)->post(route('cart.coupon.apply'), ['code' => 'save10'])->assertRedirect();

        $cart = Cart::where('user_id', $user->id)->first();
        $this->assertSame(300.0, $cart->coupon->calculateDiscount((float) $cart->subtotal));
    }

    public function test_coupon_below_minimum_order_amount_is_rejected(): void
    {
        $user = $this->customer();
        Coupon::factory()->create(['code' => 'BIGSPEND', 'min_order_amount' => 10000]);
        $this->actingAs($user)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 1]);

        $response = $this->actingAs($user)->post(route('cart.coupon.apply'), ['code' => 'BIGSPEND']);

        $response->assertSessionHas('error');
        $this->assertDatabaseHas('carts', ['user_id' => $user->id, 'coupon_id' => null]);
    }

    public function test_free_shipping_applies_above_threshold(): void
    {
        Setting::set('shipping.free_shipping_threshold', 5000);
        Setting::set('shipping.flat_rate', 250);
        $user = $this->customer();

        $this->actingAs($user)->post(route('cart.add', $this->product), ['variant_id' => $this->variant->id, 'quantity' => 3]); // 6000

        $response = $this->actingAs($user)->get('/cart');
        $response->assertSee('Free');
    }
}
