<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Requests\Session\UpdateSessionRequest;
use App\Models\Session;
use App\Services\ActivityLogger;
use App\Services\ScoringService;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use Inertia\Response as InertiaResponse;

final class SessionController extends Controller
{
    /**
     * Create a new session for the selected category.
     */
    public function store(Request $request): RedirectResponse
    {
        $session = Session::create([
            'user_id' => auth()->id(),
            'category_id' => $request->input('category_id'),
            'screening_id' => $request->input('screening_id'),
            'status' => 'in_progress',
        ]);

        ActivityLogger::log('session_started', auth()->id(), sessionId: $session->id, categoryId: (int) $request->input('category_id'), metadata: ['category_id' => $request->input('category_id')]);

        return redirect()->route('sessions.show', $session);
    }

    /**
     * Display the session questionnaire with category, question groups, questions, and existing answers.
     */
    public function show(Session $session): InertiaResponse
    {
        $session->load('category', 'user');

        $questionGroups = $session->category
            ->questionGroups()
            ->with(['questions' => fn ($q) => $q->orderBy('sort_order')])
            ->orderBy('sort_order')
            ->get();

        ActivityLogger::log('step_viewed', auth()->id(), sessionId: $session->id, categoryId: $session->category_id, metadata: ['question_group_id' => $questionGroups->first()?->id]);

        $answers = $session->answers()->get()->keyBy('question_id');

        return Inertia::render('Session/Show', [
            'session' => $session,
            'questionGroups' => $questionGroups,
            'answers' => $answers,
        ]);
    }

    /**
     * Save session basic info, answers, and additional comments.
     */
    public function update(UpdateSessionRequest $request, Session $session): RedirectResponse
    {
        sleep(3);

        $validated = $request->validated();

        if (Arr::has($validated, 'answers')) {
            $this->saveAnswers($session, Arr::get($validated, 'answers'));
        }

        if (Arr::has($validated, 'additional_comments')) {
            $session->update(['additional_comments' => Arr::get($validated, 'additional_comments')]);
        }

        if ($request->boolean('complete')) {
            return $this->completeSession($session);
        }

        return back();
    }

    /**
     * Save or update answers for the session using composite key upsert.
     */
    private function saveAnswers(Session $session, array $answers): void
    {
        foreach ($answers as $questionId => $answer) {
            $session->answers()->updateOrCreate(
                ['question_id' => (int) $questionId],
                [
                    'value' => Arr::get($answer, 'value'),
                    'text_value' => Arr::get($answer, 'text_value'),
                ]
            );

            ActivityLogger::log('answer_saved', auth()->id(), sessionId: $session->id, categoryId: $session->category_id, metadata: [
                'question_id' => (int) $questionId,
                'value' => Arr::get($answer, 'value'),
            ]);
        }
    }

    /**
     * Complete the session by calculating final score and result.
     */
    private function completeSession(Session $session): RedirectResponse
    {
        $this->validateSessionCompletion($session);

        $scoringService = new ScoringService;
        $score = $scoringService->calculateScore($session);
        $result = $scoringService->determineResult($score);

        $session->update([
            'score' => $score,
            'result' => $result,
            'status' => 'completed',
            'completed_at' => now(),
        ]);

        ActivityLogger::log('session_completed', auth()->id(), sessionId: $session->id, categoryId: $session->category_id, metadata: ['category_id' => $session->category_id, 'score' => $score, 'result' => $result]);

        return redirect()->route('sessions.result', $session);
    }

    /**
     * Validate that all required fields are answered before session completion.
     */
    private function validateSessionCompletion(Session $session): void
    {
        $session->load(['category.questionGroups.questions', 'answers']);

        $errors = [];

        foreach ($session->category->questionGroups as $questionGroup) {
            foreach ($questionGroup->questions as $question) {
                $answer = $session->answers->firstWhere('question_id', $question->id);

                $this->validateRadioAnswer($question, $answer, $errors);
                $this->validateDetailsAnswer($question, $answer, $errors);
            }
        }

        if (Arr::exists($errors, 0)) {
            throw ValidationException::withMessages([
                'complete' => $errors,
            ]);
        }
    }

    /**
     * Validate that radio button questions have an answer selected.
     */
    private function validateRadioAnswer($question, $answer, array &$errors): void
    {
        $hasRadioButtons = $question->has_yes || $question->has_no || $question->has_na;

        if ($hasRadioButtons && (! $answer || $answer->value === null)) {
            $errors[] = "Question '{$question->text}' requires an answer.";
        }
    }

    /**
     * Validate that questions with required details have text values provided.
     */
    private function validateDetailsAnswer($question, $answer, array &$errors): void
    {
        $details = $question->details;
        $hasRadioButtons = $question->has_yes || $question->has_no || $question->has_na;

        if ($details === 'required') {
            if (! $answer || empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
                $errors[] = "Question '{$question->text}' requires details to be provided.";
            }
        }

        if ($details === 'req_on_yes' && $answer && $answer->value === 'yes') {
            if (empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
                $errors[] = "Question '{$question->text}' requires details when answered 'Yes'.";
            }
        }

        if ($details === 'req_on_no' && $answer && $answer->value === 'no') {
            if (empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
                $errors[] = "Question '{$question->text}' requires details when answered 'No'.";
            }
        }

        if (! $hasRadioButtons && $details !== null && $details !== '' && $details !== 'optional') {
            if (! $answer || empty(trim(Arr::get($answer->toArray(), 'text_value', '')))) {
                $errors[] = "Question '{$question->text}' requires a text response.";
            }
        }
    }

    /**
     * Display the final session result.
     */
    public function result(Session $session): InertiaResponse
    {
        $session->load('category');

        return Inertia::render('Session/Result', [
            'session' => $session,
            'score' => $session->score,
            'result' => $session->result,
            'categoryName' => $session->category->name,
        ]);
    }

    /**
     * Generate and download a PDF export of the completed session result.
     */
    public function pdf(Session $session): Response
    {
        abort_unless($session->user_id === auth()->id(), 403);
        abort_unless($session->status === 'completed', 403);

        $session->load(['user', 'category', 'answers.question']);

        $questionGroups = $session->category
            ->questionGroups()
            ->with(['questions' => fn ($q) => $q->orderBy('sort_order')])
            ->orderBy('sort_order')
            ->get();

        ActivityLogger::log('session_pdf_downloaded', $session->user_id, sessionId: $session->id, categoryId: $session->category_id);

        $pdf = Pdf::loadView('pdf.session-result', [
            'session' => $session,
            'questionGroups' => $questionGroups,
        ]);

        return $pdf->download("go-no-go-{$session->id}.pdf");
    }
}
