<?php

namespace App\Http\Controllers;

use App\Models\Ballot;
use App\Models\Candidate;
use App\Models\Election;
use App\Models\Student;
use App\Models\VotingReceipt;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class VotingController extends Controller
{
    public function showVoteForm(Request $request)
    {
        $student = Student::with('grade')->find($request->session()->get('student_id'));

        if (! $student) {
            return redirect()->route('login.form');
        }

        $election = Election::query()->where('is_active', true)->latest('id')->first();

        if (! $election || ! $election->isOpen()) {
            return view('voting.closed', compact('student', 'election'));
        }

        $alreadyVotedInCurrentElection = VotingReceipt::query()
            ->where('election_id', $election->id)
            ->where('student_id', $student->id)
            ->exists();

        if ($alreadyVotedInCurrentElection) {
            return redirect()->route('voting.receipt.show')->with('status', 'Ya registraste tu voto en esta elección.');
        }

        $personeros = Candidate::query()
            ->where('election_id', $election->id)
            ->where('role', 'personero')
            ->where('is_active', true)
            ->orderBy('display_number')
            ->with('grade')
            ->get();

        $contralores = Candidate::query()
            ->where('election_id', $election->id)
            ->where('role', 'contralor')
            ->where('is_active', true)
            ->orderBy('display_number')
            ->with('grade')
            ->get();

        return view('voting.form', compact('student', 'election', 'personeros', 'contralores'));
    }

    public function submitVote(Request $request)
    {
        $studentId = $request->session()->get('student_id');
        if (! $studentId) {
            return redirect()->route('login.form');
        }

        $validated = $request->validate([
            'personero_choice' => ['required', 'string'],
            'contralor_choice' => ['required', 'string'],
        ]);

        $election = Election::query()->where('is_active', true)->latest('id')->firstOrFail();
        if (! $election->isOpen()) {
            return back()->withErrors(['general' => 'La elección no está habilitada en este momento.']);
        }

        DB::transaction(function () use ($studentId, $validated, $election, $request) {
            $student = Student::query()->lockForUpdate()->findOrFail($studentId);

            $alreadyVotedInCurrentElection = VotingReceipt::query()
                ->where('election_id', $election->id)
                ->where('student_id', $student->id)
                ->exists();

            if ($alreadyVotedInCurrentElection) {
                abort(409, 'Este estudiante ya votó en esta elección.');
            }

            $personeroCandidateId = null;
            $contralorCandidateId = null;
            $personeroBlank = $validated['personero_choice'] === 'blanco';
            $contralorBlank = $validated['contralor_choice'] === 'blanco';

            if (! $personeroBlank) {
                $personeroCandidateId = Candidate::query()
                    ->where('id', $validated['personero_choice'])
                    ->where('election_id', $election->id)
                    ->where('role', 'personero')
                    ->where('is_active', true)
                    ->value('id');
            }

            if (! $contralorBlank) {
                $contralorCandidateId = Candidate::query()
                    ->where('id', $validated['contralor_choice'])
                    ->where('election_id', $election->id)
                    ->where('role', 'contralor')
                    ->where('is_active', true)
                    ->value('id');
            }

            if (! $personeroBlank && ! $personeroCandidateId) {
                abort(422, 'Selección de personero inválida.');
            }

            if (! $contralorBlank && ! $contralorCandidateId) {
                abort(422, 'Selección de contralor inválida.');
            }

            Ballot::query()->create([
                'election_id' => $election->id,
                'ballot_uuid' => (string) Str::uuid(),
                'personero_candidate_id' => $personeroCandidateId,
                'contralor_candidate_id' => $contralorCandidateId,
                'personero_blank' => $personeroBlank,
                'contralor_blank' => $contralorBlank,
                'submitted_at' => now(),
            ]);

            $student->forceFill([
                'has_voted' => true,
                'voted_at' => now(),
            ])->save();

            $receiptCode = strtoupper(Str::random(16));
            VotingReceipt::query()->create([
                'election_id' => $election->id,
                'student_id' => $student->id,
                'receipt_code' => $receiptCode,
                'verification_hash' => hash('sha256', $receiptCode.'|'.$student->id.'|'.$election->id.'|'.config('app.key')),
                'voted_at' => now(),
            ]);

            $request->session()->put('receipt_code', $receiptCode);
        });

        return redirect()->route('voting.receipt.show')->with('auto_download_receipt', true);
    }

    public function showReceipt(Request $request)
    {
        $studentId = $request->session()->get('student_id');
        if (! $studentId) {
            return redirect()->route('login.form');
        }

        $activeElection = Election::query()->where('is_active', true)->latest('id')->first();

        $receipt = VotingReceipt::query()
            ->with('election')
            ->where('student_id', $studentId)
            ->when($activeElection, function ($query) use ($activeElection) {
                $query->where('election_id', $activeElection->id);
            })
            ->latest('id')
            ->first();

        if (! $receipt) {
            if ($activeElection) {
                return redirect()->route('voting.form');
            }

            $receipt = VotingReceipt::query()
                ->with('election')
                ->where('student_id', $studentId)
                ->latest('id')
                ->first();
        }

        if (! $receipt) {
            return redirect()->route('voting.form');
        }

        return view('voting.receipt', compact('receipt'));
    }

    public function verifyReceipt(string $receiptCode)
    {
        $receipt = VotingReceipt::query()
            ->with('election')
            ->where('receipt_code', $receiptCode)
            ->first();

        if (! $receipt) {
            abort(404, 'Certificado no encontrado.');
        }

        return view('voting.verify', compact('receipt'));
    }

    public function downloadReceiptPdf(Request $request)
    {
        $studentId = $request->session()->get('student_id');
        if (! $studentId) {
            return redirect()->route('login.form');
        }

        $activeElection = Election::query()->where('is_active', true)->latest('id')->first();

        $receipt = VotingReceipt::query()
            ->with(['election', 'student.grade'])
            ->where('student_id', $studentId)
            ->when($activeElection, function ($query) use ($activeElection) {
                $query->where('election_id', $activeElection->id);
            })
            ->latest('id')
            ->first();

        if (! $receipt) {
            if ($activeElection) {
                return redirect()->route('voting.form');
            }

            $receipt = VotingReceipt::query()
                ->with(['election', 'student.grade'])
                ->where('student_id', $studentId)
                ->latest('id')
                ->first();
        }

        if (! $receipt) {
            return redirect()->route('voting.form');
        }

        $pdf = Pdf::loadView('voting.receipt-pdf', compact('receipt'));

        return $pdf->download('certificado-votacion-'.$receipt->receipt_code.'.pdf');
    }
}
