<?php

declare(strict_types=1);

use App\Models\CartItem;
use App\Models\Plan;
use App\Models\PlanPrice;
use App\Models\User;
use App\Services\Billing\CartService;
use Database\Seeders\RoleAndPermissionSeeder;

beforeEach(function (): void {
    $this->seed(RoleAndPermissionSeeder::class);

    $this->user = User::factory()->create();
    $this->user->assignRole('customer');

    $this->plan = Plan::factory()->create([
        'name' => 'VPS Basic',
        'price' => 10.00,
        'service_type' => 'vps',
        'status' => 'active',
    ]);

    PlanPrice::factory()->create([
        'plan_id' => $this->plan->id,
        'billing_cycle' => 'monthly',
        'price' => 10.00,
    ]);

    PlanPrice::factory()->create([
        'plan_id' => $this->plan->id,
        'billing_cycle' => 'annual',
        'price' => 100.00,
    ]);
});

test('CartService can add item for a user', function (): void {
    $service = app(CartService::class);

    $item = $service->addItem($this->user, $this->plan, 'monthly');

    expect($item)->toBeInstanceOf(CartItem::class);
    expect($item->user_id)->toBe($this->user->id);
    expect($item->plan_id)->toBe($this->plan->id);
    expect($item->billing_cycle)->toBe('monthly');
    expect($item->quantity)->toBe(1);
});

test('CartService increments quantity when adding duplicate item', function (): void {
    $service = app(CartService::class);

    $service->addItem($this->user, $this->plan, 'monthly');
    $service->addItem($this->user, $this->plan, 'monthly', 2);

    $items = $service->getItems($this->user);
    expect($items)->toHaveCount(1);
    expect($items->first()->quantity)->toBe(3);
});

test('CartService can add item for a session', function (): void {
    $service = app(CartService::class);

    $item = $service->addItem('session-123', $this->plan, 'monthly');

    expect($item->session_id)->toBe('session-123');
    expect($item->user_id)->toBeNull();
});

test('CartService can remove item', function (): void {
    $service = app(CartService::class);

    $item = $service->addItem($this->user, $this->plan, 'monthly');
    $service->removeItem($item->id);

    expect($service->getItems($this->user))->toHaveCount(0);
});

test('CartService can update quantity', function (): void {
    $service = app(CartService::class);

    $item = $service->addItem($this->user, $this->plan, 'monthly');
    $service->updateQuantity($item->id, 5);

    $updated = CartItem::find($item->id);
    expect($updated->quantity)->toBe(5);
});

test('CartService removes item when updating quantity to zero', function (): void {
    $service = app(CartService::class);

    $item = $service->addItem($this->user, $this->plan, 'monthly');
    $service->updateQuantity($item->id, 0);

    expect(CartItem::find($item->id))->toBeNull();
});

test('CartService calculates total correctly', function (): void {
    $service = app(CartService::class);

    $service->addItem($this->user, $this->plan, 'monthly', 2);

    $total = $service->getTotal($this->user);
    expect($total)->toBe(20.00); // 10.00 * 2
});

test('CartService calculates total with annual cycle', function (): void {
    $service = app(CartService::class);

    $service->addItem($this->user, $this->plan, 'annual', 1);

    $total = $service->getTotal($this->user);
    expect($total)->toBe(100.00); // annual price
});

test('CartService gets item count', function (): void {
    $service = app(CartService::class);

    $service->addItem($this->user, $this->plan, 'monthly', 3);

    $plan2 = Plan::factory()->create(['price' => 20.00, 'status' => 'active']);
    PlanPrice::factory()->create([
        'plan_id' => $plan2->id,
        'billing_cycle' => 'monthly',
        'price' => 20.00,
    ]);
    $service->addItem($this->user, $plan2, 'monthly', 2);

    expect($service->getItemCount($this->user))->toBe(5);
});

test('CartService can merge session cart to user', function (): void {
    $service = app(CartService::class);

    // Add item to session
    $service->addItem('session-abc', $this->plan, 'monthly', 2);

    // Merge to user
    $service->mergeSessionToUser('session-abc', $this->user);

    // Session should be empty
    expect($service->getItems('session-abc'))->toHaveCount(0);

    // User should have the item
    $userItems = $service->getItems($this->user);
    expect($userItems)->toHaveCount(1);
    expect($userItems->first()->quantity)->toBe(2);
});

test('CartService merge combines quantities for duplicates', function (): void {
    $service = app(CartService::class);

    // Add items to both session and user with same plan
    $service->addItem($this->user, $this->plan, 'monthly', 1);
    $service->addItem('session-xyz', $this->plan, 'monthly', 3);

    $service->mergeSessionToUser('session-xyz', $this->user);

    $userItems = $service->getItems($this->user);
    expect($userItems)->toHaveCount(1);
    expect($userItems->first()->quantity)->toBe(4);
});

test('CartService can clear cart', function (): void {
    $service = app(CartService::class);

    $service->addItem($this->user, $this->plan, 'monthly', 2);
    $service->clear($this->user);

    expect($service->getItems($this->user))->toHaveCount(0);
});

test('authenticated user can view cart page', function (): void {
    $response = $this->actingAs($this->user)
        ->get('http://account.ezscale.dev/cart');

    $response->assertOk();
});

test('authenticated user can add item to cart', function (): void {
    $response = $this->actingAs($this->user)
        ->post('http://account.ezscale.dev/cart', [
            'plan_id' => $this->plan->id,
            'billing_cycle' => 'monthly',
            'quantity' => 1,
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('cart_items', [
        'user_id' => $this->user->id,
        'plan_id' => $this->plan->id,
    ]);
});

test('authenticated user can update cart item quantity', function (): void {
    $cartItem = CartItem::factory()->create([
        'user_id' => $this->user->id,
        'plan_id' => $this->plan->id,
        'billing_cycle' => 'monthly',
        'quantity' => 1,
    ]);

    $response = $this->actingAs($this->user)
        ->put("http://account.ezscale.dev/cart/{$cartItem->id}", [
            'quantity' => 3,
        ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('cart_items', [
        'id' => $cartItem->id,
        'quantity' => 3,
    ]);
});

test('authenticated user can remove cart item', function (): void {
    $cartItem = CartItem::factory()->create([
        'user_id' => $this->user->id,
        'plan_id' => $this->plan->id,
    ]);

    $response = $this->actingAs($this->user)
        ->delete("http://account.ezscale.dev/cart/{$cartItem->id}");

    $response->assertRedirect();

    $this->assertDatabaseMissing('cart_items', [
        'id' => $cartItem->id,
    ]);
});

test('user cannot modify another users cart item', function (): void {
    $otherUser = User::factory()->create();
    $otherUser->assignRole('customer');

    $cartItem = CartItem::factory()->create([
        'user_id' => $otherUser->id,
        'plan_id' => $this->plan->id,
    ]);

    $response = $this->actingAs($this->user)
        ->put("http://account.ezscale.dev/cart/{$cartItem->id}", [
            'quantity' => 5,
        ]);

    $response->assertForbidden();
});

test('user cannot remove another users cart item', function (): void {
    $otherUser = User::factory()->create();
    $otherUser->assignRole('customer');

    $cartItem = CartItem::factory()->create([
        'user_id' => $otherUser->id,
        'plan_id' => $this->plan->id,
    ]);

    $response = $this->actingAs($this->user)
        ->delete("http://account.ezscale.dev/cart/{$cartItem->id}");

    $response->assertForbidden();
});
