<?php

declare(strict_types=1);

use App\Models\NovaCard;
use App\Models\NovaCardCategory;
use App\Models\NovaCardChallenge;
use App\Models\NovaCardComment;
use App\Models\NovaCardCollection;
use App\Models\NovaCardTemplate;
use App\Models\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

function v2Category(array $attributes = []): NovaCardCategory
{
    return NovaCardCategory::query()->create(array_merge([
        'slug' => 'v2-category-' . Str::lower(Str::random(6)),
        'name' => 'V2 Category',
        'description' => 'Nova Cards v2 category',
        'active' => true,
        'order_num' => 0,
    ], $attributes));
}

function v2Template(array $attributes = []): NovaCardTemplate
{
    return NovaCardTemplate::query()->create(array_merge([
        'slug' => 'v2-template-' . Str::lower(Str::random(6)),
        'name' => 'V2 Template',
        'description' => 'Nova Cards v2 template',
        'config_json' => [
            'font_preset' => 'modern-sans',
            'gradient_preset' => 'midnight-nova',
            'layout' => 'quote_heavy',
            'text_align' => 'center',
            'text_color' => '#ffffff',
            'overlay_style' => 'dark-soft',
        ],
        'supported_formats' => ['square', 'portrait'],
        'active' => true,
        'official' => true,
        'order_num' => 0,
    ], $attributes));
}

function v2PublishedCard(User $user, array $attributes = []): NovaCard
{
    $category = $attributes['category'] ?? v2Category();
    $template = $attributes['template'] ?? v2Template();

    return NovaCard::query()->create(array_merge([
        'user_id' => $user->id,
        'category_id' => $category->id,
        'template_id' => $template->id,
        'title' => 'V2 Public Card',
        'slug' => 'v2-public-card-' . Str::lower(Str::random(5)),
        'quote_text' => 'A card built for Nova Cards v2 tests.',
        'quote_author' => 'Nova V2',
        'quote_source' => 'Feature suite',
        'description' => 'Published Nova Cards v2 test card.',
        'format' => NovaCard::FORMAT_SQUARE,
        'project_json' => [
            'schema_version' => 2,
            'content' => [
                'title' => 'V2 Public Card',
                'quote_text' => 'A card built for Nova Cards v2 tests.',
                'quote_author' => 'Nova V2',
                'quote_source' => 'Feature suite',
            ],
            'text_blocks' => [
                ['key' => 'title', 'type' => 'title', 'text' => 'V2 Public Card', 'enabled' => true],
                ['key' => 'quote', 'type' => 'quote', 'text' => 'A card built for Nova Cards v2 tests.', 'enabled' => true],
                ['key' => 'author', 'type' => 'author', 'text' => 'Nova V2', 'enabled' => true],
            ],
            'layout' => ['layout' => 'quote_heavy', 'position' => 'center', 'alignment' => 'center', 'padding' => 'comfortable', 'max_width' => 'balanced'],
            'typography' => ['font_preset' => 'modern-sans', 'text_color' => '#ffffff', 'accent_color' => '#e0f2fe', 'quote_size' => 72, 'author_size' => 28, 'letter_spacing' => 0, 'line_height' => 1.2, 'shadow_preset' => 'soft'],
            'background' => ['type' => 'gradient', 'gradient_preset' => 'midnight-nova', 'gradient_colors' => ['#0f172a', '#1d4ed8'], 'overlay_style' => 'dark-soft', 'focal_position' => 'center', 'blur_level' => 0, 'opacity' => 50],
            'decorations' => [],
            'assets' => ['pack_ids' => [], 'template_pack_ids' => [], 'items' => []],
        ],
        'schema_version' => 2,
        'render_version' => 2,
        'background_type' => 'gradient',
        'visibility' => NovaCard::VISIBILITY_PUBLIC,
        'status' => NovaCard::STATUS_PUBLISHED,
        'moderation_status' => NovaCard::MOD_APPROVED,
        'allow_download' => true,
        'allow_remix' => true,
        'published_at' => now()->subHour(),
    ], Arr::except($attributes, ['category', 'template'])));
}

it('supports likes favorites saves and remix lineage for published cards', function (): void {
    $creator = User::factory()->create();
    $viewer = User::factory()->create();
    $card = v2PublishedCard($creator, ['slug' => 'remix-source-card']);

    $this->actingAs($viewer)
        ->postJson(route('api.cards.like', ['id' => $card->id]))
        ->assertOk()
        ->assertJsonPath('liked', true);

    $this->actingAs($viewer)
        ->postJson(route('api.cards.favorite', ['id' => $card->id]))
        ->assertOk()
        ->assertJsonPath('favorited', true);

    $this->actingAs($viewer)
        ->postJson(route('api.cards.save', ['id' => $card->id]))
        ->assertOk()
        ->assertJsonPath('ok', true);

    $remix = $this->actingAs($viewer)
        ->postJson(route('api.cards.remix', ['id' => $card->id]))
        ->assertCreated()
        ->assertJsonPath('data.lineage.original_card_id', $card->id);

    $remixedCard = NovaCard::query()->findOrFail((int) $remix->json('data.id'));

    expect($card->fresh()->likes_count)->toBe(1)
        ->and($card->fresh()->favorites_count)->toBe(1)
        ->and($card->fresh()->saves_count)->toBe(1)
        ->and($card->fresh()->remixes_count)->toBe(1)
        ->and($remixedCard->original_card_id)->toBe($card->id)
        ->and(NovaCardCollection::query()->where('user_id', $viewer->id)->exists())->toBeTrue();
});

it('returns lineage data for a published card via the api', function (): void {
    $creator = User::factory()->create();
    $root = v2PublishedCard($creator, ['slug' => 'api-lineage-root', 'title' => 'API Lineage Root']);
    $remix = v2PublishedCard($creator, [
        'slug' => 'api-lineage-remix',
        'title' => 'API Lineage Remix',
        'original_card_id' => $root->id,
        'root_card_id' => $root->id,
    ]);

    $this->getJson(route('api.cards.lineage', ['id' => $remix->id]))
        ->assertOk()
        ->assertJsonPath('data.root_card.id', $root->id)
        ->assertJsonPath('data.trail.0.id', $root->id)
        ->assertJsonPath('data.card.id', $remix->id);
});

it('supports the literal spec-style api aliases for v2 card flows', function (): void {
    $creator = User::factory()->create();
    $viewer = User::factory()->create();
    $card = v2PublishedCard($creator, ['slug' => 'alias-source-card', 'title' => 'Alias Source']);
    $challenge = NovaCardChallenge::query()->create([
        'slug' => 'alias-challenge',
        'title' => 'Alias Challenge',
        'status' => NovaCardChallenge::STATUS_ACTIVE,
        'official' => true,
    ]);

    $this->actingAs($viewer)
        ->postJson('/api/cards/' . $card->id . '/like')
        ->assertOk()
        ->assertJsonPath('liked', true);

    $this->actingAs($viewer)
        ->postJson('/api/cards/' . $card->id . '/save')
        ->assertOk()
        ->assertJsonPath('ok', true);

    $this->actingAs($viewer)
        ->postJson('/api/cards/' . $card->id . '/remix')
        ->assertCreated();

    $owned = v2PublishedCard($viewer, ['slug' => 'alias-owned-card', 'title' => 'Alias Owned']);

    $this->actingAs($viewer)
        ->postJson('/api/cards/challenges/' . $challenge->id . '/submit', ['card_id' => $owned->id])
        ->assertOk()
        ->assertJsonPath('entry.challenge_id', $challenge->id);

    $this->getJson('/api/cards/' . $card->id . '/lineage')
        ->assertOk();
});

it('supports collection metadata and item management via the v2 api collection endpoints', function (): void {
    $user = User::factory()->create();
    $card = v2PublishedCard(User::factory()->create(), ['slug' => 'collection-endpoint-card']);

    $create = $this->actingAs($user)
        ->postJson(route('api.cards.collections.store'), [
            'name' => 'My Picks',
            'visibility' => 'public',
        ])
        ->assertCreated();

    $collectionId = (int) $create->json('collection.id');

    $this->actingAs($user)
        ->patchJson('/api/cards/collections/' . $collectionId, [
            'name' => 'Updated Picks',
            'slug' => 'updated-picks',
            'visibility' => 'public',
        ])
        ->assertOk()
        ->assertJsonPath('collection.slug', 'updated-picks');

    $this->actingAs($user)
        ->postJson('/api/cards/collections/' . $collectionId . '/items', [
            'card_id' => $card->id,
            'note' => 'Pinned',
        ])
        ->assertCreated()
        ->assertJsonPath('collection.items.0.card.id', $card->id);

    $this->actingAs($user)
        ->deleteJson('/api/cards/collections/' . $collectionId . '/items/' . $card->id)
        ->assertOk();
});

it('lists challenge, asset, and template resources through the v2 api', function (): void {
    $user = User::factory()->create();
    NovaCardChallenge::query()->create([
        'slug' => 'resource-check',
        'title' => 'Resource Check',
        'status' => NovaCardChallenge::STATUS_ACTIVE,
        'official' => true,
    ]);

    $this->actingAs($user)->getJson('/api/cards/challenges')->assertOk();
    $this->actingAs($user)->getJson('/api/cards/assets')->assertOk();
    $this->actingAs($user)->getJson('/api/cards/templates')->assertOk();
});

it('recomputes comments_count when card comments change', function (): void {
    $creator = User::factory()->create();
    $commenter = User::factory()->create();
    $card = v2PublishedCard($creator, ['slug' => 'comment-counter-card']);

    NovaCardComment::query()->create([
        'card_id' => $card->id,
        'user_id' => $commenter->id,
        'body' => 'First comment',
        'rendered_body' => 'First comment',
        'status' => 'visible',
    ]);

    app(\App\Services\NovaCards\NovaCardTrendingService::class)->refreshCard($card->fresh());

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

it('upgrades legacy v1 project json into the v2 editor shape when editing', function (): void {
    $user = User::factory()->create();
    $category = v2Category();
    $template = v2Template();
    $card = NovaCard::query()->create([
        'user_id' => $user->id,
        'category_id' => $category->id,
        'template_id' => $template->id,
        'title' => 'Legacy Card',
        'slug' => 'legacy-card',
        'quote_text' => 'Legacy quote',
        'format' => NovaCard::FORMAT_SQUARE,
        'project_json' => [
            'schema_version' => 1,
            'content' => [
                'title' => 'Legacy Card',
                'quote_text' => 'Legacy quote',
            ],
            'background' => [
                'type' => 'gradient',
                'gradient_preset' => 'midnight-nova',
            ],
        ],
        'schema_version' => 1,
        'background_type' => 'gradient',
        'visibility' => NovaCard::VISIBILITY_PRIVATE,
        'status' => NovaCard::STATUS_DRAFT,
        'moderation_status' => NovaCard::MOD_PENDING,
        'allow_download' => true,
    ]);

    $response = $this->actingAs($user)
        ->patchJson(route('api.cards.drafts.update', ['id' => $card->id]), [
            'quote_author' => 'Legacy Author',
        ])
        ->assertOk();

    expect($response->json('data.schema_version'))->toBe(2)
        ->and($response->json('data.project_json.text_blocks.0.type'))->toBe('title')
        ->and(NovaCard::query()->findOrFail($card->id)->schema_version)->toBe(2);
});

it('duplicates an owned card into a fresh draft without remix lineage', function (): void {
    $user = User::factory()->create();
    $card = v2PublishedCard($user, ['slug' => 'duplicate-source-card', 'title' => 'Duplicate Source']);

    $response = $this->actingAs($user)
        ->postJson(route('api.cards.duplicate', ['id' => $card->id]))
        ->assertCreated();

    $duplicate = NovaCard::query()->findOrFail((int) $response->json('data.id'));

    expect($duplicate->user_id)->toBe($user->id)
        ->and($duplicate->title)->toBe('Copy of Duplicate Source')
        ->and($duplicate->status)->toBe(NovaCard::STATUS_DRAFT)
        ->and($duplicate->original_card_id)->toBeNull()
        ->and($duplicate->root_card_id)->toBeNull();
});

it('restores an earlier draft version through the v2 versions api', function (): void {
    $user = User::factory()->create();
    $category = v2Category();
    $template = v2Template();

    $create = $this->actingAs($user)->postJson(route('api.cards.drafts.store'), [
        'title' => 'Versioned Card',
        'quote_text' => 'First version.',
        'category_id' => $category->id,
        'template_id' => $template->id,
    ])->assertCreated();

    $cardId = (int) $create->json('data.id');

    $this->actingAs($user)->postJson(route('api.cards.drafts.autosave', ['id' => $cardId]), [
        'quote_text' => 'Second version.',
    ])->assertOk();

    $versions = $this->actingAs($user)
        ->getJson(route('api.cards.drafts.versions', ['id' => $cardId]))
        ->assertOk();

    $firstVersionId = (int) collect($versions->json('data'))->last()['id'];

    $this->actingAs($user)
        ->postJson(route('api.cards.drafts.restore', ['id' => $cardId, 'versionId' => $firstVersionId]))
        ->assertOk()
        ->assertJsonPath('data.quote_text', 'First version.');

    expect(NovaCard::query()->findOrFail($cardId)->quote_text)->toBe('First version.');
});

it('submits a published card to a challenge and renders the new public v2 routes', function (): void {
    $user = User::factory()->create(['username' => 'v2creator']);
    $card = v2PublishedCard($user, ['slug' => 'challenge-entry-card']);
    $challenge = NovaCardChallenge::query()->create([
        'slug' => 'weekly-clarity',
        'title' => 'Weekly Clarity',
        'description' => 'Make a clean editorial card about clarity.',
        'prompt' => 'Design a card that turns one strong sentence into an editorial object.',
        'status' => NovaCardChallenge::STATUS_ACTIVE,
        'official' => true,
        'featured' => true,
        'starts_at' => now()->subDay(),
        'ends_at' => now()->addWeek(),
    ]);

    $this->actingAs($user)
        ->postJson(route('api.cards.challenges.submit', ['challengeId' => $challenge->id, 'id' => $card->id]))
        ->assertOk()
        ->assertJsonPath('entry.challenge_id', $challenge->id);

    expect($card->fresh()->challenge_entries_count)->toBe(1);

    $this->get(route('cards.popular'))->assertOk();
    $this->get(route('cards.remixed'))->assertOk();
    $this->get(route('cards.challenges'))->assertOk()->assertSee('Weekly Clarity');
    $this->get(route('cards.challenges.show', ['slug' => $challenge->slug]))->assertOk()->assertSee($card->title);
    $this->get(route('cards.templates'))->assertOk();
    $this->get(route('cards.assets'))->assertOk();
});