<?php

namespace App\Models;

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

class TeacherClass extends BaseModel
{
    protected $table = 'teacher_class';

    protected $fillable = [
        'class_section_id',
        'teacher_id',
        'position',
        'semester',
        'school_year',
        'description',
        'created_at',
        'updated_at',
        'updated_by',
    ];

    public $timestamps = true;

    protected $casts = [
        'teacher_id'       => 'integer',
        'class_section_id' => 'integer',
        'updated_by'       => 'integer',
        'created_at'       => 'datetime',
        'updated_at'       => 'datetime',
    ];

   
    /* ============================================================
     * Constants
     * ============================================================
     */
    public const POS_MAIN = 'main';
    public const POS_TA   = 'ta';

    public static function allowedPositions(): array
    {
        return [self::POS_MAIN, self::POS_TA];
    }

    
    /* ============================================================
     * Relationships (optional)
     * ============================================================
     */
    public function teacher(): BelongsTo
    {
        // your teachers are users in most of your joins
        return $this->belongsTo(User::class, 'teacher_id');
    }

    public function classSection(): BelongsTo
    {
        // CI frequently joins on classSection.class_section_id
        return $this->belongsTo(ClassSection::class, 'class_section_id', 'class_section_id');
    }

    public function updater(): BelongsTo
    {
        return $this->belongsTo(User::class, 'updated_by'); // change to Admin if needed
    }

    /* ============================================================
     * Scopes
     * ============================================================
     */
    public function scopeForTeacher(Builder $q, int $teacherId): Builder
    {
        return $q->where('teacher_id', $teacherId);
    }

    public function scopeForSchoolYear(Builder $q, string $schoolYear): Builder
    {
        return $q->where('school_year', $schoolYear);
    }

    public function scopeForSectionCode(Builder $q, int $sectionCode): Builder
    {
        return $q->where('class_section_id', $sectionCode);
    }

    public function scopeMain(Builder $q): Builder
    {
        return $q->where('position', self::POS_MAIN);
    }

    public function scopeTa(Builder $q): Builder
    {
        return $q->where('position', self::POS_TA);
    }

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

    /** CI: getClassSectionIdByUserId() */
    public static function getClassSectionIdByUserId(int $userId): ?self
    {
        return static::query()->forTeacher($userId)->first();
    }

    /**
     * CI: getClassAssignmentsByUserId()
     * Returns array rows with:
     * class_section_pk, class_section_id, class_section_name, class_id, class_name, teacher_id, teacher_role, school_year, semester
     */
    public static function getClassAssignmentsByUserId(int $userId, ?string $schoolYear = null, ?string $semester = null): array
    {
        $q = DB::table('teacher_class as tc')
            ->selectRaw('
                cs.class_section_name,
                cs.id AS class_section_pk,
                cs.class_section_id,
                c.id AS class_id,
                c.class_name,
                tc.teacher_id,
                tc.position,
                tc.school_year
            ')
            ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id')
            ->leftJoin('classes as c', 'cs.class_id', '=', 'c.id')
            ->where('tc.teacher_id', $userId)
            ->whereNotNull('tc.class_section_id')
            ->orderBy('cs.class_section_name', 'asc');

        if ($schoolYear) {
            $q->where('tc.school_year', $schoolYear);
        }

        $rows = $q->get();

        return $rows->map(function ($r) use ($semester) {
            return [
                'class_section_pk'   => isset($r->class_section_pk) ? (int) $r->class_section_pk : null,
                'class_section_id'   => (int) $r->class_section_id,
                'class_section_name' => (string) $r->class_section_name,
                'class_id'           => isset($r->class_id) ? (int) $r->class_id : null,
                'class_name'         => $r->class_name ?? null,
                'teacher_id'         => (int) $r->teacher_id,
                'teacher_role'       => ucfirst((string) $r->position), // Main / Ta
                'school_year'        => (string) $r->school_year,
                'semester'           => $semester,
            ];
        })->all();
    }

    /** CI: getClassSectionsByTeacherId() */
    public static function getClassSectionsByTeacherId(int $teacherId): array
    {
        $rows = DB::table('teacher_class as tc')
            ->selectRaw('cs.class_section_name, tc.teacher_id, tc.position')
            ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id')
            ->where('tc.teacher_id', $teacherId)
            ->whereNotNull('tc.class_section_id')
            ->get();

        if ($rows->isEmpty()) {
            return ['role' => '', 'class_section_name' => 'No class assigned'];
        }

        $sectionNames = $rows->pluck('class_section_name')->filter()->all();
        $position = (string) ($rows[0]->position ?? '');

        return [
            'role' => ucfirst($position),
            'class_section_name' => implode(', ', $sectionNames),
        ];
    }

    /** CI: getClassByTeacherId() */
    public static function getClassByTeacherId(int $teacherId): array
    {
        return DB::table('teacher_class as tc')
            ->selectRaw('cs.class_section_id, cs.class_section_name')
            ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id')
            ->where('tc.teacher_id', $teacherId)
            ->whereNotNull('tc.class_section_id')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /** CI: getTeacherIdByClassSection($classSectionName, $semester, $schoolYear) */
    public static function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear): ?int
    {
        $classSectionName = trim($classSectionName);
        if (str_contains($classSectionName, ',')) {
            $parts = array_values(array_filter(array_map('trim', explode(',', $classSectionName))));
            if (!empty($parts)) $classSectionName = $parts[0];
        }
        if ($classSectionName === '') return null;

        $sectionCode = DB::table('classSection')
            ->where('class_section_name', $classSectionName)
            ->value('class_section_id');

        if (!$sectionCode) return null;

        $teacherId = DB::table('teacher_class')
            ->where('class_section_id', $sectionCode)
            ->where('school_year', $schoolYear)
            ->where('position', self::POS_MAIN)
            ->value('teacher_id');

        return $teacherId ? (int) $teacherId : null;
    }

    /** CI: getMainTeacherBySection($classSectionName, $semester, $schoolYear) */
    public static function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array
    {
        $row = DB::table('classSection as cs')
            ->selectRaw('u.id AS teacher_id, u.firstname, u.lastname')
            // IMPORTANT: your CI code had tc.class_section_id = cs.id in one place (PK join),
            // but most of your code uses class_section_id code join.
            // Here we keep the "code join" for consistency:
            ->join('teacher_class as tc', 'tc.class_section_id', '=', 'cs.class_section_id')
            ->join('users as u', 'u.id', '=', 'tc.teacher_id')
            ->where('cs.class_section_name', $classSectionName)
            ->where('tc.school_year', $schoolYear)
            ->where('tc.position', self::POS_MAIN)
            ->first();

        return $row ? (array) $row : null;
    }

    /**
     * CI: assignTeacherToClass()
     * Upsert by (teacher_id, class_section_id, school_year, position) and touch updated_at.
     */
    public static function assignTeacherToClass(
        int $teacherId,
        int $sectionCode,
        string $semester,
        string $schoolYear,
        string $position = self::POS_MAIN,
        ?int $updatedBy = null
    ): bool {
        $position = strtolower(trim($position));
        if (!in_array($position, self::allowedPositions(), true)) {
            throw new \InvalidArgumentException('Position must be either "main" or "ta".');
        }

        $updatedBy = $updatedBy ?? (auth()->id() ?? null);

        static::query()->updateOrCreate(
            [
                'teacher_id'       => $teacherId,
                'class_section_id' => $sectionCode,
                'school_year'      => $schoolYear,
                'position'         => $position,
            ],
            [
                'updated_by' => $updatedBy,
                'updated_at' => now(),
            ]
        );

        return true;
    }

    /**
     * CI: getAssignedClassSections()
     * NOTE: your CI joins classSection.id = teacher_class.class_section_id (PK join).
     * If your teacher_class.class_section_id is actually a section CODE, change join to cs.class_section_id.
     */
    public static function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester): array
    {
        return DB::table('teacher_class as tc')
            ->selectRaw('tc.class_section_id, tc.position, cs.class_section_name')
            ->join('classSection as cs', 'cs.class_section_id', '=', 'tc.class_section_id')
            ->where('tc.teacher_id', $teacherId)
            ->where('tc.school_year', $schoolYear)
            ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /**
     * CI: assignedForSectionTerm()
     * Returns rows: class_section_id, teacher_id, position, firstname, lastname
     */
    public static function assignedForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
    {
        return DB::table('teacher_class as tc')
            ->selectRaw('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
            ->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
            ->where('tc.class_section_id', $sectionCode)
            ->where('tc.school_year', $schoolYear)
            ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
            ->orderBy('u.firstname', 'asc')
            ->get()
            ->map(fn ($r) => (array) $r)
            ->all();
    }

    /** CI: assignedMapForSectionTerm() */
    public static function assignedMapForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
    {
        $out = [];
        foreach (static::assignedForSectionTerm($sectionCode, $semester, $schoolYear) as $r) {
            $out[(int) $r['teacher_id']] = $r;
        }
        return $out;
    }

    /**
     * CI: assignedBySectionForTerm()
     * Returns: [sectionCode] => [rows...]
     */
    public static function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array
    {
        $q = DB::table('teacher_class as tc')
            ->selectRaw('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
            ->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
            ->where('tc.school_year', $schoolYear)
            ->orderBy('tc.class_section_id', 'asc')
            ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
            ->orderBy('u.firstname', 'asc');

        if (!empty($onlySectionCodes)) {
            $ids = array_values(array_unique(array_filter(array_map('intval', $onlySectionCodes))));
            if (!empty($ids)) $q->whereIn('tc.class_section_id', $ids);
        }

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

        $out = [];
        foreach ($rows as $r) {
            $code = (int) ($r['class_section_id'] ?? 0);
            if ($code <= 0) continue;
            $out[$code][] = $r;
        }

        return $out;
    }

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

        return [
            'teacher_id'       => [$req, 'integer', 'min:1', 'exists:users,id'],
            'class_section_id' => [$req, 'integer', 'min:1'],
            'school_year'      => [$req, 'string', 'max:9'],
            'position'         => [$req, 'string', 'in:' . implode(',', self::allowedPositions())],
            'updated_by'       => ['nullable', 'integer'],
        ];
    }
}
