<?php

namespace App\Models;

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;

/**
 * TrainingSession Model
 *
 * Represents an individual training session/event within a training package.
 * Contains scheduling, venue, and meeting link information.
 *
 * @property int $id
 * @property int $training_package_id
 * @property string $session_name
 * @property string|null $description
 * @property int $session_order
 * @property \Illuminate\Support\Carbon $session_date
 * @property string $start_time
 * @property string $end_time
 * @property string $session_type
 * @property string|null $venue_name
 * @property string|null $venue_address
 * @property string|null $venue_city
 * @property string $venue_country
 * @property string|null $meeting_link
 * @property string|null $meeting_id
 * @property string|null $meeting_password
 * @property string|null $meeting_provider
 * @property int|null $max_participants
 * @property string $status
 * @property string|null $cancellation_reason
 * @property \Illuminate\Support\Carbon|null $new_date
 * @property array|null $materials
 * @property string|null $recording_url
 * @property int|null $created_by
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 */
class TrainingSession extends Model
{
    use HasFactory;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'training_sessions';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'training_package_id',
        'session_name',
        'description',
        'session_order',
        'session_date',
        'start_time',
        'end_time',
        'session_type',
        'venue_name',
        'venue_address',
        'venue_city',
        'venue_country',
        'meeting_link',
        'meeting_id',
        'meeting_password',
        'meeting_provider',
        'max_participants',
        'status',
        'cancellation_reason',
        'new_date',
        'materials',
        'recording_url',
        'created_by',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'session_date' => 'date',
        'new_date' => 'date',
        'start_time' => 'datetime:H:i:s',
        'end_time' => 'datetime:H:i:s',
        'materials' => 'array',
        'session_order' => 'integer',
        'max_participants' => 'integer',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];

    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'session_type' => 'in_person',
        'status' => 'scheduled',
        'session_order' => 1,
        'venue_country' => 'Uganda',
    ];

    // =========================================================================
    // RELATIONSHIPS
    // =========================================================================

    /**
     * Get the training package this session belongs to.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function trainingPackage(): BelongsTo
    {
        return $this->belongsTo(TrainingPackage::class, 'training_package_id');
    }

    /**
     * Get the user who created this session.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Get all trainers assigned to this session.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function trainers(): BelongsToMany
    {
        return $this->belongsToMany(User::class, 'training_session_trainers', 'training_session_id', 'trainer_id')
            ->withPivot(['role', 'topic', 'notes', 'compensation_amount', 'confirmation_status', 'confirmed_at'])
            ->withTimestamps();
    }

    /**
     * Get lead trainers for this session.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function leadTrainers()
    {
        return $this->trainers()->wherePivot('role', 'lead');
    }

    /**
     * Get assistant trainers for this session.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function assistantTrainers()
    {
        return $this->trainers()->wherePivot('role', 'assistant');
    }

    /**
     * Get all attendance records for this session.
     *
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function attendances(): HasMany
    {
        return $this->hasMany(TrainingAttendance::class, 'training_session_id');
    }

    // =========================================================================
    // QUERY SCOPES
    // =========================================================================

    /**
     * Scope to get sessions for a specific date.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $date
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOnDate($query, string $date)
    {
        return $query->where('session_date', $date);
    }

    /**
     * Scope to get upcoming sessions.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeUpcoming($query)
    {
        return $query->where('session_date', '>=', now()->toDateString())
            ->where('status', 'scheduled')
            ->orderBy('session_date')
            ->orderBy('start_time');
    }

    /**
     * Scope to get past sessions.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePast($query)
    {
        return $query->where('session_date', '<', now()->toDateString())
            ->orderByDesc('session_date')
            ->orderByDesc('start_time');
    }

    /**
     * Scope to get sessions by status.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $status
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeWithStatus($query, string $status)
    {
        return $query->where('status', $status);
    }

    /**
     * Scope to get sessions by type.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param string $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, string $type)
    {
        return $query->where('session_type', $type);
    }

    /**
     * Scope to get online sessions.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOnline($query)
    {
        return $query->whereIn('session_type', ['online', 'hybrid']);
    }

    /**
     * Scope to get in-person sessions.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeInPerson($query)
    {
        return $query->whereIn('session_type', ['in_person', 'hybrid']);
    }

    // =========================================================================
    // HELPER METHODS
    // =========================================================================

    /**
     * Check if the session is scheduled for the future.
     *
     * @return bool
     */
    public function isUpcoming(): bool
    {
        return $this->session_date->isFuture() || 
            ($this->session_date->isToday() && $this->start_time > now()->format('H:i:s'));
    }

    /**
     * Check if the session is ongoing.
     *
     * @return bool
     */
    public function isOngoing(): bool
    {
        if (!$this->session_date->isToday()) {
            return false;
        }
        
        $now = now()->format('H:i:s');
        return $this->start_time <= $now && $this->end_time >= $now;
    }

    /**
     * Check if the session has ended.
     *
     * @return bool
     */
    public function hasEnded(): bool
    {
        return $this->session_date->isPast() || 
            ($this->session_date->isToday() && $this->end_time < now()->format('H:i:s'));
    }

    /**
     * Check if the session is online.
     *
     * @return bool
     */
    public function isOnline(): bool
    {
        return in_array($this->session_type, ['online', 'hybrid']);
    }

    /**
     * Check if the session is in-person.
     *
     * @return bool
     */
    public function isInPerson(): bool
    {
        return in_array($this->session_type, ['in_person', 'hybrid']);
    }

    /**
     * Check if the session has a meeting link.
     *
     * @return bool
     */
    public function hasMeetingLink(): bool
    {
        return !empty($this->meeting_link);
    }

    /**
     * Get the session duration in minutes.
     *
     * @return int
     */
    public function getDurationMinutes(): int
    {
        $start = \Carbon\Carbon::parse($this->start_time);
        $end = \Carbon\Carbon::parse($this->end_time);
        
        return $end->diffInMinutes($start);
    }

    /**
     * Get the formatted duration.
     *
     * @return string
     */
    public function getFormattedDuration(): string
    {
        $minutes = $this->getDurationMinutes();
        $hours = floor($minutes / 60);
        $mins = $minutes % 60;
        
        if ($hours > 0 && $mins > 0) {
            return "{$hours}h {$mins}m";
        } elseif ($hours > 0) {
            return "{$hours}h";
        } else {
            return "{$mins}m";
        }
    }

    /**
     * Get the formatted time range.
     *
     * @return string
     */
    public function getFormattedTimeRange(): string
    {
        return \Carbon\Carbon::parse($this->start_time)->format('g:i A') . ' - ' . 
               \Carbon\Carbon::parse($this->end_time)->format('g:i A');
    }

    /**
     * Get the formatted date.
     *
     * @return string
     */
    public function getFormattedDate(): string
    {
        return $this->session_date->format('F j, Y');
    }

    /**
     * Get attendance count.
     *
     * @return int
     */
    public function getAttendanceCount(): int
    {
        return $this->attendances()->where('attendance_status', 'present')->count();
    }

    /**
     * Mark session as completed.
     *
     * @return bool
     */
    public function markCompleted(): bool
    {
        return $this->update(['status' => 'completed']);
    }

    /**
     * Cancel the session.
     *
     * @param string $reason
     * @return bool
     */
    public function cancel(string $reason): bool
    {
        return $this->update([
            'status' => 'cancelled',
            'cancellation_reason' => $reason,
        ]);
    }

    /**
     * Postpone the session.
     *
     * @param string $newDate
     * @param string|null $reason
     * @return bool
     */
    public function postpone(string $newDate, ?string $reason = null): bool
    {
        return $this->update([
            'status' => 'postponed',
            'new_date' => $newDate,
            'cancellation_reason' => $reason,
        ]);
    }
}
