<?php

declare(strict_types=1);

use App\Models\Artwork;
use App\Models\Post;
use App\Models\PostReaction;
use App\Models\User;
use App\Services\Posts\PostFeedService;
use App\Services\Posts\PostShareService;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

// ── SQLite polyfill + helpers ──────────────────────────────────────────────────

function postTestArtwork(array $attrs = []): Artwork
{
    return Artwork::withoutEvents(fn () => Artwork::factory()->create([
        'is_public'   => true,
        'is_approved' => true,
        ...$attrs,
    ]));
}

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

// ── Post visibility scopes ─────────────────────────────────────────────────────

test('public post is visible to everyone', function () {
    $author = User::factory()->create();
    $post   = Post::factory()->create(['user_id' => $author->id, 'visibility' => 'public']);

    expect(Post::visibleTo(null)->find($post->id))->not->toBeNull();
    expect(Post::visibleTo(999)->find($post->id))->not->toBeNull();
});

test('followers-only post is hidden from non-followers', function () {
    $author  = User::factory()->create();
    $stranger = User::factory()->create();
    $post    = Post::factory()->create(['user_id' => $author->id, 'visibility' => 'followers']);

    expect(Post::visibleTo($stranger->id)->find($post->id))->toBeNull();
});

test('followers-only post is visible to followers', function () {
    $author    = User::factory()->create();
    $follower  = User::factory()->create();
    DB::table('user_followers')->insert([
        'user_id'    => $author->id,
        'follower_id' => $follower->id,
        'created_at' => now(),
    ]);
    $post = Post::factory()->create(['user_id' => $author->id, 'visibility' => 'followers']);

    expect(Post::visibleTo($follower->id)->find($post->id))->not->toBeNull();
});

test('private post is only visible to the author', function () {
    $author  = User::factory()->create();
    $other   = User::factory()->create();
    $post    = Post::factory()->create(['user_id' => $author->id, 'visibility' => 'private']);

    expect(Post::visibleTo(null)->find($post->id))->toBeNull();
    expect(Post::visibleTo($other->id)->find($post->id))->toBeNull();
    expect(Post::visibleTo($author->id)->find($post->id))->not->toBeNull();
});

// ── PostShareService ───────────────────────────────────────────────────────────

test('shareArtwork creates a post with a target', function () {
    $user    = User::factory()->create();
    $artwork = postTestArtwork(['user_id' => $user->id]);

    $service = app(PostShareService::class);
    $post    = $service->shareArtwork($user, $artwork, 'Cool piece!', 'public');

    expect($post)->toBeInstanceOf(Post::class)
        ->and($post->type)->toBe('artwork_share')
        ->and($post->body)->toBe('<p>Cool piece!</p>')
        ->and($post->targets()->where('target_type', 'artwork')->where('target_id', $artwork->id)->exists())->toBeTrue();
});

test('shareArtwork throws when sharing same artwork within 24 hours', function () {
    $user    = User::factory()->create();
    $artwork = postTestArtwork(['user_id' => $user->id]);

    $service = app(PostShareService::class);
    $service->shareArtwork($user, $artwork, '', 'public');

    expect(fn () => $service->shareArtwork($user, $artwork, 'Again', 'public'))
        ->toThrow(ValidationException::class);
});

test('shareArtwork rejects private or unapproved artwork', function () {
    $user    = User::factory()->create();
    $private = postTestArtwork(['user_id' => $user->id, 'is_public' => false, 'is_approved' => true]);

    $service = app(PostShareService::class);
    expect(fn () => $service->shareArtwork($user, $private, '', 'public'))
        ->toThrow(ValidationException::class);
});

// ── Profile feed API ───────────────────────────────────────────────────────────

test('GET /api/posts/profile/{username} returns paginated public posts', function () {
    $author = User::factory()->create();
    Post::factory()->count(3)->create(['user_id' => $author->id, 'visibility' => 'public']);
    Post::factory()->create(['user_id' => $author->id, 'visibility' => 'private']);

    $response = $this->getJson("/api/posts/profile/{$author->username}");
    $response->assertOk()->assertJsonCount(3, 'data');
});

test('GET /api/posts/profile/{username} hides followers-only posts from strangers', function () {
    $author  = User::factory()->create();
    $viewer  = User::factory()->create();
    Post::factory()->count(2)->create(['user_id' => $author->id, 'visibility' => 'followers']);
    Post::factory()->count(1)->create(['user_id' => $author->id, 'visibility' => 'public']);

    $response = $this->actingAs($viewer)->getJson("/api/posts/profile/{$author->username}");
    $response->assertOk()->assertJsonCount(1, 'data');
});

// ── Following feed API ─────────────────────────────────────────────────────────

test('GET /api/posts/following requires authentication', function () {
    $this->getJson('/api/posts/following')->assertUnauthorized();
});

test('GET /api/posts/following returns posts from followed users only', function () {
    $viewer  = User::factory()->create();
    $followed = User::factory()->create();
    $stranger = User::factory()->create();

    DB::table('user_followers')->insert([
        'user_id'    => $followed->id,
        'follower_id' => $viewer->id,
        'created_at' => now(),
    ]);

    Post::factory()->create(['user_id' => $followed->id, 'visibility' => 'public']);
    Post::factory()->create(['user_id' => $stranger->id, 'visibility' => 'public']);

    $response = $this->actingAs($viewer)->getJson('/api/posts/following');
    $response->assertOk();

    $ids = collect($response->json('data'))->pluck('author.id');
    expect($ids)->each->toBe($followed->id);
});

// ── Reactions ─────────────────────────────────────────────────────────────────

test('POST /api/posts/{id}/reactions adds reaction and increments counter', function () {
    $user = User::factory()->create();
    $post = Post::factory()->create(['user_id' => $user->id, 'visibility' => 'public']);

    $this->actingAs($user)
        ->postJson("/api/posts/{$post->id}/reactions", ['reaction' => 'like'])
        ->assertStatus(201);

    expect($post->fresh()->reactions_count)->toBe(1);
    expect(PostReaction::where(['post_id' => $post->id, 'user_id' => $user->id])->exists())->toBeTrue();
});

test('DELETE /api/posts/{id}/reactions/{reaction} removes reaction and decrements counter', function () {
    $user = User::factory()->create();
    $post = Post::factory()->create(['user_id' => $user->id, 'visibility' => 'public', 'reactions_count' => 1]);
    PostReaction::create(['post_id' => $post->id, 'user_id' => $user->id, 'reaction' => 'like']);

    $this->actingAs($user)
        ->deleteJson("/api/posts/{$post->id}/reactions/like")
        ->assertOk();

    expect($post->fresh()->reactions_count)->toBe(0);
    expect(PostReaction::where(['post_id' => $post->id, 'user_id' => $user->id])->exists())->toBeFalse();
});

// ── Comments ──────────────────────────────────────────────────────────────────

test('POST /api/posts/{id}/comments creates comment and increments counter', function () {
    $user = User::factory()->create();
    $post = Post::factory()->create(['user_id' => $user->id, 'visibility' => 'public']);

    $this->actingAs($user)
        ->postJson("/api/posts/{$post->id}/comments", ['body' => 'Nice post!'])
        ->assertCreated()
        ->assertJsonPath('comment.body', '<p>Nice post!</p>');

    expect($post->fresh()->comments_count)->toBe(1);
});

test('GET /api/posts/{id}/comments is publicly accessible', function () {
    $user = User::factory()->create();
    $post = Post::factory()->create(['user_id' => $user->id, 'visibility' => 'public']);

    $this->getJson("/api/posts/{$post->id}/comments")
        ->assertOk()
        ->assertJsonStructure(['data', 'meta']);
});

// ── Diversity pass ─────────────────────────────────────────────────────────────

test('PostFeedService diversity pass limits consecutive posts from same author', function () {
    $service = app(PostFeedService::class);

    $userA = User::factory()->create();
    $userB = User::factory()->create();

    // 8 posts from A then 2 from B
    $posts = collect();
    for ($i = 0; $i < 8; $i++) {
        $posts->push(Post::factory()->make(['user_id' => $userA->id]));
    }
    for ($i = 0; $i < 2; $i++) {
        $posts->push(Post::factory()->make(['user_id' => $userB->id]));
    }

    $diversified = $service->applyDiversityPass($posts);

    // After the 5th consecutive post from A, subsequent A posts should be deferred
    $runLength = 0;
    $maxRun    = 0;
    $lastId    = null;
    foreach ($diversified as $post) {
        if ($post->user_id === $lastId) {
            $runLength++;
        } else {
            $runLength = 1;
            $lastId    = $post->user_id;
        }
        $maxRun = max($maxRun, $runLength);
    }

    expect($maxRun)->toBeLessThanOrEqual(5);
});
