<?php

namespace Tests\Feature;

use App\Livewire\AssessmentRunner;
use App\Models\Assessment;
use App\Models\AssessmentAttempt;
use App\Models\Lead;
use App\Models\User;
use Database\Seeders\FinancialHealthSeeder;
use Database\Seeders\RolesAndAdminSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

class AssessmentFlowTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        $this->seed(RolesAndAdminSeeder::class);
        $this->seed(FinancialHealthSeeder::class);
    }

    /** Build an answers map choosing the option at $index for every question. */
    private function answersAt(Assessment $assessment, int $index): array
    {
        return $assessment->questions->mapWithKeys(function ($q) use ($index) {
            $options = $q->options;
            $option = $options[min($index, count($options) - 1)];

            return [$q->id => $option['key']];
        })->all();
    }

    public function test_assessment_page_renders(): void
    {
        $this->get('/assessments/financial-health')
            ->assertOk()
            ->assertSee('Financial Health Check');
    }

    public function test_guest_completes_assessment_and_lead_is_captured(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();
        $answers = $this->answersAt($assessment, 0); // first option = max points

        $component = Livewire::test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $answers, 'Wanjiku Test', 'wanjiku@example.com');

        $attempt = AssessmentAttempt::firstOrFail();

        $this->assertSame('completed', $attempt->status);
        $this->assertSame(100.0, (float) $attempt->percent);
        $this->assertNotNull($attempt->band_id);
        $this->assertCount(20, $attempt->answers);
        $this->assertSame('The Money-Wise', $attempt->band->label);

        $this->assertDatabaseHas('leads', [
            'email' => 'wanjiku@example.com',
            'source' => 'assessment:financial-health',
        ]);

        $component->assertRedirect(route('assessments.results', [
            'assessment' => 'financial-health',
            'attempt' => $attempt->uuid,
        ]));
    }

    public function test_logged_in_user_attempt_is_linked_without_lead(): void
    {
        $user = User::factory()->create();
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();
        $answers = $this->answersAt($assessment, 99); // last option = lowest points

        Livewire::actingAs($user)
            ->test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $answers, null, null);

        $attempt = AssessmentAttempt::firstOrFail();

        $this->assertSame($user->id, $attempt->user_id);
        $this->assertSame('completed', $attempt->status);
        $this->assertSame(0, Lead::count());
    }

    public function test_invalid_option_keys_are_ignored(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();
        $answers = $this->answersAt($assessment, 0);

        // Corrupt one answer with a non-existent option key
        $firstId = array_key_first($answers);
        $answers[$firstId] = 'zz';

        Livewire::test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $answers, 'Tester', 'invalid-opt@example.com');

        $attempt = AssessmentAttempt::firstOrFail();

        $this->assertCount(19, $attempt->answers);
    }

    public function test_guest_without_email_cannot_unlock_score(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();

        Livewire::test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $this->answersAt($assessment, 0), 'NoEmail', null)
            ->assertStatus(422);

        $this->assertSame(0, AssessmentAttempt::count());
    }

    public function test_collected_email_is_added_to_newsletter(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();

        Livewire::test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $this->answersAt($assessment, 0), 'Subscriber', 'sub@example.com');

        $this->assertDatabaseHas('leads', ['email' => 'sub@example.com', 'source' => 'newsletter']);
        $this->assertDatabaseHas('leads', ['email' => 'sub@example.com', 'source' => 'assessment:financial-health']);
    }

    public function test_results_page_renders_band_and_score(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();

        Livewire::test(AssessmentRunner::class, ['assessment' => $assessment])
            ->call('submit', $this->answersAt($assessment, 0), 'Tester', 'results@example.com');

        $attempt = AssessmentAttempt::firstOrFail();

        $this->get(route('assessments.results', ['assessment' => 'financial-health', 'attempt' => $attempt->uuid]))
            ->assertOk()
            ->assertSee('The Money-Wise')
            ->assertSee('Your next 3 moves');
    }

    public function test_registering_claims_guest_attempts(): void
    {
        $assessment = Assessment::where('slug', 'financial-health')->firstOrFail();

        AssessmentAttempt::create([
            'assessment_id' => $assessment->id,
            'guest_email' => 'claim-me@example.com',
            'status' => 'completed',
            'percent' => 50,
            'completed_at' => now(),
        ]);

        \Livewire\Volt\Volt::test('pages.auth.register')
            ->set('name', 'Claimer')
            ->set('email', 'claim-me@example.com')
            ->set('password', 'SuperSecret!123')
            ->set('password_confirmation', 'SuperSecret!123')
            ->call('register');

        $user = User::where('email', 'claim-me@example.com')->firstOrFail();

        $this->assertTrue($user->hasRole('member'));
        $this->assertSame(1, $user->attempts()->count());
    }
}
