<?php

declare(strict_types=1);

use App\Models\Artwork;
use App\Models\Group;
use App\Models\GroupChallenge;
use App\Models\User;
use App\Models\World;
use App\Models\WorldSubmission;
use App\Services\GroupChallengeService;
use App\Services\Worlds\WorldService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;

uses(RefreshDatabase::class);

function challengeLinkedWorld(?User $moderator = null, array $attributes = []): World
{
    $moderator ??= User::factory()->create([
        'role' => 'moderator',
        'username' => 'worldchallenge-' . Str::lower(Str::random(6)),
    ]);

    return World::factory()->create(array_merge([
        'created_by_user_id' => $moderator->id,
        'status' => World::STATUS_PUBLISHED,
        'published_at' => now()->subDay(),
        'accepts_submissions' => true,
        'participation_mode' => World::PARTICIPATION_MODE_MANUAL_APPROVAL,
        'submission_note_enabled' => true,
        'community_section_enabled' => true,
        'allow_readd_after_removal' => true,
        'submission_starts_at' => now()->subDay(),
        'submission_ends_at' => now()->addDays(7),
    ], $attributes));
}

function worldUpdatePayload(World $world, array $overrides = []): array
{
    return array_merge([
        'title' => $world->title,
        'status' => $world->status,
        'type' => $world->type,
        'tagline' => $world->tagline,
        'summary' => $world->summary,
        'description' => $world->description,
        'accepts_submissions' => (bool) $world->accepts_submissions,
        'participation_mode' => $world->participation_mode,
        'submission_note_enabled' => (bool) $world->submission_note_enabled,
        'community_section_enabled' => (bool) $world->community_section_enabled,
        'allow_readd_after_removal' => (bool) $world->allow_readd_after_removal,
        'is_featured' => (bool) $world->is_featured,
        'is_active_campaign' => (bool) $world->is_active_campaign,
        'is_homepage_featured' => (bool) $world->is_homepage_featured,
        'is_recurring' => (bool) $world->is_recurring,
        'cta_label' => $world->cta_label,
        'cta_url' => $world->cta_url,
        'badge_label' => $world->badge_label,
        'badge_description' => $world->badge_description,
        'badge_url' => $world->badge_url,
        'linked_challenge_id' => $world->linked_challenge_id,
        'show_linked_challenge_section' => (bool) ($world->show_linked_challenge_section ?? true),
        'show_linked_challenge_entries' => (bool) ($world->show_linked_challenge_entries ?? true),
        'show_linked_challenge_winners' => (bool) ($world->show_linked_challenge_winners ?? true),
        'show_linked_challenge_finalists' => (bool) ($world->show_linked_challenge_finalists ?? true),
        'auto_grant_challenge_world_rewards' => (bool) ($world->auto_grant_challenge_world_rewards ?? true),
        'challenge_teaser_override' => $world->challenge_teaser_override,
        'relations' => [],
    ], $overrides);
}

function linkedGroupChallenge(Group $group, User $owner, array $attributes = []): GroupChallenge
{
    return GroupChallenge::query()->create(array_merge([
        'group_id' => $group->id,
        'title' => 'Pixel Week Finals',
        'slug' => 'pixel-week-finals-' . Str::lower(Str::random(6)),
        'summary' => 'Challenge finale.',
        'description' => 'Challenge finale description.',
        'visibility' => GroupChallenge::VISIBILITY_PUBLIC,
        'participation_scope' => GroupChallenge::PARTICIPATION_PUBLIC,
        'status' => GroupChallenge::STATUS_ACTIVE,
        'start_at' => now()->subDay(),
        'end_at' => now()->addDay(),
        'created_by_user_id' => $owner->id,
        'featured_artwork_id' => null,
    ], $attributes));
}

it('syncs winner rewards from linked challenge outcomes', function (): void {
    $moderator = User::factory()->create(['role' => 'moderator']);
    $creator = User::factory()->create();
    $groupOwner = User::factory()->create();
    $group = Group::factory()->for($groupOwner, 'owner')->create();
    $world = challengeLinkedWorld($moderator);
    $artwork = Artwork::factory()->for($creator)->create([
        'group_id' => $group->id,
        'title' => 'Challenge Winner Artwork',
        'slug' => 'challenge-winner-artwork',
        'artwork_status' => 'published',
        'published_at' => now()->subDay(),
        'is_public' => true,
        'visibility' => Artwork::VISIBILITY_PUBLIC,
    ]);

    $submission = WorldSubmission::query()->create([
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'submitted_by_user_id' => $creator->id,
        'status' => WorldSubmission::STATUS_LIVE,
        'reviewed_by_user_id' => $moderator->id,
        'reviewed_at' => now()->subHour(),
    ]);

    $challenge = linkedGroupChallenge($group, $groupOwner);
    $challenge->artworks()->attach($artwork->id, ['submitted_by_user_id' => $groupOwner->id, 'sort_order' => 0]);

    $world->worldRelations()->create([
        'section_key' => 'related_programming',
        'related_type' => 'challenge',
        'related_id' => $challenge->id,
        'context_label' => 'Challenge finale',
        'sort_order' => 0,
        'is_featured' => true,
    ]);

    app(GroupChallengeService::class)->update($challenge, $groupOwner, [
        'outcomes' => [[
            'artwork_id' => $artwork->id,
            'outcome_type' => 'winner',
            'position' => 1,
            'sort_order' => 0,
            'title_override' => 'Grand Winner',
        ]],
    ]);

    $this->assertDatabaseHas('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'world_submission_id' => $submission->id,
        'reward_type' => 'winner',
        'grant_source' => 'challenge',
    ]);
});

it('syncs finalist rewards from linked challenge outcomes', function (): void {
    $moderator = User::factory()->create(['role' => 'moderator']);
    $creator = User::factory()->create();
    $groupOwner = User::factory()->create();
    $group = Group::factory()->for($groupOwner, 'owner')->create();
    $world = challengeLinkedWorld($moderator);
    $artwork = Artwork::factory()->for($creator)->create([
        'group_id' => $group->id,
        'title' => 'Challenge Finalist Artwork',
        'slug' => 'challenge-finalist-artwork',
        'artwork_status' => 'published',
        'published_at' => now()->subDay(),
        'is_public' => true,
        'visibility' => Artwork::VISIBILITY_PUBLIC,
    ]);

    $submission = WorldSubmission::query()->create([
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'submitted_by_user_id' => $creator->id,
        'status' => WorldSubmission::STATUS_LIVE,
        'reviewed_by_user_id' => $moderator->id,
        'reviewed_at' => now()->subHour(),
    ]);

    $challenge = linkedGroupChallenge($group, $groupOwner);
    $challenge->artworks()->attach($artwork->id, ['submitted_by_user_id' => $groupOwner->id, 'sort_order' => 0]);

    $world->worldRelations()->create([
        'section_key' => 'related_programming',
        'related_type' => 'challenge',
        'related_id' => $challenge->id,
        'context_label' => 'Challenge finale',
        'sort_order' => 0,
        'is_featured' => true,
    ]);

    app(GroupChallengeService::class)->update($challenge, $groupOwner, [
        'outcomes' => [[
            'artwork_id' => $artwork->id,
            'outcome_type' => 'finalist',
            'sort_order' => 0,
            'note' => 'Finalist award.',
        ]],
    ]);

    $this->assertDatabaseHas('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'world_submission_id' => $submission->id,
        'reward_type' => 'finalist',
        'grant_source' => 'challenge',
    ]);
});

it('syncs challenge winner rewards when challenge relations are added to a world', function (): void {
    $moderator = User::factory()->create(['role' => 'moderator']);
    $creator = User::factory()->create();
    $groupOwner = User::factory()->create();
    $group = Group::factory()->for($groupOwner, 'owner')->create();
    $world = challengeLinkedWorld($moderator);
    $artwork = Artwork::factory()->for($creator)->create([
        'group_id' => $group->id,
        'title' => 'Relation Sync Artwork',
        'slug' => 'relation-sync-artwork',
        'artwork_status' => 'published',
        'published_at' => now()->subDay(),
        'is_public' => true,
        'visibility' => Artwork::VISIBILITY_PUBLIC,
    ]);

    WorldSubmission::query()->create([
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'submitted_by_user_id' => $creator->id,
        'status' => WorldSubmission::STATUS_LIVE,
        'reviewed_by_user_id' => $moderator->id,
        'reviewed_at' => now()->subHour(),
    ]);

    $challenge = linkedGroupChallenge($group, $groupOwner, [
        'featured_artwork_id' => $artwork->id,
    ]);

    app(WorldService::class)->update($world, $moderator, worldUpdatePayload($world, [
        'relations' => [[
            'section_key' => 'related_programming',
            'related_type' => 'challenge',
            'related_id' => $challenge->id,
            'context_label' => 'Challenge finale',
            'sort_order' => 0,
            'is_featured' => true,
        ]],
    ]));

    $this->assertDatabaseHas('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'reward_type' => 'winner',
        'grant_source' => 'challenge',
    ]);
});

it('syncs challenge winner rewards when a primary linked challenge is set on a world', function (): void {
    $moderator = User::factory()->create(['role' => 'moderator']);
    $creator = User::factory()->create();
    $groupOwner = User::factory()->create();
    $group = Group::factory()->for($groupOwner, 'owner')->create();
    $world = challengeLinkedWorld($moderator);
    $artwork = Artwork::factory()->for($creator)->create([
        'group_id' => $group->id,
        'title' => 'Primary Challenge Sync Artwork',
        'slug' => 'primary-challenge-sync-artwork',
        'artwork_status' => 'published',
        'published_at' => now()->subDay(),
        'is_public' => true,
        'visibility' => Artwork::VISIBILITY_PUBLIC,
    ]);

    WorldSubmission::query()->create([
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'submitted_by_user_id' => $creator->id,
        'status' => WorldSubmission::STATUS_LIVE,
        'reviewed_by_user_id' => $moderator->id,
        'reviewed_at' => now()->subHour(),
    ]);

    $challenge = linkedGroupChallenge($group, $groupOwner, [
        'featured_artwork_id' => $artwork->id,
    ]);

    app(WorldService::class)->update($world, $moderator, worldUpdatePayload($world, [
        'linked_challenge_id' => $challenge->id,
    ]));

    $this->assertDatabaseHas('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'reward_type' => 'winner',
        'grant_source' => 'challenge',
    ]);
});

it('revokes challenge-sourced winner rewards when linked challenge winners are cleared', function (): void {
    $moderator = User::factory()->create(['role' => 'moderator']);
    $creator = User::factory()->create();
    $groupOwner = User::factory()->create();
    $group = Group::factory()->for($groupOwner, 'owner')->create();
    $world = challengeLinkedWorld($moderator);
    $artwork = Artwork::factory()->for($creator)->create([
        'group_id' => $group->id,
        'title' => 'Revoked Challenge Winner',
        'slug' => 'revoked-challenge-winner',
        'artwork_status' => 'published',
        'published_at' => now()->subDay(),
        'is_public' => true,
        'visibility' => Artwork::VISIBILITY_PUBLIC,
    ]);

    $submission = WorldSubmission::query()->create([
        'world_id' => $world->id,
        'artwork_id' => $artwork->id,
        'submitted_by_user_id' => $creator->id,
        'status' => WorldSubmission::STATUS_LIVE,
        'reviewed_by_user_id' => $moderator->id,
        'reviewed_at' => now()->subHour(),
    ]);

    $challenge = linkedGroupChallenge($group, $groupOwner);

    $world->worldRelations()->create([
        'section_key' => 'related_programming',
        'related_type' => 'challenge',
        'related_id' => $challenge->id,
        'context_label' => 'Challenge finale',
        'sort_order' => 0,
        'is_featured' => true,
    ]);

    $challengeService = app(GroupChallengeService::class);
    $challengeService->update($challenge, $groupOwner, ['featured_artwork_id' => $artwork->id]);

    $this->assertDatabaseHas('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'world_submission_id' => $submission->id,
        'reward_type' => 'winner',
        'grant_source' => 'challenge',
    ]);

    $challengeService->update($challenge->fresh(), $groupOwner, ['featured_artwork_id' => null]);

    $this->assertDatabaseMissing('world_reward_grants', [
        'user_id' => $creator->id,
        'world_id' => $world->id,
        'reward_type' => 'winner',
        'grant_source' => 'challenge',
    ]);
});