<?php

namespace App\Models;

use CodeIgniter\Model;

class TeacherClassModel extends Model
{
    protected $table = 'teacher_class';
    protected $primaryKey = 'id';
    protected $allowedFields = [
        'teacher_id',
        'class_section_id',
                'school_year',
        'position', // NEW
        'updated_by',
        'updated_at',
        'created_at'
    ];

    protected $useTimestamps = true;
    protected $createdField = 'created_at';
    protected $updatedField = 'updated_at';
    protected $returnType = 'array';
    protected $useSoftDeletes = false;
    protected $skipValidation = false;
    /** Request-lifetime memo cache */
    protected array $assignedCache = [];
    protected array $assignedBySectionCache = [];

    protected $validationRules = [
        'teacher_id' => 'required|integer',
        'class_section_id' => 'required|integer',
        'school_year' => 'required|string|max_length[9]',
        'position' => 'required|in_list[main,ta]',
    ];

    protected $validationMessages = [
        'teacher_id' => [
            'required' => 'The teacher ID is required.',
            'integer'  => 'The teacher ID must be an integer.',
        ],
        'class_section_id' => [
            'required' => 'The class section ID is required.',
            'integer'  => 'The class section ID must be an integer.',
        ],
        'school_year' => [
            'required' => 'The school year is required.',
            'max_length' => 'The school year cannot exceed 9 characters.',
        ],
        'position' => [
            'required' => 'The position (main or ta) is required.',
            'in_list' => 'The position must be either "main" or "ta".',
        ],
    ];

    public function getClassSectionIdByUserId($user_id)
    {
        $result = $this->where('teacher_id', $user_id)->first();

        if (!$result) {
            log_message('error', "No class section found for user ID: $user_id");
            return null;
        }

        return $result;
    }

    public function getClassAssignmentsByUserId(int $userId, ?string $schoolYear = null, ?string $semester = null): array
    {
        $hasSectionSchoolYear = $this->db->fieldExists('school_year', 'classSection');
        $sectionJoin = 'tc.class_section_id = cs.class_section_id';
        if ($schoolYear && $hasSectionSchoolYear) {
            $sectionJoin .= ' AND cs.school_year = ' . $this->db->escape($schoolYear);
        }

        $builder = $this->db->table('teacher_class tc')
            ->select([
                'tc.class_section_id AS teacher_class_section_id',
                'cs.class_section_name',
                'cs.class_section_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 cs', $sectionJoin, 'left', false)
            ->join('classes c', 'cs.class_id = c.id', 'left')
            ->where('tc.teacher_id', $userId)
            ->where('tc.class_section_id IS NOT NULL', null, false)
            ->orderBy('COALESCE(cs.class_section_name, tc.class_section_id)', 'ASC', false);

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

        $rows = $builder->get()->getResultArray();

        $out = [];
        foreach ($rows as $r) {
            $sectionId = (int) ($r['teacher_class_section_id'] ?? $r['class_section_id'] ?? 0);
            $sectionName = $r['class_section_name']
                ?? $this->fallbackClassSectionName($sectionId, $schoolYear);

            $out[] = [
                'class_section_pk'   => (int)($r['class_section_pk'] ?? $sectionId),
                'class_section_id'   => $sectionId,
                'class_section_name' => $sectionName,
                '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($r['position']), // "Main" or "Ta"
                'school_year'        => $r['school_year'],
                'semester'           => $semester,
            ];
        }

        return $out;
    }

    private function fallbackClassSectionName(int $classSectionId, ?string $schoolYear = null): string
    {
        if ($classSectionId <= 0) {
            return 'Class';
        }

        if ($schoolYear && $this->db->fieldExists('school_year', 'classSection')) {
            $row = $this->db->table('classSection')
                ->select('class_section_name')
                ->where('class_section_id', $classSectionId)
                ->where('school_year', $schoolYear)
                ->get()
                ->getRowArray();

            if (!empty($row['class_section_name'])) {
                return (string) $row['class_section_name'];
            }
        }

        $row = $this->db->table('classSection')
            ->select('class_section_name')
            ->where('class_section_id', $classSectionId)
            ->orderBy('id', 'DESC')
            ->get()
            ->getRowArray();

        return !empty($row['class_section_name'])
            ? (string) $row['class_section_name']
            : ('Class ' . $classSectionId);
    }

    public function getClassSectionsByTeacherId($teacherId)
    {
        $builder = $this->db->table('teacher_class')
            ->select('classSection.class_section_name, teacher_class.teacher_id, teacher_class.position')
            ->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id')
            ->where('teacher_class.teacher_id', $teacherId)
            ->where('teacher_class.class_section_id IS NOT NULL', null, false);

        $result = $builder->get()->getResultArray();

        if ($result) {
            $sectionNames = array_map(fn($row) => $row['class_section_name'], $result);
            $position = $result[0]['position'] ?? '';

            return [
                'role' => ucfirst($position),
                'class_section_name' => implode(', ', $sectionNames)
            ];
        } else {
            return ['role' => '', 'class_section_name' => 'No class assigned'];
        }
    }

    public function getClassByTeacherId($teacherId)
    {
        $builder = $this->db->table('teacher_class')
            ->select('classSection.class_section_id, classSection.class_section_name')
            ->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id')
            ->where('teacher_class.teacher_id', $teacherId)
            ->where('teacher_class.class_section_id IS NOT NULL', null, false);

        return $builder->get()->getResultArray() ?? [];
    }

    public function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear)
    {
        $db = \Config\Database::connect();

        $classSectionName = trim($classSectionName);
        if (strpos($classSectionName, ',') !== false) {
            $parts = array_filter(array_map('trim', explode(',', $classSectionName)));
            if (!empty($parts)) {
                $classSectionName = reset($parts); // pick the first section as primary
            }
        }
        if ($classSectionName === '') {
            return null;
        }

        $section = $db->table('classSection')
            ->select('class_section_id')
            ->where('class_section_name', $classSectionName)
            ->get()
            ->getRowArray();

        if (!$section) return null;

        $result = $db->table('teacher_class')
            ->select('teacher_id')
            ->where([
                'class_section_id' => $section['class_section_id'],
                'school_year' => $schoolYear,
                'position' => 'main'
            ])
            ->get()
            ->getRowArray();

        return $result['teacher_id'] ?? null;
    }

    public function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array
    {
        $db = \Config\Database::connect();

        // Adjust table/column names if yours differ (e.g., cs.class_section_id vs cs.id)
        $row = $db->table('classSection cs')
            ->select('u.id AS teacher_id, u.firstname, u.lastname')
            ->join('teacher_class tc', 'tc.class_section_id = cs.class_section_id', 'inner')
            ->join('users u', 'u.id = tc.teacher_id', 'inner')
            ->where('cs.class_section_name', $classSectionName)
            ->where('tc.school_year', $schoolYear)
            ->where('tc.position', 'main')
            ->get()
            ->getRowArray();

        return $row ?: null; // ['teacher_id' => ..., 'firstname' => ..., 'lastname' => ...] or null
    }

    /**
 * Return teacher + TAs for a class section in the current term.
 * Output:
 * [
 *   'teacher_id'   => int|null,
 *   'teacher_name' => string,        // 'N/A' if none found
 *   'tas'          => [ ['id'=>int,'name'=>string], ... ],
 * ]
 */
    public function getTeacherTABySection(int|string $classSectionId, string $semester, string $schoolYear): array
    {
        $db = \Config\Database::connect();

        // We can read everything straight from teacher_class; no need to join classSection.
        $rows = $db->table('teacher_class tc')
            ->select('u.id AS user_id, u.firstname, u.lastname, u.gender, tc.position')
            ->join('users u', 'u.id = tc.teacher_id', 'inner')
            ->where('tc.class_section_id', $classSectionId)
            ->where('tc.school_year', $schoolYear)
            // Prefer the most recent assignment and keep main/co-teachers ahead of assistants
            ->orderBy("CASE WHEN LOWER(tc.position) IN ('main','teacher','main teacher') THEN 0 ELSE 1 END", 'ASC', false)
            ->orderBy('COALESCE(tc.updated_at, tc.id)', 'DESC', false)
            ->orderBy('tc.id', 'DESC')
            ->get()
            ->getResultArray();

        $teacherId     = null;
        $teacherName   = 'N/A';
        $teacherNames  = [];
        $tas           = [];
        $firstSeen     = null; // fallback if no explicit "main"/"teacher"
        $taSeen        = [];
        $mainSeen      = [];

        $withPrefix = static function (array $row): string {
            $first = trim((string)($row['firstname'] ?? ''));
            $last  = trim((string)($row['lastname']  ?? ''));
            $gender = strtolower(trim((string)($row['gender'] ?? '')));
            $name = trim($first . ' ' . $last);
            if ($name === '') return '';
            $prefix = ($gender === 'female') ? 'Sr ' : (($gender === 'male') ? 'Br ' : '');
            return trim($prefix . $name);
        };

        foreach ($rows as $r) {
            $full = $withPrefix($r);
            if ($full === '') continue;

            if ($firstSeen === null) {
                $firstSeen = ['id' => (int)$r['user_id'], 'name' => $full];
            }

            $pos = strtolower((string)($r['position'] ?? ''));

            // Treat either 'main' or legacy 'teacher' as the main teacher role.
            if (in_array($pos, ['main', 'teacher', 'main teacher'], true)) {
                $uid = (int)$r['user_id'];
                if (!isset($mainSeen[$uid])) {
                    $teacherNames[] = $full;
                    $mainSeen[$uid] = true;
                }
                if ($teacherId === null) {
                    $teacherId   = $uid;
                    $teacherName = $full;
                }
                continue;
            }

            // Treat 'ta', 'assistant', 'teacher_assistant' (and similar) as TA roles.
            if (in_array($pos, ['ta', 'assistant', 'teacher_assistant'], true)) {
                $tid = (int)$r['user_id'];
                if (!isset($taSeen[$tid])) {
                    $tas[] = ['id' => $tid, 'name' => $full];
                    $taSeen[$tid] = true;
                }
                continue;
            }

            // Unknown/missing position: don't discard; we’ll use firstSeen as a fallback later.
        }

        // Fallback: if no explicit main teacher found, use the first record seen.
        if ($teacherId === null && $firstSeen !== null) {
            $teacherId   = $firstSeen['id'];
            $teacherName = $firstSeen['name'];
            $teacherNames[] = $firstSeen['name'];
        }

        // Ensure uniqueness/cleaning
        $teacherNames = array_values(array_unique(array_filter(array_map('trim', $teacherNames))));

        return [
            'teacher_id'    => $teacherId,
            'teacher_name'  => $teacherName,
            'teacher_names' => $teacherNames,
            'tas'           => $tas,
        ];
    }



    public function assignTeacherToClass($teacherId, $classSectionId, $semester, $schoolYear, $position = 'main')
    {
        if (!in_array($position, ['main', 'ta'])) {
            throw new \InvalidArgumentException('Position must be either "main" or "ta"');
        }

        $existing = $this->where([
            'teacher_id' => $teacherId,
            'class_section_id' => $classSectionId,
            'school_year' => $schoolYear,
            'position' => $position
        ])->first();

        if ($existing) {
            return $this->update($existing['id'], ['updated_at' => utc_now()]);
        }

        return $this->insert([
            'teacher_id' => $teacherId,
            'class_section_id' => $classSectionId,
            'school_year' => $schoolYear,
            'position' => $position,
            'created_at' => utc_now(),
            'updated_at' => utc_now()
        ]);
    }

    public function findAll($limit = 0, $offset = 0)
    {
        return parent::findAll($limit, $offset);
    }

    public function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester)
    {
        return $this->select('teacher_class.class_section_id, teacher_class.position, classSection.class_section_name')
            ->join('classSection', 'classSection.id = teacher_class.class_section_id')
            ->where('teacher_class.teacher_id', $teacherId)
            ->where('teacher_class.school_year', $schoolYear)
            ->findAll();
    }

    /**
     * Get assigned teachers (with names) for ONE section code + term.
     * Returns rows: class_section_id, teacher_id, position, firstname, lastname
     */
    public function assignedForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
    {
        $key = $sectionCode . '|' . $semester . '|' . $schoolYear;
        if (isset($this->assignedCache[$key])) {
            return $this->assignedCache[$key];
        }

        $rows = $this->db->table('teacher_class tc')
            ->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
            ->join('users u', 'u.id = tc.teacher_id', 'left')
            ->where('tc.class_section_id', $sectionCode)      // SECTION CODE
            ->where('tc.school_year', $schoolYear)
            ->orderBy("FIELD(tc.position, 'main','ta')", '', false) // main → ta
            ->orderBy('u.firstname', 'ASC')
            ->get()->getResultArray();

        return $this->assignedCache[$key] = $rows;
    }

    /**
     * Convenience: same as above but keyed by teacher_id.
     * Returns: [teacher_id] => row
     */
    public function assignedMapForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
    {
        $out = [];
        foreach ($this->assignedForSectionTerm($sectionCode, $semester, $schoolYear) as $r) {
            $out[(int)$r['teacher_id']] = $r;
        }
        return $out;
    }

    /**
     * Batch helper for a whole term (optionally filtered to some sections).
     * Returns: [sectionCode] => [rows...]
     */
    public function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array
    {
        $cacheKey = ($onlySectionCodes ? implode(',', array_map('intval', $onlySectionCodes)) : '*') . '|' . $semester . '|' . $schoolYear;
        if (isset($this->assignedBySectionCache[$cacheKey])) {
            return $this->assignedBySectionCache[$cacheKey];
        }

        $qb = $this->db->table('teacher_class tc')
            ->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
            ->join('users u', 'u.id = tc.teacher_id', 'left')
            ->where('tc.school_year', $schoolYear)
            ->orderBy('tc.class_section_id', 'ASC')
            ->orderBy("FIELD(tc.position, 'main','ta')", '', false)
            ->orderBy('u.firstname', 'ASC');

        if (!empty($onlySectionCodes)) {
            $qb->whereIn('tc.class_section_id', array_map('intval', $onlySectionCodes));
        }

        $rows = $qb->get()->getResultArray();

        $out = [];
        foreach ($rows as $r) {
            $code = (int)$r['class_section_id'];
            $out[$code][] = $r;
        }

        return $this->assignedBySectionCache[$cacheKey] = $out;
    }
}
