<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\ClassSectionModel;
use App\Models\CompetitionClassWinnerModel;
use App\Models\CompetitionModel;
use App\Models\CompetitionScoreModel;
use App\Models\ConfigurationModel;
use App\Models\TeacherClassModel;

class CompetitionScoresController extends BaseController
{
    protected $db;
    protected TeacherClassModel $teacherClassModel;
    protected ConfigurationModel $configModel;
    protected ClassSectionModel $classSectionModel;
    protected CompetitionClassWinnerModel $classWinnerModel;
    protected string $classStudentTable;
    protected bool $hasQuestionCount = false;
    protected bool $hasClassWinnerTable = false;

    public function __construct()
    {
        $this->db = \Config\Database::connect();
        $this->teacherClassModel = new TeacherClassModel();
        $this->configModel = new ConfigurationModel();
        $this->classSectionModel = new ClassSectionModel();
        $this->classWinnerModel = new CompetitionClassWinnerModel();

        if ($this->db->tableExists('class_student')) {
            $this->classStudentTable = 'class_student';
        } elseif ($this->db->tableExists('student_class')) {
            $this->classStudentTable = 'student_class';
        } else {
            $this->classStudentTable = '';
        }

        $this->hasClassWinnerTable = $this->db->tableExists('competition_class_winners');
        $this->hasQuestionCount = $this->hasClassWinnerTable
            && $this->db->fieldExists('question_count', 'competition_class_winners');
    }

    public function index()
    {
        [$assignments, $schoolYear, $semester] = $this->getTeacherContext();
        $activeClassId = $this->resolveActiveClassId($assignments);
        $activeClassName = $this->getActiveClassName($assignments, $activeClassId);

        if (empty($assignments) || $activeClassId <= 0) {
            return view('teacher/competition_scores/index', [
                'competitions'    => [],
                'activeClassId'   => $activeClassId,
                'activeClassName' => $activeClassName,
                'questionCounts'  => [],
                'scoreCounts'     => [],
                'studentTotal'    => 0,
                'sectionMap'      => $this->getClassSectionMap(),
                'schoolYear'      => $schoolYear,
                'semester'        => $semester,
                'hasClasses'      => false,
            ]);
        }

        $competitions = $this->getCompetitionsForClass($activeClassId, $schoolYear, $semester);
        $competitionIds = array_values(array_filter(array_map(static function ($row) {
            return (int) ($row['id'] ?? 0);
        }, $competitions)));

        $questionCounts = $this->getQuestionCounts($competitionIds, $activeClassId);
        $scoreCounts = $this->getScoreCounts($competitionIds, $activeClassId);
        $studentTotal = $this->getClassStudentCount($activeClassId, $schoolYear);

        return view('teacher/competition_scores/index', [
            'competitions'    => $competitions,
            'activeClassId'   => $activeClassId,
            'activeClassName' => $activeClassName,
            'questionCounts'  => $questionCounts,
            'scoreCounts'     => $scoreCounts,
            'studentTotal'    => $studentTotal,
            'sectionMap'      => $this->getClassSectionMap(),
            'schoolYear'      => $schoolYear,
            'semester'        => $semester,
            'hasClasses'      => true,
        ]);
    }

    public function edit($id)
    {
        [$assignments, $schoolYear, $semester] = $this->getTeacherContext();
        $allowedClassIds = $this->getAllowedClassIds($assignments);
        if (empty($allowedClassIds)) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'No class assignments found for your account.');
        }

        $competitionModel = new CompetitionModel();
        $scoreModel = new CompetitionScoreModel();
        $competition = $competitionModel->find($id);
        if (!$competition) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'Competition not found.');
        }
        $isLocked = !empty($competition['is_locked']);

        $activeClassId = $this->resolveActiveClassId($assignments);
        $lockedClassId = (int) ($competition['class_section_id'] ?? 0);
        $classSectionId = $lockedClassId > 0 ? $lockedClassId : $activeClassId;
        if ($classSectionId <= 0) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'Select a class section before entering scores.');
        }
        if (!in_array($classSectionId, $allowedClassIds, true)) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'You are not assigned to that class.');
        }

        $students = $this->getStudentsForCompetition($competition, $classSectionId);
        $existingScores = $scoreModel
            ->where('competition_id', $id)
            ->where('class_section_id', $classSectionId)
            ->findAll();

        $scoreMap = [];
        foreach ($existingScores as $row) {
            $scoreMap[$row['student_id']] = $row['score'];
        }

        $questionCount = null;
        if ($this->hasQuestionCount && $this->hasClassWinnerTable) {
            $row = $this->classWinnerModel
                ->select('question_count')
                ->where('competition_id', $id)
                ->where('class_section_id', $classSectionId)
                ->first();
            if ($row && $row['question_count'] !== null) {
                $questionCount = (int) $row['question_count'];
            }
        }

        $classStudentCount = $this->getClassStudentCount(
            $classSectionId,
            $competition['school_year'] ?? $schoolYear
        );

        $activeClassName = $this->getActiveClassName($assignments, $classSectionId);

        return view('teacher/competition_scores/scores', [
            'competition'       => $competition,
            'students'          => $students,
            'scoreMap'          => $scoreMap,
            'classSectionId'    => $classSectionId,
            'classSectionName'  => $activeClassName,
            'classStudentCount' => $classStudentCount,
            'questionCount'     => $questionCount,
            'classSelectionLocked' => $lockedClassId > 0,
            'isLocked'          => $isLocked,
        ]);
    }

    public function save($id)
    {
        [$assignments] = $this->getTeacherContext();
        $allowedClassIds = $this->getAllowedClassIds($assignments);
        if (empty($allowedClassIds)) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'No class assignments found for your account.');
        }

        $competitionModel = new CompetitionModel();
        $scoreModel = new CompetitionScoreModel();
        $competition = $competitionModel->find($id);
        if (!$competition) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'Competition not found.');
        }
        if (!empty($competition['is_locked'])) {
            return redirect()->to("/teacher/competition-scores/{$id}")
                ->with('error', 'Competition is locked. You cannot update scores.');
        }

        $lockedClassId = (int) ($competition['class_section_id'] ?? 0);
        $classSectionId = $lockedClassId > 0
            ? $lockedClassId
            : (int) $this->request->getPost('class_section_id');
        if ($classSectionId <= 0) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'Select a class section before saving scores.');
        }
        if (!in_array($classSectionId, $allowedClassIds, true)) {
            return redirect()->to('/teacher/competition-scores')
                ->with('error', 'You are not assigned to that class.');
        }

        $scores = (array) $this->request->getPost('scores');
        $cleanScores = [];
        $invalidScores = [];

        foreach ($scores as $studentId => $scoreValue) {
            $scoreValue = trim((string) $scoreValue);
            if ($scoreValue === '') {
                continue;
            }
            if (!preg_match('/^\d+$/', $scoreValue)) {
                $invalidScores[] = $studentId;
                continue;
            }
            $cleanScores[(int) $studentId] = (int) $scoreValue;
        }

        if (!empty($invalidScores)) {
            return redirect()->to("/teacher/competition-scores/{$id}")
                ->with('error', 'Scores must be whole numbers (no decimals).');
        }

        foreach ($cleanScores as $studentId => $scoreValue) {
            $existing = $scoreModel
                ->where('competition_id', $id)
                ->where('student_id', (int) $studentId)
                ->where('class_section_id', $classSectionId)
                ->first();

            if ($existing) {
                $scoreModel->update($existing['id'], [
                    'score'            => $scoreValue,
                    'class_section_id' => $classSectionId,
                ]);
            } else {
                $scoreModel->insert([
                    'competition_id'   => (int) $id,
                    'student_id'       => (int) $studentId,
                    'class_section_id' => $classSectionId,
                    'score'            => $scoreValue,
                ]);
            }
        }

        return redirect()->to("/teacher/competition-scores/{$id}")
            ->with('success', 'Scores saved.');
    }

    private function getTeacherContext(): array
    {
        $userId = (int) (session()->get('user_id') ?? 0);
        $schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
        $semester = (string) ($this->configModel->getConfig('semester') ?? '');
        $assignments = $this->teacherClassModel->getClassAssignmentsByUserId(
            $userId,
            $schoolYear,
            $semester
        );

        return [$assignments, $schoolYear, $semester];
    }

    private function getAllowedClassIds(array $assignments): array
    {
        $ids = array_map(static function ($row) {
            return (int) ($row['class_section_id'] ?? 0);
        }, $assignments);

        $ids = array_values(array_filter(array_unique($ids)));
        return $ids;
    }

    private function resolveActiveClassId(array $assignments): int
    {
        $allowed = $this->getAllowedClassIds($assignments);
        $active = (int) (session()->get('class_section_id') ?? 0);
        if ($active > 0 && in_array($active, $allowed, true)) {
            return $active;
        }

        $active = $allowed[0] ?? 0;
        if ($active > 0) {
            session()->set('class_section_id', $active);
        }

        return (int) $active;
    }

    private function getActiveClassName(array $assignments, int $classSectionId): ?string
    {
        if ($classSectionId <= 0) {
            return null;
        }

        foreach ($assignments as $row) {
            if ((int) ($row['class_section_id'] ?? 0) === $classSectionId) {
                return $row['class_section_name'] ?? null;
            }
        }

        $row = $this->classSectionModel
            ->select('class_section_name')
            ->where('class_section_id', $classSectionId)
            ->first();

        return $row['class_section_name'] ?? null;
    }

    private function getCompetitionsForClass(int $classSectionId, string $schoolYear, string $semester): array
    {
        $competitionModel = new CompetitionModel();
        $builder = $competitionModel->orderBy('id', 'DESC');

        if ($classSectionId > 0) {
            $builder->groupStart()
                ->where('class_section_id', $classSectionId)
                ->orWhere('class_section_id', 0)
                ->orWhere('class_section_id IS NULL', null, false)
                ->groupEnd();
        }

        if ($schoolYear !== '') {
            $builder->groupStart()
                ->where('school_year', $schoolYear)
                ->orWhere('school_year', '')
                ->orWhere('school_year IS NULL', null, false)
                ->groupEnd();
        }

        if ($semester !== '') {
            $builder->groupStart()
                ->where('semester', $semester)
                ->orWhere('semester', '')
                ->orWhere('semester IS NULL', null, false)
                ->groupEnd();
        }

        return $builder->findAll();
    }

    private function getQuestionCounts(array $competitionIds, int $classSectionId): array
    {
        if (!$this->hasQuestionCount || !$this->hasClassWinnerTable || empty($competitionIds)) {
            return [];
        }

        $rows = $this->classWinnerModel
            ->select('competition_id, question_count')
            ->where('class_section_id', $classSectionId)
            ->whereIn('competition_id', $competitionIds)
            ->findAll();

        $out = [];
        foreach ($rows as $row) {
            $compId = (int) ($row['competition_id'] ?? 0);
            if ($compId > 0 && $row['question_count'] !== null) {
                $out[$compId] = (int) $row['question_count'];
            }
        }

        return $out;
    }

    private function getScoreCounts(array $competitionIds, int $classSectionId): array
    {
        if (empty($competitionIds)) {
            return [];
        }

        $rows = $this->db->table('competition_scores')
            ->select('competition_id, COUNT(*) AS total')
            ->where('class_section_id', $classSectionId)
            ->whereIn('competition_id', $competitionIds)
            ->groupBy('competition_id')
            ->get()
            ->getResultArray();

        $out = [];
        foreach ($rows as $row) {
            $compId = (int) ($row['competition_id'] ?? 0);
            if ($compId > 0) {
                $out[$compId] = (int) ($row['total'] ?? 0);
            }
        }

        return $out;
    }

    private function getClassStudentCount(int $classSectionId, ?string $schoolYear): int
    {
        if ($classSectionId <= 0 || $this->classStudentTable === '') {
            return 0;
        }

        $builder = $this->db->table($this->classStudentTable)
            ->select('COUNT(*) AS total')
            ->where('class_section_id', $classSectionId);

        if ($schoolYear && $this->db->fieldExists('school_year', $this->classStudentTable)) {
            $builder->where('school_year', $schoolYear);
        }

        $row = $builder->get()->getRowArray();
        return (int) ($row['total'] ?? 0);
    }

    private function getStudentsForCompetition(array $competition, int $classSectionId): array
    {
        if ($classSectionId <= 0 || $this->classStudentTable === '') {
            return [];
        }

        $builder = $this->db->table('students s')
            ->select('s.id, s.school_id, s.firstname, s.lastname')
            ->join($this->classStudentTable . ' cs', 'cs.student_id = s.id', 'inner')
            ->where('cs.class_section_id', $classSectionId);

        $hasSchoolYear = $this->db->fieldExists('school_year', $this->classStudentTable);
        if ($hasSchoolYear && !empty($competition['school_year'])) {
            $builder->where('cs.school_year', $competition['school_year']);
        }

        $builder->distinct();
        $builder->orderBy('s.lastname', 'ASC')
            ->orderBy('s.firstname', 'ASC');

        return $builder->get()->getResultArray();
    }

    private function getClassSectionMap(): array
    {
        $sections = $this->classSectionModel
            ->select('class_section_id, class_section_name')
            ->orderBy('class_section_name', 'ASC')
            ->findAll();

        $map = [];
        foreach ($sections as $section) {
            $id = (int) ($section['class_section_id'] ?? 0);
            if ($id > 0) {
                $map[$id] = $section['class_section_name'] ?? (string) $id;
            }
        }

        return $map;
    }
}
