<?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 StaffAttendance extends BaseModel
{
    protected $table = 'staff_attendance';

    public $timestamps = true;

    /**
     * IMPORTANT:
     * Your earlier comment said only these columns exist.
     * But later methods use class_section_id + position.
     *
     * ✅ If class_section_id/position DO exist in your DB, add them to fillable.
     * ❌ If they do NOT exist, remove them and also remove related methods/scopes.
     */
    protected $fillable = [
        'user_id',
        'role_name',
        'date',
        'semester',
        'school_year',
        'status',
        'reason',
        'created_at',
        'updated_at',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'user_id'          => 'integer',
        'class_section_id' => 'integer',
        'created_by'       => 'integer',
        'updated_by'       => 'integer',
        'date'             => 'date:Y-m-d',
        'created_at'       => 'datetime',
        'updated_at'       => 'datetime',
    ];

    /* ============================================================
     * Constants
     * ============================================================
     */
    public const STATUS_PRESENT = 'present';
    public const STATUS_ABSENT  = 'absent';
    public const STATUS_LATE    = 'late';

    public const POS_MAIN = 'main';
    public const POS_TA   = 'ta';

    public static function allowedStatuses(): array
    {
        return [self::STATUS_PRESENT, self::STATUS_ABSENT, self::STATUS_LATE];
    }

    /* ============================================================
     * Relationships (optional)
     * ============================================================
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

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

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

    public function classSection(): BelongsTo
    {
        return $this->belongsTo(ClassSection::class, 'class_section_id');
    }

    /* ============================================================
     * Scopes
     * ============================================================
     */
    public function scopeForDay(Builder $q, string $date): Builder
    {
        return $q->whereDate('date', $date);
    }

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

    public function scopeForUser(Builder $q, int $userId): Builder
    {
        return $q->where('user_id', $userId);
    }

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

    /* ============================================================
     * Helpers
     * ============================================================
     */
    protected static function normStatus(?string $status): ?string
    {
        $s = strtolower(trim((string) $status));
        return in_array($s, self::allowedStatuses(), true) ? $s : null;
    }

    protected static function isValidDate(string $d): bool
    {
        $dt = date_create_from_format('Y-m-d', $d);
        return $dt && $dt->format('Y-m-d') === $d;
    }

    /* ============================================================
     * Upserts (Laravel style)
     * ============================================================
     */

    /**
     * Upsert by (user_id, date, semester, school_year)
     * Returns saved model or null if invalid.
     */
    public static function upsertOne(
        int $userId,
        ?string $roleName,
        string $date,
        string $semester,
        string $schoolYear,
        string $status,
        ?string $reason,
        ?int $editorId = null
    ): ?self {
        if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') {
            return null;
        }

        $norm = self::normStatus($status);
        if ($norm === null) {
            return null;
        }

        $editorId = $editorId ?: (auth()->id() ?? null);

        $keys = [
            'user_id'     => $userId,
            'date'        => $date,
            'semester'    => $semester,
            'school_year' => $schoolYear,
        ];

        $payload = [
            'role_name'   => $roleName,
            'status'      => $norm,
            'reason'      => $reason ?: null,
            'updated_by'  => $editorId,
        ];

        // Ensure created_by is set on create
        return static::query()->updateOrCreate($keys, $payload + [
            'created_by' => $editorId,
        ]);
    }

    /**
     * Upsert a single cell by (user_id, date).
     * - If status empty/invalid => delete record for that day (if exists) and return true.
     * - Otherwise upsert.
     */
    public static function upsertCell(
        int $userId,
        string $date,
        ?string $status,
        string $semester,
        string $schoolYear,
        ?string $roleName = null,
        ?int $classSectionId = null,
        ?string $position = null,
        ?string $reason = null,
        ?int $editorId = null
    ): bool {
        if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') {
            return false;
        }

        $editorId = $editorId ?: (auth()->id() ?? null);

        $existing = static::query()
            ->where('user_id', $userId)
            ->whereDate('date', $date)
            ->first();

        $norm = self::normStatus($status);

        // DELETE if status cleared
        if ($norm === null) {
            if ($existing) {
                return (bool) $existing->delete();
            }
            return true;
        }

        $keys = [
            'user_id'     => $userId,
            'date'        => $date,
            'semester'    => $semester,
            'school_year' => $schoolYear,
        ];

        $payload = [
            'role_name'        => $roleName ?? ($existing->role_name ?? null),
            'class_section_id' => $classSectionId,
            'position'         => $position,
            'status'           => $norm,
            'reason'           => $reason,
            'updated_by'       => $editorId,
        ];

        static::query()->updateOrCreate($keys, $payload + ['created_by' => $editorId]);
        return true;
    }

    public static function getOne(int $userId, string $date): ?self
    {
        return static::query()->where('user_id', $userId)->whereDate('date', $date)->first();
    }

    public static function deleteOne(int $userId, string $date): bool
    {
        $row = static::getOne($userId, $date);
        return $row ? (bool) $row->delete() : true;
    }

    /* ============================================================
     * Staff list with status for a day (users + roles + staff_attendance)
     * Mirrors your CI staffWithStatusForDay()
     * ============================================================
     */
    public static function staffWithStatusForDay(
        string $date,
        string $semester,
        string $schoolYear,
        array $excludeUserIds = []
    ): array {
        // Subquery: pick one stable role name per user (MIN(name))
        $roleSub = DB::table('user_roles as ur')
            ->selectRaw('ur.user_id, MIN(r.name) as role_name')
            ->leftJoin('roles as r', 'r.id', '=', 'ur.role_id')
            ->groupBy('ur.user_id');

        $q = DB::table('users as u')
            ->selectRaw("
                u.id as user_id,
                u.firstname,
                u.lastname,
                COALESCE(rs.role_name, '') as role_name,
                sa.id as satt_id,
                sa.status,
                sa.reason
            ")
            ->leftJoinSub($roleSub, 'rs', fn ($j) => $j->on('rs.user_id', '=', 'u.id'))
            ->leftJoin('staff_attendance as sa', function ($j) use ($date, $semester, $schoolYear) {
                $j->on('sa.user_id', '=', 'u.id')
                  ->whereDate('sa.date', '=', $date)
                  ->where('sa.semester', '=', $semester)
                  ->where('sa.school_year', '=', $schoolYear);
            })
            ->whereNotIn(DB::raw('LOWER(COALESCE(rs.role_name,""))'), ['parent', 'guest'])
            ->orderBy('u.firstname')
            ->orderBy('u.lastname');

        if (!empty($excludeUserIds)) {
            $q->whereNotIn('u.id', array_values(array_map('intval', $excludeUserIds)));
        }

        return $q->get()->map(function ($r) {
            return [
                'user_id'    => (int) $r->user_id,
                'firstname'  => (string) ($r->firstname ?? ''),
                'lastname'   => (string) ($r->lastname ?? ''),
                'role_name'  => (string) ($r->role_name ?? ''),
                'satt_id'    => $r->satt_id ? (int) $r->satt_id : null,
                'status'     => $r->status ? (string) $r->status : null,
                'reason'     => $r->reason,
            ];
        })->all();
    }

    /* ============================================================
     * Teacher/TA helpers (teacher_class-backed)
     * ============================================================
     */

    public static function getAssignedTeachersForSection(int $classSectionId, string $schoolYear): array
    {
        return DB::table('teacher_class as tc')
            ->selectRaw('tc.class_section_id, tc.teacher_id as teacher_id, tc.position, u.firstname, u.lastname')
            ->join('users as u', 'u.id', '=', 'tc.teacher_id')
            ->where('tc.class_section_id', $classSectionId)
            ->where('tc.school_year', $schoolYear)
            ->orderByRaw("FIELD(tc.position,'main','ta')")
            ->orderBy('u.firstname')
            ->get()
            ->map(fn ($r) => [
                'class_section_id' => (int) $r->class_section_id,
                'teacher_id'       => (int) $r->teacher_id,
                'position'         => (string) $r->position,
                'firstname'        => (string) ($r->firstname ?? ''),
                'lastname'         => (string) ($r->lastname ?? ''),
            ])->all();
    }

    public static function getStatusForDay(
        int $teacherId,
        int $classSectionId,
        string $date,
        string $semester,
        string $schoolYear
    ): ?array {
        $row = static::query()
            ->where('user_id', $teacherId)
            ->where('class_section_id', $classSectionId)
            ->whereDate('date', $date)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->first();

        return $row ? $row->toArray() : null;
    }

    public static function assignedWithStatusForDay(
        int $classSectionId,
        string $date,
        string $semester,
        string $schoolYear
    ): array {
        return DB::table('teacher_class as tc')
            ->selectRaw("
                tc.class_section_id,
                tc.teacher_id as teacher_id,
                tc.position,
                u.firstname, u.lastname,
                t.id as satt_id, t.status, t.reason
            ")
            ->join('users as u', 'u.id', '=', 'tc.teacher_id')
            ->leftJoin('staff_attendance as t', function ($j) use ($date, $semester, $schoolYear) {
                $j->on('t.user_id', '=', 'tc.teacher_id')
                  ->on('t.class_section_id', '=', 'tc.class_section_id')
                  ->whereDate('t.date', '=', $date)
                  ->where('t.semester', '=', $semester)
                  ->where('t.school_year', '=', $schoolYear);
            })
            ->where('tc.class_section_id', $classSectionId)
            ->where('tc.school_year', $schoolYear)
            ->orderByRaw("FIELD(tc.position,'main','ta')")
            ->orderBy('u.firstname')
            ->get()
            ->map(fn ($r) => [
                'class_section_id' => (int) $r->class_section_id,
                'teacher_id'       => (int) $r->teacher_id,
                'position'         => (string) $r->position,
                'firstname'        => (string) ($r->firstname ?? ''),
                'lastname'         => (string) ($r->lastname ?? ''),
                'satt_id'          => $r->satt_id ? (int) $r->satt_id : null,
                'status'           => $r->status ? strtolower((string) $r->status) : null,
                'reason'           => $r->reason,
            ])->all();
    }

    public static function allAssignedStatusesPresent(
        int $classSectionId,
        string $date,
        string $semester,
        string $schoolYear
    ): bool {
        $assigned = (int) DB::table('teacher_class')
            ->where('class_section_id', $classSectionId)
            ->where('school_year', $schoolYear)
            ->count();

        if ($assigned <= 0) return true;

        $present = (int) DB::table('staff_attendance')
            ->where('class_section_id', $classSectionId)
            ->whereDate('date', $date)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->distinct('user_id')
            ->count('user_id');

        return $present === $assigned;
    }

    /**
     * Statuses for a range by section codes.
     * Returns: [sectionCode][teacherId][Y-m-d] = ['status'=>..,'reason'=>..]
     */
    public static function statusesByRange(
        array $sectionCodes,
        string $startDate,
        string $endDate,
        string $semester,
        string $schoolYear
    ): array {
        $codes = array_values(array_unique(array_filter(array_map('intval', $sectionCodes))));
        if (empty($codes)) return [];

        $rows = DB::table('staff_attendance')
            ->selectRaw('class_section_id, user_id, DATE_FORMAT(date,"%Y-%m-%d") as d, status, reason')
            ->whereIn('class_section_id', $codes)
            ->whereDate('date', '>=', $startDate)
            ->whereDate('date', '<=', $endDate)
            ->where('semester', $semester)
            ->where('school_year', $schoolYear)
            ->get();

        $map = [];
        foreach ($rows as $r) {
            $sid = (int) $r->class_section_id;
            $tid = (int) $r->user_id;
            $day = (string) $r->d;

            $map[$sid][$tid][$day] = [
                'status' => strtolower((string) $r->status),
                'reason' => $r->reason,
            ];
        }
        return $map;
    }

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

        return [
            'user_id'     => [$req, 'integer', 'min:1', 'exists:users,id'],
            'date'        => [$req, 'date_format:Y-m-d'],
            'semester'    => [$req, 'string', 'max:20'],
            'school_year' => [$req, 'string', 'max:20'],

            'role_name'   => ['nullable', 'string', 'max:80'],
            'status'      => ['nullable', 'string', 'in:' . implode(',', self::allowedStatuses())],
            'reason'      => ['nullable', 'string', 'max:2000'],

            'class_section_id' => ['nullable', 'integer'],
            'position'         => ['nullable', 'string', 'in:' . implode(',', [self::POS_MAIN, self::POS_TA])],

            'created_by'  => ['nullable', 'integer'],
            'updated_by'  => ['nullable', 'integer'],
        ];
    }
}