<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;

class Student extends BaseModel
{
    use HasFactory;
    protected $table = 'students';

    /**
     * CI: timestamps disabled
     */
    public $timestamps = false;

    protected $fillable = [
        'school_id',
        'firstname',
        'lastname',
        'dob',
        'age',
        'gender',
        'is_active',
        'registration_grade',
        'is_new',
        'photo_consent',
        'parent_id',
        'registration_date',
        'tuition_paid',
        'rfid_tag',
        'semester',
        'year_of_registration',
        'school_year',
    ];

    protected $casts = [
        'school_id'          => 'string',
        'age'                => 'integer',
        'photo_consent'      => 'boolean',
        'is_new'             => 'boolean',
        'parent_id'          => 'integer',
        'tuition_paid'       => 'boolean',
        'is_active'          => 'boolean',
        'dob'                => 'date',
        'registration_date'  => 'date',
        'year_of_registration' => 'integer',
    ];

    /* ============================================================
     * Relationships (optional but useful)
     * ============================================================
     */

    public function parent(): BelongsTo
    {
        // Change User model if your parent lives elsewhere
        return $this->belongsTo(User::class, 'parent_id');
    }

    public function classAssignments(): HasMany
    {
        return $this->hasMany(StudentClass::class, 'student_id');
    }

    public function allergies(): HasMany
    {
        return $this->hasMany(StudentAllergy::class, 'student_id');
    }

    public function medicalConditions(): HasMany
    {
        return $this->hasMany(StudentMedicalCondition::class, 'student_id');
    }

    /* ============================================================
     * Scopes
     * ============================================================
     */

    public function scopeActive(Builder $q): Builder
    {
        return $q->where('is_active', 1);
    }

    public function scopeNew(Builder $q): Builder
    {
        return $q->where('is_new', 1);
    }

    public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder
    {
        if ($schoolYear === null || strtolower(trim($schoolYear)) === 'all' || trim($schoolYear) === '') {
            return $q;
        }
        return $q->where('school_year', $schoolYear);
    }

    public function scopeOrderByName(Builder $q): Builder
    {
        return $q->orderBy('lastname', 'asc')->orderBy('firstname', 'asc');
    }

    /* ============================================================
     * CI-compatible methods (converted)
     * ============================================================
     */

    /** CI: getNewStudents() */
    public static function getNewStudents(): array
    {
        return static::query()
            ->new()
            ->orderByName()
            ->get()
            ->all();
    }

    /** CI: getNewStudentsWithParents() */
    public static function getNewStudentsWithParents(): array
    {
        return DB::table('students')
            ->selectRaw('
                students.*,
                u.id        AS parent_id,
                u.firstname AS parent_firstname,
                u.lastname  AS parent_lastname,
                u.email     AS parent_email,
                u.cellphone AS parent_phone
            ')
            ->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
            ->where('students.is_new', 1)
            ->orderBy('students.lastname', 'asc')
            ->orderBy('students.firstname', 'asc')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /**
     * CI: getNewStudentsWithParentsAndEmergency()
     * Picks one emergency contact via MIN(...) with groupBy student.
     */
    public static function getNewStudentsWithParentsAndEmergency(): array
    {
        return DB::table('students')
            ->selectRaw('
                students.*,
                u.firstname AS parent_firstname,
                u.lastname  AS parent_lastname,
                u.email     AS parent_email,
                u.cellphone AS parent_phone,
                MIN(ec.emergency_contact_name) AS emergency_name,
                MIN(ec.relation)               AS emergency_relationship,
                MIN(ec.cellphone)              AS emergency_phone
            ')
            ->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
            ->leftJoin('emergency_contacts as ec', 'ec.parent_id', '=', 'students.parent_id')
            ->where('students.is_new', 1)
            ->groupBy('students.id')
            ->orderBy('students.lastname', 'asc')
            ->orderBy('students.firstname', 'asc')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /**
     * CI: getStudentsWithParentsAndEmergency(?schoolYear, ?isNew)
     */
    public static function getStudentsWithParentsAndEmergency(?string $schoolYear = null, ?int $isNew = null): array
    {
        $q = DB::table('students')
            ->selectRaw('
                students.*,
                u.firstname AS parent_firstname,
                u.lastname  AS parent_lastname,
                u.email     AS parent_email,
                u.cellphone AS parent_phone,
                MIN(ec.emergency_contact_name) AS emergency_name,
                MIN(ec.relation)               AS emergency_relationship,
                MIN(ec.cellphone)              AS emergency_phone
            ')
            ->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
            ->leftJoin('emergency_contacts as ec', 'ec.parent_id', '=', 'students.parent_id');

        // is_new filter
        if ($isNew === 0 || $isNew === 1) {
            $q->where('students.is_new', $isNew);
        } else {
            $q->whereIn('students.is_new', [0, 1]);
        }

        // school_year filter (skip if null or "all")
        if ($schoolYear !== null && strtolower($schoolYear) !== 'all') {
            $q->where('students.school_year', $schoolYear);
        }

        return $q->groupBy('students.id')
            ->orderByDesc('students.is_new') // new first
            ->orderBy('students.lastname', 'asc')
            ->orderBy('students.firstname', 'asc')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /** CI: getStudentSchoolIdByStudentId() */
    public static function getStudentSchoolIdByStudentId(int $studentId): ?string
    {
        $v = static::query()->where('id', $studentId)->value('school_id');
        return $v !== null ? (string) $v : null;
    }

    /** CI: getAllStudents() */
    public static function getAllStudents()
    {
        return static::query()->get();
    }

    /** CI: getTotalStudents() */
    public static function getTotalStudents(): int
    {
        return (int) static::query()->count();
    }

    /** CI: getFullNameById() => returns [firstname, lastname] or null */
    public static function getFullNameById($studentId): ?array
    {
        $student = static::query()->find($studentId);
        if (!$student) return null;

        $first = (string) ($student->firstname ?? '');
        $last  = (string) ($student->lastname ?? '');

        if ($first === '' && $last === '') return null;

        return ['firstname' => $first, 'lastname' => $last];
    }

    /** CI: getFullName($student array) */
    public static function getFullName(array $student): string
    {
        return trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? ''));
    }

    /** CI: getLastRegistrationDate($ParentId) */
    public static function getLastRegistrationDate(int $parentId): ?array
    {
        $row = static::query()
            ->select('registration_date')
            ->where('parent_id', $parentId)
            ->orderByDesc('registration_date')
            ->first();

        return $row ? ['registration_date' => $row->registration_date] : null;
    }

    /**
     * CI: getByClassAndYear($class_section_id, $school_year, ?semester ignored)
     */
    public static function getByClassAndYear(int $classSectionId, string $schoolYear, ?string $semester = null): array
    {
        $studentIds = StudentClass::query()
            ->select('student_id')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->pluck('student_id')
            ->all();

        if (empty($studentIds)) return [];

        return static::query()
            ->whereIn('id', $studentIds)
            ->where('is_active', 1)
            ->orderBy('firstname', 'asc')
            ->orderBy('lastname', 'asc')
            ->get()
            ->all();
    }

    public static function getSchoolIdByStudentId($studentId)
    {
        return static::query()->where('id', $studentId)->value('school_id');
    }

    public static function getSchoolIdsByParentId(int $parentId): array
    {
        return static::query()
            ->where('parent_id', $parentId)
            ->distinct()
            ->pluck('school_id')
            ->filter(fn ($v) => $v !== null && $v !== '')
            ->values()
            ->all();
    }

    public static function getStudentIdsByParentId(int $parentId): array
    {
        return static::query()
            ->where('parent_id', $parentId)
            ->distinct()
            ->pluck('id')
            ->map(fn ($v) => (int) $v)
            ->values()
            ->all();
    }

    /**
     * CI: getStudentsWithClassAndEnrollment()
     * Keeps your original grouping so behavior doesn’t change.
     */
    public static function getStudentsWithClassAndEnrollment(): array
    {
        return DB::table('students')
            ->selectRaw('
                students.*,
                student_class.class_section_id,
                enrollments.enrollment_status,
                enrollments.admission_status,
                u.firstname AS parent_firstname,
                u.lastname  AS parent_lastname
            ')
            ->leftJoin('student_class', 'student_class.student_id', '=', 'students.id')
            ->leftJoin('enrollments', 'enrollments.student_id', '=', 'students.id')
            ->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
            ->groupByRaw('students.id, student_class.class_section_id, enrollments.enrollment_status, enrollments.admission_status, u.firstname, u.lastname')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /**
     * CI: getStudentInfoByClassSectionId($classSectionId, $semester ignored, $schoolYear optional)
     * NOTE: In CI you appended updated_by from session; here we accept actor id.
     */
    public static function getStudentInfoByClassSectionId(int $classSectionId, ?string $semester = null, ?string $schoolYear = null, ?int $actorId = null): array
    {
        if ($classSectionId <= 0) return [];

        $q = DB::table('students')
            ->selectRaw('students.id AS student_id, students.school_id, students.firstname, students.lastname, sc.class_section_id')
            ->join('student_class as sc', 'sc.student_id', '=', 'students.id')
            ->where('sc.class_section_id', $classSectionId)
            ->where('students.is_active', 1)
            ->distinct()
            ->orderBy('students.lastname', 'asc')
            ->orderBy('students.firstname', 'asc');

        if ($schoolYear !== null) {
            $q->where('sc.school_year', $schoolYear);
        }

        $rows = $q->get()->map(fn ($r) => (array) $r)->all();

        $actorId = $actorId ?? (auth()->id() ?? 0);
        foreach ($rows as &$row) {
            $row['updated_by'] = (int) $actorId;
        }
        unset($row);

        return $rows;
    }

    /* ============================================================
     * Optional validation helper
     * ============================================================
     */
    public static function rules(bool $updating = false): array
    {
        $req = $updating ? 'sometimes' : 'required';

        return [
            'school_id'           => ['nullable', 'integer', 'min:1'],
            'firstname'           => [$req, 'string', 'max:120'],
            'lastname'            => [$req, 'string', 'max:120'],
            'dob'                 => ['nullable', 'date'],
            'age'                 => ['nullable', 'integer', 'min:0'],
            'gender'              => ['nullable', 'string', 'max:20'],
            'registration_grade'  => ['nullable', 'string', 'max:50'],
            'photo_consent'       => ['nullable', 'boolean'],
            'is_new'              => ['nullable', 'boolean'],
            'parent_id'           => ['nullable', 'integer'],
            'school_year'         => ['nullable', 'string', 'max:20'],
            'registration_date'   => ['nullable', 'date'],
            'tuition_paid'        => ['nullable', 'boolean'],
            'year_of_registration'=> ['nullable', 'integer', 'min:0'],
            'rfid_tag'            => ['nullable', 'string', 'max:120'],
            'is_active'           => ['nullable', 'boolean'],
        ];
    }
}
