<?php

use App\Models\Artwork;
use App\Models\ArtworkStats;
use App\Models\ArtworkMetricSnapshotHourly;
use Illuminate\Foundation\Testing\RefreshDatabase;

/**
 * Helper: create an artwork row without triggering observers (avoids GREATEST() SQLite issue).
 */
function createArtworkWithoutObserver(array $attrs = []): Artwork
{
    return Artwork::withoutEvents(function () use ($attrs) {
        return Artwork::factory()->create($attrs);
    });
}

// ─── Snapshot Collection Command ───────────────────────────────────────────

it('nova:metrics-snapshot-hourly runs without errors', function () {
    $this->artisan('nova:metrics-snapshot-hourly --dry-run')
        ->assertSuccessful();
});

it('creates snapshot rows for eligible artworks', function () {
    $artwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subDay(),
    ]);

    ArtworkStats::upsert([
        [
            'artwork_id'  => $artwork->id,
            'views'       => 100,
            'downloads'   => 10,
            'favorites'   => 5,
            'comments_count' => 2,
            'shares_count'   => 1,
        ],
    ], ['artwork_id']);

    $this->artisan('nova:metrics-snapshot-hourly')
        ->assertSuccessful();

    $snapshot = ArtworkMetricSnapshotHourly::where('artwork_id', $artwork->id)->first();
    expect($snapshot)->not->toBeNull();
    expect((int) $snapshot->views_count)->toBe(100);
    expect((int) $snapshot->downloads_count)->toBe(10);
    expect((int) $snapshot->favourites_count)->toBe(5);
});

it('upserts on duplicate bucket_hour', function () {
    $artwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subDay(),
    ]);

    ArtworkStats::upsert([
        [
            'artwork_id' => $artwork->id,
            'views'      => 50,
            'downloads'  => 5,
            'favorites'  => 2,
        ],
    ], ['artwork_id']);

    // Run twice — should not throw
    $this->artisan('nova:metrics-snapshot-hourly')->assertSuccessful();

    // Update stats and run again
    ArtworkStats::where('artwork_id', $artwork->id)->update(['views' => 75]);
    $this->artisan('nova:metrics-snapshot-hourly')->assertSuccessful();

    $count = ArtworkMetricSnapshotHourly::where('artwork_id', $artwork->id)->count();
    expect($count)->toBe(1); // upserted, not duplicated

    $snapshot = ArtworkMetricSnapshotHourly::where('artwork_id', $artwork->id)->first();
    expect((int) $snapshot->views_count)->toBe(75);
});

// ─── Heat Recalculation Command ────────────────────────────────────────────

it('nova:recalculate-heat runs without errors', function () {
    $this->artisan('nova:recalculate-heat --dry-run')
        ->assertSuccessful();
});

it('computes heat_score from snapshot deltas', function () {
    $artwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subHours(2),
    ]);

    ArtworkStats::upsert([
        ['artwork_id' => $artwork->id, 'views' => 0, 'downloads' => 0, 'favorites' => 0],
    ], ['artwork_id']);

    $prevHour    = now()->startOfHour()->subHour();
    $currentHour = now()->startOfHour();

    // Previous hour snapshot
    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => $prevHour,
        'views_count'      => 10,
        'downloads_count'  => 2,
        'favourites_count' => 1,
        'comments_count'   => 0,
        'shares_count'     => 0,
    ]);

    // Current hour snapshot (engagement grew)
    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => $currentHour,
        'views_count'      => 30,
        'downloads_count'  => 5,
        'favourites_count' => 4,
        'comments_count'   => 2,
        'shares_count'     => 1,
    ]);

    $this->artisan('nova:recalculate-heat')
        ->assertSuccessful();

    $stat = ArtworkStats::where('artwork_id', $artwork->id)->first();
    expect((float) $stat->heat_score)->toBeGreaterThan(0);

    // Verify delta values cached on stats
    expect((int) $stat->views_1h)->toBe(20);       // 30 - 10
    expect((int) $stat->downloads_1h)->toBe(3);     // 5 - 2
    expect((int) $stat->favourites_1h)->toBe(3);    // 4 - 1
    expect((int) $stat->comments_1h)->toBe(2);      // 2 - 0
    expect((int) $stat->shares_1h)->toBe(1);        // 1 - 0
});

it('handles negative deltas gracefully by clamping to zero', function () {
    $artwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subHour(),
    ]);

    ArtworkStats::upsert([
        ['artwork_id' => $artwork->id, 'views' => 0, 'downloads' => 0, 'favorites' => 0],
    ], ['artwork_id']);

    $prevHour    = now()->startOfHour()->subHour();
    $currentHour = now()->startOfHour();

    // Simulate counter reset: current < previous
    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => $prevHour,
        'views_count'      => 100,
        'downloads_count'  => 50,
        'favourites_count' => 20,
        'comments_count'   => 10,
        'shares_count'     => 5,
    ]);

    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => $currentHour,
        'views_count'      => 50,  // < prev
        'downloads_count'  => 30,  // < prev
        'favourites_count' => 10,  // < prev
        'comments_count'   => 5,   // < prev
        'shares_count'     => 2,   // < prev
    ]);

    $this->artisan('nova:recalculate-heat')
        ->assertSuccessful();

    $stat = ArtworkStats::where('artwork_id', $artwork->id)->first();
    expect((float) $stat->heat_score)->toBe(0.0); // all deltas negative → clamped to 0
    expect((int) $stat->views_1h)->toBe(0);
    expect((int) $stat->downloads_1h)->toBe(0);
});

// ─── Pruning Command ──────────────────────────────────────────────────────

it('nova:prune-metric-snapshots removes old data', function () {
    $artwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subDays(30),
    ]);

    // Old snapshot (10 days ago)
    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => now()->subDays(10)->startOfHour(),
        'views_count'      => 50,
        'downloads_count'  => 5,
        'favourites_count' => 2,
        'comments_count'   => 0,
        'shares_count'     => 0,
    ]);

    // Recent snapshot (1 hour ago)
    ArtworkMetricSnapshotHourly::create([
        'artwork_id'       => $artwork->id,
        'bucket_hour'      => now()->subHour()->startOfHour(),
        'views_count'      => 100,
        'downloads_count'  => 10,
        'favourites_count' => 5,
        'comments_count'   => 1,
        'shares_count'     => 0,
    ]);

    $this->artisan('nova:prune-metric-snapshots --keep-days=7')
        ->assertSuccessful();

    $remaining = ArtworkMetricSnapshotHourly::where('artwork_id', $artwork->id)->count();
    expect($remaining)->toBe(1); // only the recent one survives
});

// ─── Heat Formula Unit Check ───────────────────────────────────────────────

it('heat formula applies age factor correctly', function () {
    // Newer artwork should get higher heat than older one with same deltas
    $newArtwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subHours(1),
        'created_at'   => now()->subHours(1),
    ]);

    $oldArtwork = createArtworkWithoutObserver([
        'is_approved' => true,
        'is_public'   => true,
        'published_at' => now()->subDays(30),
        'created_at'   => now()->subDays(30),
    ]);

    $prevHour    = now()->startOfHour()->subHour();
    $currentHour = now()->startOfHour();

    foreach ([$newArtwork, $oldArtwork] as $art) {
        ArtworkStats::upsert([
            ['artwork_id' => $art->id, 'views' => 0, 'downloads' => 0, 'favorites' => 0],
        ], ['artwork_id']);

        ArtworkMetricSnapshotHourly::create([
            'artwork_id'       => $art->id,
            'bucket_hour'      => $prevHour,
            'views_count'      => 0,
            'downloads_count'  => 0,
            'favourites_count' => 0,
            'comments_count'   => 0,
            'shares_count'     => 0,
        ]);

        ArtworkMetricSnapshotHourly::create([
            'artwork_id'       => $art->id,
            'bucket_hour'      => $currentHour,
            'views_count'      => 100,
            'downloads_count'  => 10,
            'favourites_count' => 5,
            'comments_count'   => 3,
            'shares_count'     => 2,
        ]);
    }

    $this->artisan('nova:recalculate-heat')->assertSuccessful();

    $newStat = ArtworkStats::where('artwork_id', $newArtwork->id)->first();
    $oldStat = ArtworkStats::where('artwork_id', $oldArtwork->id)->first();

    expect((float) $newStat->heat_score)->toBeGreaterThan(0);
    expect((float) $oldStat->heat_score)->toBeGreaterThan(0);
    // Newer artwork should have higher heat score due to age factor
    expect((float) $newStat->heat_score)->toBeGreaterThan((float) $oldStat->heat_score);
});
