<?php

namespace App\Models;

use App\Enums\RecordStatus;
use App\Enums\StudyMode;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Programme extends Model
{
    use HasFactory;

    public const LEVEL_UNDERGRADUATE = 'undergraduate';
    public const LEVEL_DIPLOMA = 'diploma';
    public const LEVEL_CERTIFICATE = 'certificate';
    public const LEVEL_POSTGRADUATE = 'postgraduate';

    protected $fillable = [
        'school_id', 'department_id', 'name', 'slug', 'code', 'award', 'level',
        'description', 'overview', 'career_opportunities', 'duration', 'duration_unit',
        'study_modes', 'campuses', 'tuition_amount', 'application_fee', 'currency',
        'featured_image', 'is_featured', 'status', 'sort_order',
    ];

    protected function casts(): array
    {
        return [
            'study_modes' => 'array',
            'campuses' => 'array',
            'tuition_amount' => 'decimal:2',
            'application_fee' => 'decimal:2',
            'is_featured' => 'boolean',
            'status' => RecordStatus::class,
        ];
    }

    public function getRouteKeyName(): string
    {
        return 'slug';
    }

    // ---------------------------------------------------------------- Relations

    public function school(): BelongsTo
    {
        return $this->belongsTo(School::class);
    }

    public function department(): BelongsTo
    {
        return $this->belongsTo(Department::class);
    }

    public function requirements(): HasMany
    {
        return $this->hasMany(ProgrammeRequirement::class)->orderBy('sort_order');
    }

    public function intakes(): BelongsToMany
    {
        return $this->belongsToMany(Intake::class, 'intake_programmes')
            ->using(IntakeProgramme::class)
            ->withPivot(['available_slots', 'application_open', 'application_close', 'application_fee_override', 'status'])
            ->withTimestamps();
    }

    public function intakeProgrammes(): HasMany
    {
        return $this->hasMany(IntakeProgramme::class);
    }

    public function choices(): HasMany
    {
        return $this->hasMany(ApplicationChoice::class);
    }

    // ------------------------------------------------------------------ Scopes

    public function scopeActive(Builder $query): Builder
    {
        return $query->where('status', RecordStatus::Active);
    }

    public function scopeOrdered(Builder $query): Builder
    {
        return $query->orderBy('sort_order')->orderBy('name');
    }

    public function scopeLevel(Builder $query, ?string $level): Builder
    {
        return $level ? $query->where('level', $level) : $query;
    }

    public function scopeForSchool(Builder $query, int|string|null $school): Builder
    {
        return $school ? $query->where('school_id', $school) : $query;
    }

    public function scopeStudyMode(Builder $query, ?string $mode): Builder
    {
        return $mode ? $query->whereJsonContains('study_modes', $mode) : $query;
    }

    public function scopeCampus(Builder $query, ?string $campus): Builder
    {
        return $campus ? $query->whereJsonContains('campuses', $campus) : $query;
    }

    /** Indexed keyword search across the fields staff and visitors actually use (PRD 66). */
    public function scopeSearch(Builder $query, ?string $term): Builder
    {
        if (blank($term)) {
            return $query;
        }

        $term = trim($term);

        return $query->where(function (Builder $q) use ($term) {
            $q->where('name', 'like', "%{$term}%")
                ->orWhere('code', 'like', "%{$term}%")
                ->orWhere('award', 'like', "%{$term}%");
        });
    }

    /** Programmes open for application in a given intake. */
    public function scopeOpenForIntake(Builder $query, ?Intake $intake): Builder
    {
        if (! $intake) {
            return $query->whereRaw('1 = 0');
        }

        return $query->whereHas('intakeProgrammes', function (Builder $q) use ($intake) {
            $q->where('intake_id', $intake->id)->where('status', RecordStatus::Active);
        });
    }

    // ----------------------------------------------------------------- Helpers

    public function durationLabel(): string
    {
        $unit = $this->duration == 1
            ? rtrim($this->duration_unit, 's')
            : $this->duration_unit;

        return "{$this->duration} {$unit}";
    }

    public function studyModeLabel(): string
    {
        $modes = collect($this->study_modes ?? [])
            ->map(fn (string $m) => StudyMode::tryFrom($m)?->label() ?? ucfirst($m));

        return $modes->isEmpty() ? 'Day' : $modes->implode(' / ');
    }

    public function campusLabel(): string
    {
        return collect($this->campuses ?? [])->implode(', ') ?: 'Bunga Campus';
    }

    public function levelLabel(): string
    {
        return ucfirst($this->level);
    }

    /** Application fee for this programme, falling back to the platform default. */
    public function applicationFee(): float
    {
        return (float) ($this->application_fee ?? setting('application_fee', 50000));
    }
}
