<?php

namespace App\Services\Students;

use App\Models\Configuration;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class StudentScoreCardService
{
    /**
     * Students shown on the score-card picker (legacy `StudentController::scoreCardList` parity).
     * Teachers: only students in `class_section_id` for `school_year` after `teacher_class` check.
     * Parents: their children only. Other roles: active students (optional search/limit).
     *
     * @return list<array{id: int, student_id: int, school_id: string, firstname: string, lastname: string, name: string, is_active: int}>
     */
    public function scoreCardSelectableStudents(
        User $user,
        string $searchQuery,
        int $limit,
        ?int $classSectionId,
        ?string $schoolYear,
    ): array {
        $rolesLower = array_map('strtolower', $user->roleNames());
        $isTeacher = in_array('teacher', $rolesLower, true) || in_array('teacher_assistant', $rolesLower, true);
        $isParent = in_array('parent', $rolesLower, true);

        $cid = (int) ($classSectionId ?? 0);
        $year = $schoolYear !== null && trim($schoolYear) !== '' ? trim($schoolYear) : (string) (Configuration::getConfig('school_year') ?? '');

        if ($isTeacher && $cid > 0) {
            $assigned = TeacherClass::query()
                ->where('teacher_id', $user->id)
                ->where('class_section_id', $cid)
                ->when($year !== '', fn ($q) => $q->where('school_year', $year))
                ->exists();

            if (! $assigned) {
                return [];
            }

            $students = $year !== ''
                ? Student::getByClassAndYear($cid, $year)
                : Student::query()
                    ->whereIn('id', StudentClass::query()->select('student_id')->where('class_section_id', $cid))
                    ->where('is_active', 1)
                    ->orderBy('lastname')
                    ->orderBy('firstname')
                    ->get()
                    ->all();

            $rows = array_map(fn (Student $s) => $this->selectableStudentPayload($s), $students);

            return $this->applySearchAndLimit($rows, $searchQuery, $limit);
        }

        if ($isTeacher && ! $isParent) {
            return [];
        }

        if ($isParent) {
            $q = Student::query()
                ->select('id', 'school_id', 'firstname', 'lastname', 'is_active')
                ->where('parent_id', $user->id)
                ->where('is_active', 1);

            if ($searchQuery !== '') {
                $sq = $searchQuery;
                $q->where(function ($b) use ($sq) {
                    $b->where('firstname', 'like', '%'.$sq.'%')
                        ->orWhere('lastname', 'like', '%'.$sq.'%')
                        ->orWhere('school_id', 'like', '%'.$sq.'%');
                });
            }

            $students = $q->orderBy('lastname')->orderBy('firstname')->limit($limit)->get();

            return $students->map(fn (Student $s) => $this->selectableStudentPayload($s))->values()->all();
        }

        $studentsQuery = Student::query()
            ->select('id', 'school_id', 'firstname', 'lastname', 'is_active')
            ->where('is_active', 1);

        if ($searchQuery !== '') {
            $studentsQuery->where(function ($builder) use ($searchQuery) {
                $builder->where('firstname', 'like', '%'.$searchQuery.'%')
                    ->orWhere('lastname', 'like', '%'.$searchQuery.'%')
                    ->orWhere('school_id', 'like', '%'.$searchQuery.'%');
            });
        }

        return $studentsQuery
            ->orderBy('lastname')
            ->orderBy('firstname')
            ->limit($limit)
            ->get()
            ->map(fn (Student $student) => $this->selectableStudentPayload($student))
            ->values()
            ->all();
    }

    /**
     * @param  list<array{id: int, student_id: int, school_id: string, firstname: string, lastname: string, name: string, is_active: int}>  $rows
     * @return list<array{id: int, student_id: int, school_id: string, firstname: string, lastname: string, name: string, is_active: int}>
     */
    private function applySearchAndLimit(array $rows, string $searchQuery, int $limit): array
    {
        if ($searchQuery === '') {
            return array_slice($rows, 0, $limit);
        }

        $needle = strtolower($searchQuery);
        $filtered = array_values(array_filter($rows, static function (array $r) use ($needle): bool {
            $hay = strtolower(
                ($r['firstname'] ?? '').' '.($r['lastname'] ?? '').' '.($r['school_id'] ?? '')
            );

            return str_contains($hay, $needle);
        }));

        return array_slice($filtered, 0, $limit);
    }

    private function selectableStudentPayload(Student $student): array
    {
        return [
            'id' => (int) $student->id,
            'student_id' => (int) $student->id,
            'school_id' => (string) ($student->school_id ?? ''),
            'firstname' => (string) ($student->firstname ?? ''),
            'lastname' => (string) ($student->lastname ?? ''),
            'name' => trim(((string) ($student->firstname ?? '')).' '.((string) ($student->lastname ?? ''))),
            'is_active' => (int) ($student->is_active ?? 0),
        ];
    }

    public function scoreCard(int $studentId): array
    {
        $student = Student::query()
            ->select('id', 'firstname', 'lastname', 'school_id')
            ->where('id', $studentId)
            ->first();

        if (!$student) {
            return ['ok' => false, 'message' => 'Student not found.'];
        }

        $rows = DB::table('semester_scores as ss')
            ->select([
                'ss.school_year',
                'ss.semester',
                'ss.homework_avg',
                'ss.project_avg',
                'ss.participation_score',
                'ss.quiz_avg',
                'ss.test_avg',
                'ss.attendance_score',
                'ss.ptap_score',
                'ss.midterm_exam_score',
                'ss.semester_score',
                'cs.class_section_name',
            ])
            ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'ss.class_section_id')
            ->where('ss.student_id', $studentId)
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        usort($rows, static function (array $a, array $b): int {
            $ay = (string) ($a['school_year'] ?? '');
            $by = (string) ($b['school_year'] ?? '');
            if ($ay !== $by) {
                return strcmp($by, $ay);
            }
            return strcmp((string) ($a['semester'] ?? ''), (string) ($b['semester'] ?? ''));
        });

        $yearScoreMap = [];
        foreach ($rows as $row) {
            $year = (string) ($row['school_year'] ?? '');
            $score = $row['semester_score'] ?? null;
            if ($year === '' || !is_numeric($score)) {
                continue;
            }
            $yearScoreMap[$year][] = (float) $score;
        }

        $yearAvg = [];
        foreach ($yearScoreMap as $year => $scores) {
            $yearAvg[$year] = round(array_sum($scores) / count($scores), 1);
        }

        $commentRows = DB::table('score_comments')
            ->select('school_year', 'semester', 'score_type', 'comment', 'comment_review')
            ->where('student_id', $studentId)
            ->orderByDesc('created_at')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();

        $commentMap = [];
        foreach ($commentRows as $row) {
            $year = (string) ($row['school_year'] ?? '');
            $semester = strtolower(trim((string) ($row['semester'] ?? '')));
            $type = strtolower(trim((string) ($row['score_type'] ?? '')));
            if ($year === '' || $type === '') {
                continue;
            }
            if ($type === 'attendance_comment') {
                $type = 'attendance';
            }
            $commentVal = trim((string) ($row['comment'] ?? ''));
            $reviewVal = trim((string) ($row['comment_review'] ?? ''));
            if (in_array($type, ['midterm', 'final', 'ptap'], true)) {
                $commentVal = $reviewVal !== '' ? $reviewVal : $commentVal;
            } elseif ($type === 'attendance' && $commentVal === '' && $reviewVal !== '') {
                $commentVal = $reviewVal;
            }
            if ($commentVal === '') {
                continue;
            }
            $key = $year . '|' . $semester;
            $commentMap[$key][$type] = $commentVal;
        }

        foreach ($rows as &$row) {
            $year = (string) ($row['school_year'] ?? '');
            $semester = strtolower(trim((string) ($row['semester'] ?? '')));
            $row['comments'] = $commentMap[$year . '|' . $semester] ?? [];
            $row['year_score'] = $yearAvg[$year] ?? null;
        }
        unset($row);

        return [
            'ok' => true,
            'student' => $student->toArray(),
            'rows' => $rows,
        ];
    }
}
