<?php

namespace App\Models\Mentorship;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Session extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The table associated with the model.
     */
    protected $table = 'mentorship_sessions';

    /**
     * The attributes that are mass assignable.
     */
    protected $fillable = [
        'assignment_id',
        'programme_id',
        'title',
        'description',
        'agenda',
        'notes',
        'type',
        'session_number',
        'start_time',
        'end_time',
        'timezone',
        'duration_minutes',
        'venue',
        'venue_address',
        'venue_instructions',
        'meeting_link',
        'meeting_id',
        'meeting_password',
        'calendar_event_id',
        'meeting_details',
        'status',
        'mentor_attended',
        'mentor_joined_at',
        'mentee_attended',
        'mentee_joined_at',
        'summary',
        'action_items',
        'resources_shared',
        'attachments',
        'mentor_rating',
        'mentor_feedback',
        'mentee_rating',
        'mentee_feedback',
        'rescheduled_from',
        'reschedule_reason',
        'rescheduled_at',
        'cancelled_by',
        'cancelled_at',
        'cancellation_reason',
        'reminder_sent_24h',
        'reminder_sent_1h',
        'last_reminder_at',
        'created_by',
    ];

    /**
     * The attributes that should be cast.
     */
    protected $casts = [
        'start_time' => 'datetime',
        'end_time' => 'datetime',
        'duration_minutes' => 'integer',
        'meeting_details' => 'array',
        'attachments' => 'array',
        'mentor_attended' => 'boolean',
        'mentee_attended' => 'boolean',
        'mentor_joined_at' => 'datetime',
        'mentee_joined_at' => 'datetime',
        'mentor_rating' => 'integer',
        'mentee_rating' => 'integer',
        'rescheduled_at' => 'datetime',
        'cancelled_at' => 'datetime',
        'last_reminder_at' => 'datetime',
        'reminder_sent_24h' => 'boolean',
        'reminder_sent_1h' => 'boolean',
    ];

    /*
    |--------------------------------------------------------------------------
    | Status Constants
    |--------------------------------------------------------------------------
    */

    const STATUS_SCHEDULED = 'scheduled';
    const STATUS_CONFIRMED = 'confirmed';
    const STATUS_IN_PROGRESS = 'in_progress';
    const STATUS_COMPLETED = 'completed';
    const STATUS_CANCELLED = 'cancelled';
    const STATUS_RESCHEDULED = 'rescheduled';
    const STATUS_NO_SHOW = 'no_show';

    /*
    |--------------------------------------------------------------------------
    | Type Constants
    |--------------------------------------------------------------------------
    */

    const TYPE_ONLINE = 'online';
    const TYPE_IN_PERSON = 'in_person';

    /*
    |--------------------------------------------------------------------------
    | Relationships
    |--------------------------------------------------------------------------
    */

    /**
     * Get the assignment for this session.
     */
    public function assignment()
    {
        return $this->belongsTo(Assignment::class, 'assignment_id');
    }

    /**
     * Get the programme for this session.
     */
    public function programme()
    {
        return $this->belongsTo(Programme::class, 'programme_id');
    }

    /**
     * Get the user who created this session.
     */
    public function creator()
    {
        return $this->belongsTo(\App\Models\User::class, 'created_by');
    }

    /**
     * Get the user who cancelled this session.
     */
    public function canceller()
    {
        return $this->belongsTo(\App\Models\User::class, 'cancelled_by');
    }

    /**
     * Get the original session (if rescheduled).
     */
    public function originalSession()
    {
        return $this->belongsTo(self::class, 'rescheduled_from');
    }

    /**
     * Get the rescheduled session (if this was rescheduled).
     */
    public function rescheduledSession()
    {
        return $this->hasOne(self::class, 'rescheduled_from');
    }

    /**
     * Get the mentor through assignment.
     */
    public function mentor()
    {
        return $this->assignment->mentor();
    }

    /**
     * Get the mentee through assignment.
     */
    public function mentee()
    {
        return $this->assignment->mentee();
    }

    /*
    |--------------------------------------------------------------------------
    | Scopes
    |--------------------------------------------------------------------------
    */

    /**
     * Scope to get scheduled sessions.
     */
    public function scopeScheduled($query)
    {
        return $query->where('status', self::STATUS_SCHEDULED);
    }

    /**
     * Scope to get confirmed sessions.
     */
    public function scopeConfirmed($query)
    {
        return $query->where('status', self::STATUS_CONFIRMED);
    }

    /**
     * Scope to get completed sessions.
     */
    public function scopeCompleted($query)
    {
        return $query->where('status', self::STATUS_COMPLETED);
    }

    /**
     * Scope to get upcoming sessions.
     */
    public function scopeUpcoming($query)
    {
        return $query->whereIn('status', [self::STATUS_SCHEDULED, self::STATUS_CONFIRMED])
            ->where('start_time', '>', now())
            ->orderBy('start_time');
    }

    /**
     * Scope to get past sessions.
     */
    public function scopePast($query)
    {
        return $query->where('start_time', '<', now())
            ->orderBy('start_time', 'desc');
    }

    /**
     * Scope to get sessions for a specific assignment.
     */
    public function scopeForAssignment($query, $assignmentId)
    {
        return $query->where('assignment_id', $assignmentId);
    }

    /**
     * Scope to get sessions for a specific programme.
     */
    public function scopeForProgramme($query, $programmeId)
    {
        return $query->where('programme_id', $programmeId);
    }

    /**
     * Scope to get online sessions.
     */
    public function scopeOnline($query)
    {
        return $query->where('type', self::TYPE_ONLINE);
    }

    /**
     * Scope to get in-person sessions.
     */
    public function scopeInPerson($query)
    {
        return $query->where('type', self::TYPE_IN_PERSON);
    }

    /**
     * Scope to get sessions for a date range.
     */
    public function scopeDateRange($query, $startDate, $endDate)
    {
        return $query->whereBetween('start_time', [$startDate, $endDate]);
    }

    /*
    |--------------------------------------------------------------------------
    | Accessors & Mutators
    |--------------------------------------------------------------------------
    */

    /**
     * Get the status label.
     */
    public function getStatusLabelAttribute(): string
    {
        return match($this->status) {
            self::STATUS_SCHEDULED => 'Scheduled',
            self::STATUS_CONFIRMED => 'Confirmed',
            self::STATUS_IN_PROGRESS => 'In Progress',
            self::STATUS_COMPLETED => 'Completed',
            self::STATUS_CANCELLED => 'Cancelled',
            self::STATUS_RESCHEDULED => 'Rescheduled',
            self::STATUS_NO_SHOW => 'No Show',
            default => ucfirst($this->status),
        };
    }

    /**
     * Get the status color for badges.
     */
    public function getStatusColorAttribute(): string
    {
        return match($this->status) {
            self::STATUS_SCHEDULED => 'blue',
            self::STATUS_CONFIRMED => 'green',
            self::STATUS_IN_PROGRESS => 'yellow',
            self::STATUS_COMPLETED => 'green',
            self::STATUS_CANCELLED => 'red',
            self::STATUS_RESCHEDULED => 'purple',
            self::STATUS_NO_SHOW => 'orange',
            default => 'gray',
        };
    }

    /**
     * Get the type label.
     */
    public function getTypeLabelAttribute(): string
    {
        return $this->type === self::TYPE_ONLINE ? 'Online' : 'In-Person';
    }

    /**
     * Get formatted date and time.
     */
    public function getFormattedDateTimeAttribute(): string
    {
        return $this->start_time->format('M j, Y g:i A') . ' - ' . $this->end_time->format('g:i A');
    }

    /**
     * Get duration in human readable format.
     */
    public function getDurationTextAttribute(): string
    {
        $minutes = $this->duration_minutes;

        if ($minutes < 60) {
            return $minutes . ' min';
        }

        $hours = floor($minutes / 60);
        $remainingMinutes = $minutes % 60;

        $text = $hours . ' hr';
        if ($remainingMinutes > 0) {
            $text .= ' ' . $remainingMinutes . ' min';
        }

        return $text;
    }

    /**
     * Check if session is upcoming.
     */
    public function getIsUpcomingAttribute(): bool
    {
        return $this->start_time->isFuture() && in_array($this->status, [self::STATUS_SCHEDULED, self::STATUS_CONFIRMED]);
    }

    /**
     * Check if session is today.
     */
    public function getIsTodayAttribute(): bool
    {
        return $this->start_time->isToday();
    }

    /*
    |--------------------------------------------------------------------------
    | Helper Methods
    |--------------------------------------------------------------------------
    */

    /**
     * Check if the session is scheduled.
     */
    public function isScheduled(): bool
    {
        return $this->status === self::STATUS_SCHEDULED;
    }

    /**
     * Check if the session is completed.
     */
    public function isCompleted(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

    /**
     * Check if the session is cancelled.
     */
    public function isCancelled(): bool
    {
        return $this->status === self::STATUS_CANCELLED;
    }

    /**
     * Check if session is online.
     */
    public function isOnline(): bool
    {
        return $this->type === self::TYPE_ONLINE;
    }

    /**
     * Check if session is in-person.
     */
    public function isInPerson(): bool
    {
        return $this->type === self::TYPE_IN_PERSON;
    }

    /**
     * Confirm the session.
     */
    public function confirm(): bool
    {
        return $this->update(['status' => self::STATUS_CONFIRMED]);
    }

    /**
     * Start the session.
     */
    public function start(): bool
    {
        return $this->update(['status' => self::STATUS_IN_PROGRESS]);
    }

    /**
     * Complete the session.
     */
    public function complete(array $data = []): bool
    {
        $updateData = ['status' => self::STATUS_COMPLETED];

        if (isset($data['summary'])) {
            $updateData['summary'] = $data['summary'];
        }
        if (isset($data['action_items'])) {
            $updateData['action_items'] = $data['action_items'];
        }
        if (isset($data['mentor_attended'])) {
            $updateData['mentor_attended'] = $data['mentor_attended'];
        }
        if (isset($data['mentee_attended'])) {
            $updateData['mentee_attended'] = $data['mentee_attended'];
        }

        $result = $this->update($updateData);

        // Update assignment progress
        if ($result) {
            $this->assignment->updateProgress();
        }

        return $result;
    }

    /**
     * Cancel the session.
     */
    public function cancel(int $cancelledBy, string $reason): bool
    {
        return $this->update([
            'status' => self::STATUS_CANCELLED,
            'cancelled_by' => $cancelledBy,
            'cancelled_at' => now(),
            'cancellation_reason' => $reason,
        ]);
    }

    /**
     * Mark as no-show.
     */
    public function markNoShow(): bool
    {
        return $this->update(['status' => self::STATUS_NO_SHOW]);
    }

    /**
     * Reschedule the session.
     */
    public function reschedule(array $newData): ?self
    {
        // Create new session
        $newSession = self::create(array_merge($newData, [
            'assignment_id' => $this->assignment_id,
            'programme_id' => $this->programme_id,
            'rescheduled_from' => $this->id,
            'rescheduled_at' => now(),
            'status' => self::STATUS_SCHEDULED,
            'created_by' => $newData['created_by'] ?? $this->created_by,
        ]));

        if ($newSession) {
            // Mark original as rescheduled
            $this->update([
                'status' => self::STATUS_RESCHEDULED,
                'reschedule_reason' => $newData['reschedule_reason'] ?? null,
            ]);
        }

        return $newSession;
    }

    /**
     * Record attendance.
     */
    public function recordAttendance(string $role, bool $attended): bool
    {
        $data = [];

        if ($role === 'mentor') {
            $data['mentor_attended'] = $attended;
            $data['mentor_joined_at'] = $attended ? now() : null;
        } else {
            $data['mentee_attended'] = $attended;
            $data['mentee_joined_at'] = $attended ? now() : null;
        }

        return $this->update($data);
    }

    /**
     * Check if 24-hour reminder should be sent.
     */
    public function shouldSend24hReminder(): bool
    {
        return !$this->reminder_sent_24h
            && $this->start_time->isFuture()
            && $this->start_time->diffInHours(now()) <= 24
            && $this->start_time->diffInHours(now()) > 1;
    }

    /**
     * Check if 1-hour reminder should be sent.
     */
    public function shouldSend1hReminder(): bool
    {
        return !$this->reminder_sent_1h
            && $this->start_time->isFuture()
            && $this->start_time->diffInMinutes(now()) <= 60
            && $this->start_time->diffInMinutes(now()) > 0;
    }

    /**
     * Mark 24-hour reminder as sent.
     */
    public function markReminder24hSent(): bool
    {
        return $this->update([
            'reminder_sent_24h' => true,
            'last_reminder_at' => now(),
        ]);
    }

    /**
     * Mark 1-hour reminder as sent.
     */
    public function markReminder1hSent(): bool
    {
        return $this->update([
            'reminder_sent_1h' => true,
            'last_reminder_at' => now(),
        ]);
    }
}
