<?php

declare(strict_types=1);

use App\Models\Artwork;
use App\Models\User;
use App\Services\ArtworkStatsService;
use Illuminate\Support\Facades\DB;

beforeEach(function () {
    // Disable Meilisearch and Redis during tests
    config(['scout.driver' => 'null']);
});

// ── ArtworkViewController (POST /api/art/{id}/view) ──────────────────────────

it('returns 404 for a non-existent artwork on view', function () {
    $this->postJson('/api/art/99999/view')->assertStatus(404);
});

it('returns 404 for a private artwork on view', function () {
    $artwork = Artwork::factory()->create(['is_public' => false]);
    $this->postJson("/api/art/{$artwork->id}/view")->assertStatus(404);
});

it('returns 404 for an unapproved artwork on view', function () {
    $artwork = Artwork::factory()->create(['is_approved' => false]);
    $this->postJson("/api/art/{$artwork->id}/view")->assertStatus(404);
});

it('records a view and returns ok=true on first call', function () {
    $artwork = Artwork::factory()->create([
        'is_public'    => true,
        'is_approved'  => true,
        'published_at' => now()->subDay(),
    ]);

    // Ensure a stats row exists with 0 views
    DB::table('artwork_stats')->insertOrIgnore([
        'artwork_id'   => $artwork->id,
        'views'        => 0,
        'views_24h'    => 0,
        'views_7d'     => 0,
        'downloads'    => 0,
        'favorites'    => 0,
        'rating_avg'   => 0,
        'rating_count' => 0,
    ]);

    $response = $this->postJson("/api/art/{$artwork->id}/view");

    $response->assertStatus(200)
             ->assertJsonPath('ok', true)
             ->assertJsonPath('counted', true);

    expect(DB::table('artwork_stats')->where('artwork_id', $artwork->id)->value('views'))->toBe(1);
    expect(DB::table('artwork_view_events')->where('artwork_id', $artwork->id)->count())->toBe(1);
});

it('counts repeated visits for the same artwork', function () {
    $artwork = Artwork::factory()->create([
        'is_public'    => true,
        'is_approved'  => true,
        'published_at' => now()->subDay(),
    ]);

    DB::table('artwork_stats')->insertOrIgnore([
        'artwork_id'   => $artwork->id,
        'views'        => 0,
        'views_24h'    => 0,
        'views_7d'     => 0,
        'downloads'    => 0,
        'favorites'    => 0,
        'rating_avg'   => 0,
        'rating_count' => 0,
    ]);

    $this->postJson("/api/art/{$artwork->id}/view")
        ->assertStatus(200)
        ->assertJsonPath('counted', true);

    $this->postJson("/api/art/{$artwork->id}/view")
        ->assertStatus(200)
        ->assertJsonPath('counted', true);

    expect(DB::table('artwork_stats')->where('artwork_id', $artwork->id)->value('views'))->toBe(2);
    expect(DB::table('artwork_view_events')->where('artwork_id', $artwork->id)->count())->toBe(2);
});

// ── ArtworkDownloadController (POST /api/art/{id}/download) ──────────────────

it('returns 404 for a non-existent artwork on download', function () {
    $this->postJson('/api/art/99999/download')->assertStatus(404);
});

it('returns 404 for a private artwork on download', function () {
    $artwork = Artwork::factory()->create(['is_public' => false]);
    $this->postJson("/api/art/{$artwork->id}/download")->assertStatus(404);
});

it('records a download and returns ok=true with a url', function () {
    $artwork = Artwork::factory()->create([
        'is_public'    => true,
        'is_approved'  => true,
        'published_at' => now()->subDay(),
    ]);

    $mock = $this->mock(ArtworkStatsService::class);
    $mock->shouldReceive('incrementDownloads')
        ->once()
        ->with($artwork->id, 1, false);

    $response = $this->postJson("/api/art/{$artwork->id}/download");

    $response->assertStatus(200)
             ->assertJsonPath('ok', true)
             ->assertJsonStructure(['ok', 'url']);
});

it('inserts a row in artwork_downloads on valid download', function () {
    $user    = User::factory()->create();
    $artwork = Artwork::factory()->create([
        'is_public'    => true,
        'is_approved'  => true,
        'published_at' => now()->subDay(),
    ]);

    // Stub the stats service so we don't need Redis
    $mock = $this->mock(ArtworkStatsService::class);
    $mock->shouldReceive('incrementDownloads')->once()->with($artwork->id, 1, false);

    $this->actingAs($user)->postJson("/api/art/{$artwork->id}/download");

    $this->assertDatabaseHas('artwork_downloads', [
        'artwork_id' => $artwork->id,
        'user_id'    => $user->id,
    ]);
});

it('records download as guest (no user_id) when unauthenticated', function () {
    $artwork = Artwork::factory()->create([
        'is_public'    => true,
        'is_approved'  => true,
        'published_at' => now()->subDay(),
    ]);

    $mock = $this->mock(ArtworkStatsService::class);
    $mock->shouldReceive('incrementDownloads')->once()->with($artwork->id, 1, false);

    $this->postJson("/api/art/{$artwork->id}/download");

    $this->assertDatabaseHas('artwork_downloads', [
        'artwork_id' => $artwork->id,
        'user_id'    => null,
    ]);
});

// ── Route names ───────────────────────────────────────────────────────────────

it('view endpoint route is named api.art.view', function () {
    $artwork = Artwork::factory()->create([
        'is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay(),
    ]);
    expect(route('api.art.view', ['id' => $artwork->id]))->toContain("/api/art/{$artwork->id}/view");
});

it('download endpoint route is named api.art.download', function () {
    $artwork = Artwork::factory()->create([
        'is_public' => true, 'is_approved' => true, 'published_at' => now()->subDay(),
    ]);
    expect(route('api.art.download', ['id' => $artwork->id]))->toContain("/api/art/{$artwork->id}/download");
});
