<?php

namespace App\Livewire;

use App\Models\Assessment;
use App\Models\AssessmentAttempt;
use App\Models\Lead;
use Livewire\Attributes\Layout;
use Livewire\Component;

/**
 * The questions are stepped through entirely client-side (Alpine) for an
 * instant, zero-roundtrip experience; answers are submitted once at the end
 * and all scoring happens server-side.
 */
#[Layout('components.layouts.site')]
class AssessmentRunner extends Component
{
    public Assessment $assessment;

    public ?string $campaign = null;

    public ?string $campaignHeadline = null;

    public ?string $campaignIntro = null;

    public function mount(Assessment $assessment): void
    {
        abort_unless($assessment->is_active, 404);

        $this->assessment = $assessment->load('questions');

        // Event-specific link: /assessments/{slug}?campaign=acme-womens-group
        if ($campaign = \App\Models\Campaign::activeBySlug(request()->query('campaign'))) {
            $this->campaign = $campaign->slug;
            $this->campaignHeadline = $campaign->headline;
            $this->campaignIntro = $campaign->intro;
        }
    }

    /** Icon name => SVG path map for option icons rendered client-side. */
    public function iconMap(): array
    {
        return \App\Support\IconPaths::MAP;
    }

    /** Payload consumed by the Alpine stepper. */
    public function questionPayload(): array
    {
        return $this->assessment->questions->map(fn ($q) => [
            'id' => $q->id,
            'section' => $q->section,
            'feedback' => $q->section_feedback,
            'prompt' => $q->prompt,
            'help' => $q->help,
            'type' => $q->type,
            'options' => collect($q->options)->map(fn ($o) => [
                'key' => $o['key'],
                'label' => $o['label'],
                'icon' => $o['emoji'] ?? null,
            ])->values()->all(),
        ])->values()->all();
    }

    public function submit(array $answers, ?string $guestName = null, ?string $guestEmail = null)
    {
        $questions = $this->assessment->questions;

        // Validate: every answer maps to a real question + option key.
        $clean = [];
        foreach ($questions as $question) {
            $key = $answers[$question->id] ?? $answers[(string) $question->id] ?? null;

            if ($key !== null && collect($question->options)->pluck('key')->contains($key)) {
                $clean[$question->id] = $key;
            }
        }

        abort_if(count($clean) === 0, 422, 'No valid answers submitted.');

        $guestName = $guestName ? mb_substr(trim($guestName), 0, 120) : null;
        $guestEmail = ($guestEmail && filter_var($guestEmail, FILTER_VALIDATE_EMAIL)) ? $guestEmail : null;

        // Guests must share a valid email to unlock their score.
        abort_if(! auth()->check() && ! $guestEmail, 422, 'Email required to view your results.');

        $result = $this->assessment->isProfileMode()
            ? $this->scoreProfile($clean)
            : $this->scorePoints($clean);

        $attempt = AssessmentAttempt::create([
            'assessment_id' => $this->assessment->id,
            'user_id' => auth()->id(),
            'band_id' => $result['band']?->id,
            'guest_name' => auth()->check() ? null : $guestName,
            'guest_email' => auth()->check() ? null : $guestEmail,
            'campaign' => $this->campaign,
            'status' => 'completed',
            'answers' => $clean,
            'score' => $result['score'],
            'max_score' => $result['max'],
            'percent' => $result['percent'],
            'dimension_percents' => $result['dimensions'],
            'completed_at' => now(),
        ]);

        // Tag the campaign so this group's leads are grouped in the admin.
        if ($this->campaign) {
            \App\Models\Campaign::where('slug', $this->campaign)->increment('leads_count');
        }

        if (! auth()->check() && $guestEmail) {
            Lead::firstOrCreate(
                ['email' => $guestEmail, 'source' => "assessment:{$this->assessment->slug}"],
                ['name' => $guestName, 'segment' => $this->campaign, 'meta' => ['attempt_uuid' => $attempt->uuid, 'campaign' => $this->campaign]]
            );
        }

        // Every collected guest email joins the newsletter (unsubscribe always available).
        // Logged-in users are already in the system, so we don't create a lead for them.
        if (! auth()->check() && $guestEmail) {
            Lead::firstOrCreate(
                ['email' => $guestEmail, 'source' => 'newsletter'],
                ['name' => $guestName]
            );
        }

        return $this->redirectRoute('assessments.results', [
            'assessment' => $this->assessment->slug,
            'attempt' => $attempt->uuid,
        ]);
    }

    private function scorePoints(array $answers): array
    {
        $score = 0;
        $maxScore = 0;
        $byDimension = [];

        foreach ($this->assessment->questions as $question) {
            $points = $question->pointsFor($answers[$question->id] ?? null);
            $max = $question->maxPoints();

            $score += $points;
            $maxScore += $max;

            $dimension = $question->dimension ?? 'general';
            $byDimension[$dimension]['score'] = ($byDimension[$dimension]['score'] ?? 0) + $points;
            $byDimension[$dimension]['max'] = ($byDimension[$dimension]['max'] ?? 0) + $max;
        }

        $percent = $maxScore > 0 ? round($score / $maxScore * 100, 2) : 0;

        return [
            'score' => $score,
            'max' => $maxScore,
            'percent' => $percent,
            'dimensions' => collect($byDimension)
                ->map(fn ($d) => $d['max'] > 0 ? round($d['score'] / $d['max'] * 100) : 0)
                ->all(),
            'band' => $this->assessment->overallBandFor($percent),
        ];
    }

    private function scoreProfile(array $answers): array
    {
        $counts = collect($this->assessment->profileTypes())->map(fn () => 0)->all();
        $total = 0;

        foreach ($this->assessment->questions as $question) {
            $chosen = collect($question->options)->firstWhere('key', $answers[$question->id] ?? null);
            $type = $chosen['type'] ?? null;

            if ($type !== null && array_key_exists($type, $counts)) {
                $counts[$type]++;
                $total++;
            }
        }

        $dimensions = collect($counts)
            ->map(fn ($count) => $total > 0 ? (int) round($count / $total * 100) : 0)
            ->all();

        arsort($counts);
        $topType = array_key_first($counts);

        return [
            'score' => $counts[$topType] ?? 0,
            'max' => $total,
            'percent' => $topType !== null ? ($dimensions[$topType] ?? 0) : 0,
            'dimensions' => $dimensions,
            'band' => $topType ? $this->assessment->profileBandFor($topType) : null,
        ];
    }

    public function render()
    {
        return view('livewire.assessment-runner', [
            'payload' => $this->questionPayload(),
        ])->title($this->assessment->title.', Money-Wise');
    }
}
