<?php

namespace App\Http\Controllers\Voting;

use App\Http\Controllers\Controller;
use App\Http\Requests\Voting\StoreVotingRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

class VotingController extends Controller
{
    public function studentList(Request $request): Response
    {
        $schoolId = $this->currentSchoolId($request);
        $userId = $request->user()?->id;
        $studentId = $userId ? DB::table('students')->where('school_id', $schoolId)->where('user_id', $userId)->value('id') : null;

        $votings = $schoolId
            ? DB::table('votings')
                ->where('school_id', $schoolId)
                ->where('status', 'published')
                ->orderByDesc('created_at')
                ->get(['id', 'title', 'description', 'starts_at', 'ends_at'])
                ->map(function ($v) use ($studentId): array {
                    $alreadyVoted = $studentId
                        ? DB::table('voting_votes')->where('voting_id', $v->id)->where('student_id', $studentId)->exists()
                        : false;

                    return [
                        'id' => (int) $v->id,
                        'title' => $v->title,
                        'description' => $v->description,
                        'startsAt' => $v->starts_at ? $this->jalaliDate($v->starts_at, true) : '—',
                        'endsAt' => $v->ends_at ? $this->jalaliDate($v->ends_at, true) : '—',
                        'alreadyVoted' => $alreadyVoted,
                    ];
                })
                ->values()
            : collect()->values();

        return Inertia::render('Voting/StudentList', [
            'votings' => $votings,
        ]);
    }

    public function index(Request $request): Response
    {
        $schoolId = $this->currentSchoolId($request);

        $votings = $schoolId
            ? DB::table('votings')
                ->leftJoin('voting_votes', 'voting_votes.voting_id', '=', 'votings.id')
                ->where('votings.school_id', $schoolId)
                ->groupBy('votings.id', 'votings.title', 'votings.status', 'votings.starts_at', 'votings.ends_at')
                ->select([
                    'votings.id',
                    'votings.title',
                    'votings.status',
                    'votings.starts_at',
                    'votings.ends_at',
                    DB::raw('COUNT(voting_votes.id) as votes_count'),
                ])
                ->latest('votings.created_at')
                ->get()
            : collect();

        return Inertia::render('Voting/List', [
            'votings' => $votings->map(fn ($voting): array => [
                'id' => (int) $voting->id,
                'title' => $voting->title,
                'range' => $this->votingRange((int) $voting->id),
                'status' => $this->votingStatusLabel($voting->status),
                'votesCount' => (int) $voting->votes_count,
                'startsAt' => $voting->starts_at ? $this->jalaliDate($voting->starts_at, true) : '—',
                'endsAt' => $voting->ends_at ? $this->jalaliDate($voting->ends_at, true) : '—',
            ])->values(),
        ]);
    }

    public function create(Request $request): Response
    {
        $schoolId = $this->currentSchoolId($request);

        $grades = $schoolId
            ? DB::table('school_grade_levels')
                ->join('grade_levels', 'grade_levels.id', '=', 'school_grade_levels.grade_level_id')
                ->where('school_grade_levels.school_id', $schoolId)
                ->where('school_grade_levels.enabled', true)
                ->orderBy('grade_levels.number')
                ->get(['grade_levels.id', 'grade_levels.name_fa'])
            : collect();

        $classes = $schoolId
            ? DB::table('classrooms')
                ->where('school_id', $schoolId)
                ->where('status', 'active')
                ->orderBy('name')
                ->get(['id', 'name'])
            : collect();

        return Inertia::render('Voting/Builder', [
            'grades' => $grades->map(fn ($grade): array => ['value' => $grade->id, 'label' => $grade->name_fa])->values(),
            'classes' => $classes->map(fn ($classroom): array => ['value' => $classroom->id, 'label' => $classroom->name])->values(),
        ]);
    }

    public function results(Request $request): Response
    {
        $schoolId = $this->currentSchoolId($request);
        $votingId = $request->integer('voting_id') ?: ($schoolId ? DB::table('votings')->where('school_id', $schoolId)->latest('created_at')->value('id') : null);

        $totalVotes = $votingId
            ? DB::table('voting_votes')->where('voting_id', $votingId)->count()
            : 0;

        $results = $votingId
            ? DB::table('voting_candidates')
                ->leftJoin('voting_votes', 'voting_votes.candidate_id', '=', 'voting_candidates.id')
                ->where('voting_candidates.voting_id', $votingId)
                ->groupBy('voting_candidates.id', 'voting_candidates.display_name')
                ->select([
                    'voting_candidates.display_name',
                    DB::raw('COUNT(voting_votes.id) as votes_count'),
                ])
                ->orderByDesc('votes_count')
                ->get()
            : collect();

        return Inertia::render('Voting/Results', [
            'votingId' => $votingId ? (int) $votingId : null,
            'totalVotes' => (int) $totalVotes,
            'students' => $this->studentOptions((int) ($schoolId ?? 0)),
            'results' => $results->map(fn ($candidate): array => [
                'candidate' => $candidate->display_name,
                'votes' => (int) $candidate->votes_count,
                'percent' => $totalVotes > 0 ? round(((int) $candidate->votes_count / $totalVotes) * 100, 1) : 0,
            ])->values(),
        ]);
    }

    public function vote(Request $request, int $voting): Response
    {
        $schoolId = $this->currentSchoolId($request);

        $votingRow = DB::table('votings')
            ->where('school_id', $schoolId)
            ->where('id', $voting)
            ->where('status', 'published')
            ->first(['id', 'title', 'description']);

        abort_unless($votingRow, 404);

        $candidates = DB::table('voting_candidates')
            ->where('school_id', $schoolId)
            ->where('voting_id', $voting)
            ->where('status', 'approved')
            ->orderBy('display_name')
            ->get(['id', 'display_name', 'slogan']);

        return Inertia::render('Voting/Vote', [
            'voting' => [
                'id' => (int) $votingRow->id,
                'title' => $votingRow->title,
                'description' => $votingRow->description,
            ],
            'candidates' => $candidates->map(fn ($candidate): array => [
                'id' => (int) $candidate->id,
                'name' => $candidate->display_name,
                'slogan' => $candidate->slogan,
            ])->values(),
        ]);
    }

    public function castVote(Request $request, int $voting): RedirectResponse
    {
        $schoolId = $this->currentSchoolId($request);
        $data = $request->validate(['candidate_id' => ['required', 'integer']]);
        $studentId = DB::table('students')->where('school_id', $schoolId)->where('user_id', $request->user()?->id)->value('id');

        if (! $studentId) {
            return back()->withErrors(['candidate_id' => 'برای کاربر فعلی پرونده دانش‌آموزی پیدا نشد.']);
        }

        $candidateOk = DB::table('voting_candidates')
            ->where('school_id', $schoolId)
            ->where('voting_id', $voting)
            ->where('id', $data['candidate_id'])
            ->where('status', 'approved')
            ->exists();

        if (! $candidateOk) {
            return back()->withErrors(['candidate_id' => 'کاندیدا معتبر نیست.']);
        }

        $alreadyVoted = DB::table('voting_votes')
            ->where('voting_id', $voting)
            ->where('student_id', $studentId)
            ->exists();

        if ($alreadyVoted) {
            return back()->withErrors(['candidate_id' => 'شما قبلا در این رأی‌گیری رأی داده‌اید.']);
        }

        DB::table('voting_votes')->insert([
            'school_id' => $schoolId,
            'voting_id' => $voting,
            'candidate_id' => $data['candidate_id'],
            'student_id' => $studentId,
            'vote_hash' => hash('sha256', $voting.'|'.$studentId.'|'.config('app.key')),
            'ip_address' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 1000),
            'created_at' => now(),
        ]);

        DB::table('voting_logs')->insert([
            'school_id' => $schoolId,
            'voting_id' => $voting,
            'user_id' => $request->user()?->id,
            'action' => 'vote_cast',
            'new_value' => json_encode(['candidate_id' => $data['candidate_id'], 'student_id' => $studentId]),
            'ip_address' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 500),
            'created_at' => now(),
        ]);

        return redirect()->route('student.dashboard')->with('success', 'رأی شما ثبت شد.');
    }

    public function manage(Request $request, int $voting): Response
    {
        $schoolId = $this->currentSchoolId($request);

        $votingRow = DB::table('votings')
            ->where('school_id', $schoolId)
            ->where('id', $voting)
            ->first();

        abort_unless($votingRow, 404);

        $candidates = DB::table('voting_candidates')
            ->where('voting_id', $voting)
            ->where('school_id', $schoolId)
            ->orderBy('display_name')
            ->get(['id', 'display_name', 'slogan', 'status']);

        return Inertia::render('Voting/Manage', [
            'voting' => [
                'id' => (int) $votingRow->id,
                'title' => $votingRow->title,
                'description' => $votingRow->description ?? '',
                'status' => $votingRow->status,
                'statusLabel' => $this->votingStatusLabel($votingRow->status),
            ],
            'candidates' => $candidates->map(fn ($c): array => [
                'id' => (int) $c->id,
                'name' => $c->display_name,
                'slogan' => $c->slogan ?? '',
                'status' => $c->status,
            ])->values(),
        ]);
    }

    public function updateStatus(Request $request, int $voting): RedirectResponse
    {
        $schoolId = $this->currentSchoolId($request);

        $votingRow = DB::table('votings')->where('school_id', $schoolId)->where('id', $voting)->first();
        abort_unless($votingRow, 404);

        $data = $request->validate([
            'status' => ['required', 'string', 'in:draft,published,paused,closed'],
        ]);

        $oldStatus = $votingRow->status;

        DB::table('votings')->where('id', $voting)->update([
            'status' => $data['status'],
            'updated_at' => now(),
        ]);

        DB::table('voting_logs')->insert([
            'school_id' => $schoolId,
            'voting_id' => $voting,
            'user_id' => $request->user()?->id,
            'action' => 'status_changed',
            'old_value' => json_encode(['status' => $oldStatus]),
            'new_value' => json_encode(['status' => $data['status']]),
            'ip_address' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 500),
            'created_at' => now(),
        ]);

        return redirect()->route('school.voting.manage', $voting)->with('success', 'وضعیت رأی‌گیری تغییر کرد.');
    }

    public function destroy(Request $request, int $voting): RedirectResponse
    {
        $schoolId = $this->currentSchoolId($request);

        $votingRow = DB::table('votings')->where('school_id', $schoolId)->where('id', $voting)->first();
        abort_unless($votingRow, 404);

        DB::transaction(function () use ($voting): void {
            DB::table('voting_votes')->where('voting_id', $voting)->delete();
            DB::table('voting_candidates')->where('voting_id', $voting)->delete();
            DB::table('voting_eligible_voters')->where('voting_id', $voting)->delete();
            DB::table('votings')->where('id', $voting)->delete();
        });

        return redirect()->route('school.voting')->with('success', 'رأی‌گیری حذف شد.');
    }

    public function store(StoreVotingRequest $request): RedirectResponse
    {
        $schoolId = $this->currentSchoolId($request);

        abort_unless($schoolId, 422, 'مدرسه مشخص نیست.');

        $data = $request->validated();

        $votingId = DB::transaction(function () use ($request, $schoolId, $data): int {
            $votingId = DB::table('votings')->insertGetId([
                'school_id' => $schoolId,
                'title' => $data['title'],
                'description' => $data['description'] ?? null,
                'type' => $data['type'],
                'is_secret' => true,
                'starts_at' => $data['starts_at'] ?? null,
                'ends_at' => $data['ends_at'] ?? null,
                'status' => 'draft',
                'created_by' => $request->user()?->id,
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            foreach (($data['grade_level_ids'] ?? []) as $gradeId) {
                $allowed = DB::table('school_grade_levels')
                    ->where('school_id', $schoolId)
                    ->where('grade_level_id', $gradeId)
                    ->where('enabled', true)
                    ->exists();

                if ($allowed) {
                    DB::table('voting_eligible_voters')->insert([
                        'school_id' => $schoolId,
                        'voting_id' => $votingId,
                        'grade_level_id' => $gradeId,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);
                }
            }

            foreach (($data['classroom_ids'] ?? []) as $classroomId) {
                $allowed = DB::table('classrooms')
                    ->where('school_id', $schoolId)
                    ->where('id', $classroomId)
                    ->where('status', 'active')
                    ->exists();

                if ($allowed) {
                    DB::table('voting_eligible_voters')->insert([
                        'school_id' => $schoolId,
                        'voting_id' => $votingId,
                        'classroom_id' => $classroomId,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);
                }
            }

            DB::table('voting_logs')->insert([
                'school_id' => $schoolId,
                'voting_id' => $votingId,
                'user_id' => $request->user()?->id,
                'action' => 'created',
                'new_value' => json_encode(['title' => $data['title'], 'type' => $data['type']]),
                'ip_address' => $request->ip(),
                'user_agent' => substr((string) $request->userAgent(), 0, 500),
                'created_at' => now(),
            ]);

            return $votingId;
        });

        return redirect()->route('school.voting.results', ['voting_id' => $votingId])->with('success', 'رأی‌گیری ساخته شد و در وضعیت پیش‌نویس قرار گرفت.');
    }


    private function votingRange(int $votingId): string
    {
        $grades = DB::table('voting_eligible_voters')
            ->leftJoin('grade_levels', 'grade_levels.id', '=', 'voting_eligible_voters.grade_level_id')
            ->where('voting_eligible_voters.voting_id', $votingId)
            ->whereNotNull('grade_levels.name_fa')
            ->distinct()
            ->pluck('grade_levels.name_fa');

        if ($grades->isNotEmpty()) {
            return $grades->implode('، ');
        }

        $classes = DB::table('voting_eligible_voters')
            ->leftJoin('classrooms', 'classrooms.id', '=', 'voting_eligible_voters.classroom_id')
            ->where('voting_eligible_voters.voting_id', $votingId)
            ->whereNotNull('classrooms.name')
            ->distinct()
            ->pluck('classrooms.name');

        return $classes->isNotEmpty() ? $classes->implode('، ') : 'همه واجدان تعریف‌شده';
    }

    private function votingStatusLabel(string $status): string
    {
        return match ($status) {
            'draft' => 'پیش‌نویس',
            'published' => 'فعال',
            'paused' => 'متوقف',
            'closed' => 'پایان‌یافته',
            'archived' => 'آرشیو',
            default => $status,
        };
    }

    private function studentOptions(int $schoolId): array
    {
        if (! $schoolId) {
            return [];
        }

        return DB::table('students')
            ->where('school_id', $schoolId)
            ->where('status', 'active')
            ->orderBy('last_name')
            ->get(['id', 'first_name', 'last_name', 'student_code'])
            ->map(fn ($student): array => ['value' => $student->id, 'label' => trim($student->first_name.' '.$student->last_name).' - '.$student->student_code])
            ->values()
            ->all();
    }
}
