<?php

declare(strict_types=1);

use App\Models\Artwork;
use App\Models\ArtworkStats;
use App\Models\ArtworkVersion;
use App\Models\ArtworkVersionEvent;
use App\Models\User;
use App\Services\ArtworkVersioningService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

// ── Helpers ────────────────────────────────────────────────────────────────────

function versioningArtwork(array $attrs = []): Artwork
{
    return Artwork::withoutEvents(fn () => Artwork::factory()->create($attrs));
}

beforeEach(function () {
    // SQLite GREATEST() polyfill for observer compatibility
    if (DB::connection()->getDriverName() === 'sqlite') {
        DB::connection()->getPdo()->sqliteCreateFunction('GREATEST', function (...$args) {
            return max($args);
        }, -1);
    }

    Cache::flush();
    $this->user    = User::factory()->create();
    $this->artwork = versioningArtwork(['user_id' => $this->user->id, 'hash' => 'aaa', 'version_count' => 1]);
    $this->service = new ArtworkVersioningService();
});

// ──────────────────────────────────────────────────────────────────────────────
// ArtworkVersioningService – unit tests
// ──────────────────────────────────────────────────────────────────────────────

test('createNewVersion inserts a version row and marks it current', function () {
    $version = $this->service->createNewVersion(
        $this->artwork,
        'path/to/new.webp',
        'bbb',
        1920, 1080, 204800,
        $this->user->id,
        'First replacement',
    );

    expect($version)->toBeInstanceOf(ArtworkVersion::class)
        ->and($version->version_number)->toBe(2)
        ->and($version->is_current)->toBeTrue()
        ->and($version->file_hash)->toBe('bbb');

    $this->artwork->refresh();
    expect($this->artwork->current_version_id)->toBe($version->id)
        ->and($this->artwork->version_count)->toBe(2);
});

test('createNewVersion sets previous version is_current = false', function () {
    // Seed an existing "current" version row
    $old = ArtworkVersion::create([
        'artwork_id'     => $this->artwork->id,
        'version_number' => 1,
        'file_path'      => 'old.webp',
        'file_hash'      => 'aaahash',
        'is_current'     => true,
    ]);

    $this->service->createNewVersion(
        $this->artwork, 'new.webp', 'bbbhash',
        1920, 1080, 500, $this->user->id,
    );

    expect(ArtworkVersion::findOrFail($old->id)->is_current)->toBeFalse();
});

test('createNewVersion writes an audit log entry', function () {
    $this->service->createNewVersion(
        $this->artwork, 'path.webp', 'ccc',
        800, 600, 1024, $this->user->id,
    );

    $event = ArtworkVersionEvent::where('artwork_id', $this->artwork->id)->first();
    expect($event)->not->toBeNull()
        ->and($event->action)->toBe('create_version')
        ->and($event->user_id)->toBe($this->user->id);
});

test('createNewVersion rejects identical hash', function () {
    $this->artwork->update(['hash' => 'same_hash_here']);

    expect(fn () => $this->service->createNewVersion(
        $this->artwork, 'path.webp', 'same_hash_here',
        800, 600, 1024, $this->user->id,
    ))->toThrow(\RuntimeException::class, 'identical');
});

test('artworkVersioningService enforces hourly rate limit', function () {
    // Exhaust rate limit
    for ($i = 0; $i < 3; $i++) {
        $hash = 'hash_' . $i;
        $this->artwork->update(['hash' => 'different_' . $i]);  // avoid identical-hash rejection
        $this->service->createNewVersion(
            $this->artwork, 'path.webp', $hash,
            800, 600, 1024, $this->user->id,
        );
    }

    $this->artwork->update(['hash' => 'final_different']);

    expect(fn () => $this->service->createNewVersion(
        $this->artwork, 'path.webp', 'hash_over_limit',
        800, 600, 1024, $this->user->id,
    ))->toThrow(\Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException::class);
});

test('shouldRequireReapproval returns false for first upload', function () {
    $this->artwork->update(['width' => 0, 'height' => 0]);
    expect($this->service->shouldRequireReapproval($this->artwork, 1920, 1080))->toBeFalse();
});

test('shouldRequireReapproval returns true when dimensions change drastically', function () {
    $this->artwork->update(['width' => 1920, 'height' => 1080]);
    // 300% increase in width → triggers
    expect($this->service->shouldRequireReapproval($this->artwork, 7680, 4320))->toBeTrue();
});

test('shouldRequireReapproval returns false for small dimension change', function () {
    $this->artwork->update(['width' => 1920, 'height' => 1080]);
    // 5 % change → fine
    expect($this->service->shouldRequireReapproval($this->artwork, 2016, 1134))->toBeFalse();
});

test('applyRankingProtection decays ranking and heat scores', function () {
    DB::table('artwork_stats')->updateOrInsert(
        ['artwork_id' => $this->artwork->id],
        ['ranking_score' => 100.0, 'heat_score' => 50.0, 'engagement_velocity' => 20.0]
    );

    $this->service->applyRankingProtection($this->artwork);

    $stats = DB::table('artwork_stats')->where('artwork_id', $this->artwork->id)->first();
    expect((float) $stats->ranking_score)->toBeLessThan(100.0)
        ->and((float) $stats->heat_score)->toBeLessThan(50.0);
});

test('restoreVersion clones old version as new current version', function () {
    $old = ArtworkVersion::create([
        'artwork_id'     => $this->artwork->id,
        'version_number' => 1,
        'file_path'      => 'original.webp',
        'file_hash'      => 'oldhash',
        'width'          => 1920,
        'height'         => 1080,
        'file_size'      => 99999,
        'is_current'     => true,
    ]);

    // Simulate artwork being at version 2 with a different hash
    $this->artwork->update(['hash' => 'currenthash', 'version_count' => 2]);

    $restored = $this->service->restoreVersion($old, $this->artwork, $this->user->id);

    expect($restored->version_number)->toBe(3)
        ->and($restored->file_hash)->toBe('oldhash')
        ->and($restored->change_note)->toContain('Restored from version 1');

    $event = ArtworkVersionEvent::where('action', 'create_version')
        ->where('artwork_id', $this->artwork->id)
        ->orderByDesc('id')
        ->first();
    expect($event)->not->toBeNull();
});

// ──────────────────────────────────────────────────────────────────────────────
// Version history API endpoint
// ──────────────────────────────────────────────────────────────────────────────

test('GET studio/artworks/{id}/versions returns version list', function () {
    $this->actingAs($this->user);

    ArtworkVersion::create([
        'artwork_id' => $this->artwork->id, 'version_number' => 1,
        'file_path' => 'a.webp', 'file_hash' => 'hash1', 'is_current' => false,
    ]);
    ArtworkVersion::create([
        'artwork_id' => $this->artwork->id, 'version_number' => 2,
        'file_path' => 'b.webp', 'file_hash' => 'hash2', 'is_current' => true,
    ]);

    $response = $this->getJson("/api/studio/artworks/{$this->artwork->id}/versions");

    $response->assertOk()
        ->assertJsonCount(2, 'versions')
        ->assertJsonPath('versions.0.version_number', 2); // newest first
});

test('GET studio/artworks/{id}/versions rejects other users', function () {
    $other = User::factory()->create();
    $this->actingAs($other);

    $this->getJson("/api/studio/artworks/{$this->artwork->id}/versions")
        ->assertStatus(404);
});

test('POST studio/artworks/{id}/restore/{version_id} restores version', function () {
    $this->actingAs($this->user);

    $old = ArtworkVersion::create([
        'artwork_id' => $this->artwork->id, 'version_number' => 1,
        'file_path' => 'restored.webp', 'file_hash' => 'restorehash',
        'width' => 800, 'height' => 600, 'file_size' => 5000,
        'is_current' => false,
    ]);
    $this->artwork->update(['hash' => 'differenthash123', 'version_count' => 2]);

    $response = $this->postJson("/api/studio/artworks/{$this->artwork->id}/restore/{$old->id}");
    $response->assertOk()->assertJsonPath('success', true);

    expect(ArtworkVersion::where('artwork_id', $this->artwork->id)
        ->where('file_hash', 'restorehash')
        ->where('is_current', true)
        ->exists()
    )->toBeTrue();
});

test('POST restore rejects attempt to restore already-current version', function () {
    $this->actingAs($this->user);

    $current = ArtworkVersion::create([
        'artwork_id' => $this->artwork->id, 'version_number' => 1,
        'file_path' => 'x.webp', 'file_hash' => 'aaa',
        'is_current' => true,
    ]);

    $this->postJson("/api/studio/artworks/{$this->artwork->id}/restore/{$current->id}")
        ->assertStatus(422)
        ->assertJsonPath('success', false);
});
