<?php

use App\Models\Ecommerce\Product\Product\Product;
use App\Models\Ecommerce\Product\ProductView\ProductView;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;

uses(RefreshDatabase::class);

beforeEach(function () {
    Config::set('ecommerce.api.token', 'test-token');

    $this->user = User::factory()->create([
        'password' => 'password',
        'phone_number' => 61929248,
    ]);

    $this->createProduct = function (array $attributes = []) {
        $name = $attributes['name'] ?? 'Test Product '.Str::random(5);

        return Product::create(array_merge([
            'name' => $name,
            'slug' => Str::slug($name),
            'is_visible' => true,
            'price_amount' => 100.00,
            'stock' => 10,
            'parent_id' => null,
        ], $attributes));
    };
});

test('authenticated user viewing a product records the view', function () {
    $product = ($this->createProduct)();

    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product->id}")
        ->assertOk();

    $this->assertDatabaseHas('product_views', [
        'user_id' => $this->user->id,
        'product_id' => $product->id,
    ]);
});

test('unauthenticated user viewing a product does not record the view', function () {
    $product = ($this->createProduct)();

    $this->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product->id}")
        ->assertOk();

    $this->assertDatabaseMissing('product_views', [
        'product_id' => $product->id,
    ]);
});

test('viewing the same product again updates the timestamp', function () {
    $product = ($this->createProduct)();

    // First view
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product->id}");

    $firstView = ProductView::where('user_id', $this->user->id)
        ->where('product_id', $product->id)
        ->first();

    // Travel into the future
    $this->travel(1)->hour();

    // Second view
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product->id}");

    $secondView = ProductView::where('user_id', $this->user->id)
        ->where('product_id', $product->id)
        ->first();

    expect($secondView->updated_at->gt($firstView->updated_at))->toBeTrue();
    // Ensure no duplicate records
    expect(ProductView::where('user_id', $this->user->id)->count())->toBe(1);
});

test('authenticated user can list viewed products', function () {
    $product1 = ($this->createProduct)(['name' => 'Product 1']);
    $product2 = ($this->createProduct)(['name' => 'Product 2']);

    // View products
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product1->id}");

    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product2->id}");

    // List viewed products
    $response = $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson('/api/v1/products/viewed');

    $response->assertOk()
        ->assertJsonStructure([
            'data' => [
                '*' => [
                    'id',
                    'name',
                    'slug',
                    'price_amount',
                ],
            ],
        ]);

    $this->assertCount(2, $response->json('data'));
});

test('viewed products are sorted by most recently viewed', function () {
    $product1 = ($this->createProduct)(['name' => 'First Viewed']);
    $product2 = ($this->createProduct)(['name' => 'Second Viewed']);
    $product3 = ($this->createProduct)(['name' => 'Third Viewed']);

    // View sequence: 1 -> 2 -> 3 -> 1
    // Expected order: 1, 3, 2

    // View 1
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product1->id}");

    $this->travel(1)->minute();

    // View 2
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product2->id}");

    $this->travel(1)->minute();

    // View 3
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product3->id}");

    $this->travel(1)->minute();

    // View 1 again
    $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson("/api/v1/products/{$product1->id}");

    $response = $this->actingAs($this->user, 'sanctum')
        ->withHeaders(['Api-Token' => 'test-token'])
        ->getJson('/api/v1/products/viewed');

    $data = $response->json('data');

    expect($data[0]['id'])->toBe($product1->id)
        ->and($data[1]['id'])->toBe($product3->id)
        ->and($data[2]['id'])->toBe($product2->id);
});

test('unauthenticated user cannot list viewed products', function () {
    $this->withHeaders(['Api-Token' => 'test-token'])
        ->getJson('/api/v1/products/viewed')
        ->assertStatus(401);
});
