<?php

namespace App\Models;

use App\Models\Traits\HasAuditFields;
use App\Models\Traits\HasScopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Holiday Model
 *
 * Manages company holidays including public holidays,
 * optional holidays, and company-specific observances.
 */
class Holiday extends Model
{
    use HasFactory, SoftDeletes, HasAuditFields, HasScopes;

    protected $table = 'holidays';

    /**
     * Holiday type constants
     */
    public const TYPE_PUBLIC = 'public';
    public const TYPE_OPTIONAL = 'optional';
    public const TYPE_COMPANY = 'company';
    public const TYPE_RESTRICTED = 'restricted';

    /**
     * Status constants
     */
    public const STATUS_ACTIVE = 'active';
    public const STATUS_INACTIVE = 'inactive';

    protected $fillable = [
        'name',
        'date',
        'day',
        'type',
        'description',
        'status',
        'year',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'date' => 'date',
        'year' => 'integer',
    ];

    /**
     * Column configuration for HasScopes trait
     */
    protected string $dateColumn = 'date';

    /**
     * Boot the model
     */
    protected static function boot()
    {
        parent::boot();

        // Automatically set day name and year when creating/updating
        static::saving(function ($holiday) {
            if ($holiday->date) {
                $holiday->day = $holiday->date->format('l'); // Day name (Monday, Tuesday, etc.)
                $holiday->year = $holiday->date->year;
            }
        });
    }

    // =========================================================================
    // SCOPES
    // =========================================================================

    /**
     * Scope to filter active holidays
     */
    public function scopeActive(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_ACTIVE);
    }

    /**
     * Scope to filter by year
     */
    public function scopeForYear(Builder $query, int $year): Builder
    {
        return $query->whereYear('date', $year);
    }

    /**
     * Scope to filter current year
     */
    public function scopeCurrentYear(Builder $query): Builder
    {
        return $query->whereYear('date', now()->year);
    }

    /**
     * Scope to filter by type
     */
    public function scopeOfType(Builder $query, string $type): Builder
    {
        return $query->where('type', $type);
    }

    /**
     * Scope to filter public holidays
     */
    public function scopePublicHolidays(Builder $query): Builder
    {
        return $query->where('type', self::TYPE_PUBLIC);
    }

    /**
     * Scope to filter optional holidays
     */
    public function scopeOptionalHolidays(Builder $query): Builder
    {
        return $query->where('type', self::TYPE_OPTIONAL);
    }

    /**
     * Scope to filter upcoming holidays
     */
    public function scopeUpcoming(Builder $query): Builder
    {
        return $query->where('date', '>=', now()->toDateString())
                     ->orderBy('date', 'asc');
    }

    /**
     * Scope to filter holidays in a date range
     */
    public function scopeInRange(Builder $query, string $from, string $to): Builder
    {
        return $query->whereBetween('date', [$from, $to]);
    }

    /**
     * Scope to filter holidays in a month
     */
    public function scopeInMonth(Builder $query, int $month, ?int $year = null): Builder
    {
        $year = $year ?? now()->year;
        return $query->whereMonth('date', $month)
                     ->whereYear('date', $year);
    }

    /**
     * Scope to order by date
     */
    public function scopeOrderByDate(Builder $query, string $direction = 'asc'): Builder
    {
        return $query->orderBy('date', $direction);
    }

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

    /**
     * Get all valid types
     */
    public static function getTypes(): array
    {
        return [
            self::TYPE_PUBLIC,
            self::TYPE_OPTIONAL,
            self::TYPE_COMPANY,
            self::TYPE_RESTRICTED,
        ];
    }

    /**
     * Get all valid statuses
     */
    public static function getStatuses(): array
    {
        return [
            self::STATUS_ACTIVE,
            self::STATUS_INACTIVE,
        ];
    }

    /**
     * Check if active
     */
    public function isActive(): bool
    {
        return $this->status === self::STATUS_ACTIVE;
    }

    /**
     * Check if public holiday
     */
    public function isPublicHoliday(): bool
    {
        return $this->type === self::TYPE_PUBLIC;
    }

    /**
     * Check if optional holiday
     */
    public function isOptional(): bool
    {
        return $this->type === self::TYPE_OPTIONAL;
    }

    /**
     * Check if the holiday is today
     */
    public function isToday(): bool
    {
        return $this->date->isToday();
    }

    /**
     * Check if the holiday is upcoming
     */
    public function isUpcoming(): bool
    {
        return $this->date->isFuture();
    }

    /**
     * Check if the holiday has passed
     */
    public function hasPassed(): bool
    {
        return $this->date->isPast();
    }

    /**
     * Get formatted date
     */
    public function getFormattedDateAttribute(): string
    {
        return $this->date->format('d M Y');
    }

    /**
     * Get type label
     */
    public function getTypeLabelAttribute(): string
    {
        return match ($this->type) {
            self::TYPE_PUBLIC => 'Public Holiday',
            self::TYPE_OPTIONAL => 'Optional Holiday',
            self::TYPE_COMPANY => 'Company Holiday',
            self::TYPE_RESTRICTED => 'Restricted Holiday',
            default => ucfirst($this->type ?? 'Unknown'),
        };
    }

    // =========================================================================
    // STATIC METHODS
    // =========================================================================

    /**
     * Check if a specific date is a holiday
     */
    public static function isHoliday(string $date): bool
    {
        return self::active()
            ->where('date', $date)
            ->exists();
    }

    /**
     * Get all holiday dates for a year as array
     */
    public static function getDatesForYear(int $year): array
    {
        return self::active()
            ->forYear($year)
            ->pluck('date')
            ->map(fn($d) => $d->toDateString())
            ->toArray();
    }

    /**
     * Get holidays count for a date range
     */
    public static function countInRange(string $from, string $to): int
    {
        return self::active()
            ->inRange($from, $to)
            ->count();
    }

    /**
     * Get next upcoming holiday
     */
    public static function getNextHoliday(): ?self
    {
        return self::active()
            ->upcoming()
            ->first();
    }
}
