<?php

namespace App\Models;

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

/**
 * Employee Leave Model
 *
 * Manages employee leave requests with approval workflow.
 * Supports full day, half day, and hourly leave durations.
 */
class EmployeeLeave extends Model
{
    use HasFactory, SoftDeletes, HasAuditFields, HasScopes;

    protected $table = 'employee_leaves';

    /**
     * Status constants
     */
    public const STATUS_PENDING = 'Pending';
    public const STATUS_APPROVED = 'Approved';
    public const STATUS_REJECTED = 'Rejected';
    public const STATUS_CANCELLED = 'Cancelled';

    /**
     * Duration type constants
     */
    public const DURATION_FULL_DAY = 'Full Day';
    public const DURATION_HALF_DAY = 'Half Day';
    public const DURATION_HOURLY = 'Hourly';

    /**
     * Half type constants
     */
    public const HALF_FIRST = 'First Half';
    public const HALF_SECOND = 'Second Half';

    /**
     * Leave type constants
     */
    public const TYPE_CL = 'CL';  // Casual Leave
    public const TYPE_SL = 'SL';  // Sick Leave
    public const TYPE_PL = 'PL';  // Privilege Leave
    public const TYPE_LOP = 'LOP'; // Loss of Pay
    public const TYPE_COMP_OFF = 'Comp Off';  // Compensatory Off
    public const TYPE_MATERNITY = 'Maternity'; // Maternity Leave
    public const TYPE_PATERNITY = 'Paternity'; // Paternity Leave

    protected $fillable = [
        'employee_id',
        'employee_name',
        'department',
        'leave_type',
        'leave_from',
        'leave_to',
        'from_hour',
        'to_hour',
        'number_of_days',
        'cl_days',
        'lop_days',
        'working_days_count',
        'duration_type',
        'half_type',
        'status',
        'reason',
        'requested_on',
        'approved_by',
        'approval_date',
        'rejection_reason',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'leave_from' => 'datetime',
        'leave_to' => 'datetime',
        'approval_date' => 'date',
        'requested_on' => 'datetime',
        'number_of_days' => 'float',
        'cl_days' => 'float',
        'lop_days' => 'float',
    ];

    /**
     * Column configuration for HasScopes trait
     */
    protected string $dateColumn = 'leave_from';
    protected string $statusColumn = 'status';

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

    public function employee()
    {
        return $this->belongsTo(EmployeeDetails::class, 'employee_id', 'id');
    }

    public function approver()
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

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

    /**
     * Scope to filter pending leaves
     */
    public function scopePendingApproval(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    /**
     * Scope to filter approved leaves
     */
    public function scopeApprovedOnly(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_APPROVED);
    }

    /**
     * Scope to filter rejected leaves
     */
    public function scopeRejectedOnly(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_REJECTED);
    }

    /**
     * Scope to filter by employee
     */
    public function scopeForEmployee(Builder $query, int $employeeId): Builder
    {
        return $query->where('employee_id', $employeeId);
    }

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

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

    /**
     * Scope for current year CL
     */
    public function scopeCurrentYearCl(Builder $query, ?int $year = null): Builder
    {
        $year = $year ?: now()->year;
        return $query->where('leave_type', self::TYPE_CL)
                     ->whereYear('leave_from', $year);
    }

    /**
     * Scope for leaves overlapping a date range
     */
    public function scopeOverlapping(Builder $query, string $from, string $to): Builder
    {
        return $query->where(function ($q) use ($from, $to) {
            $q->whereBetween('leave_from', [$from, $to])
              ->orWhereBetween('leave_to', [$from, $to])
              ->orWhere(function ($inner) use ($from, $to) {
                  $inner->where('leave_from', '<=', $from)
                        ->where('leave_to', '>=', $to);
              });
        });
    }

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

    /**
     * Get all valid statuses
     */
    public static function getStatuses(): array
    {
        return [
            self::STATUS_PENDING,
            self::STATUS_APPROVED,
            self::STATUS_REJECTED,
            self::STATUS_CANCELLED,
        ];
    }

    /**
     * Get all valid duration types
     */
    public static function getDurationTypes(): array
    {
        return [
            self::DURATION_FULL_DAY,
            self::DURATION_HALF_DAY,
            self::DURATION_HOURLY,
        ];
    }

    /**
     * Get all valid half types
     */
    public static function getHalfTypes(): array
    {
        return [
            self::HALF_FIRST,
            self::HALF_SECOND,
        ];
    }

    /**
     * Get all valid leave types
     */
    public static function getLeaveTypes(): array
    {
        return [
            self::TYPE_CL,
            self::TYPE_SL,
            self::TYPE_PL,
            self::TYPE_LOP,
            self::TYPE_COMP_OFF,
            self::TYPE_MATERNITY,
            self::TYPE_PATERNITY,
        ];
    }

    /**
     * Check if pending
     */
    public function isPending(): bool
    {
        return $this->status === self::STATUS_PENDING;
    }

    /**
     * Check if approved
     */
    public function isApproved(): bool
    {
        return $this->status === self::STATUS_APPROVED;
    }

    /**
     * Check if rejected
     */
    public function isRejected(): bool
    {
        return $this->status === self::STATUS_REJECTED;
    }

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

    /**
     * Approve this leave request
     */
    public function approve(?string $remarks = null): bool
    {
        return $this->update([
            'status' => self::STATUS_APPROVED,
            'approved_by' => Auth::id(),
            'approval_date' => now(),
        ]);
    }

    /**
     * Reject this leave request
     */
    public function reject(?string $reason = null): bool
    {
        return $this->update([
            'status' => self::STATUS_REJECTED,
            'approved_by' => Auth::id(),
            'approval_date' => now(),
            'rejection_reason' => $reason,
        ]);
    }

    /**
     * Cancel this leave request
     */
    public function cancel(): bool
    {
        if (!$this->isPending()) {
            return false;
        }
        return $this->update(['status' => self::STATUS_CANCELLED]);
    }

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

    /**
     * Scope for current year CL for an employee (legacy support)
     */
    public static function currentYearCl($employeeId, $year = null)
    {
        $year = $year ?: now()->year;
        return self::where('employee_id', $employeeId)
            ->where('leave_type', self::TYPE_CL)
            ->whereYear('leave_from', $year)
            ->whereIn('status', [self::STATUS_APPROVED, self::STATUS_PENDING]);
    }

    /**
     * Calculate working days between two dates (excluding Sundays and holidays)
     */
    public static function calculateWorkingDays(Carbon $startDate, Carbon $endDate, array $holidays = []): int
    {
        $workingDays = 0;

        for ($date = $startDate->copy(); $date->lte($endDate); $date->addDay()) {
            $dateStr = $date->toDateString();

            // Skip Sundays (dayOfWeek === 0)
            if ($date->dayOfWeek === 0) {
                continue;
            }

            // Skip holidays
            if (in_array($dateStr, $holidays)) {
                continue;
            }

            $workingDays++;
        }

        return $workingDays;
    }

    /**
     * Get all holidays for a date range
     */
    public static function getHolidaysInRange(Carbon $startDate, Carbon $endDate): array
    {
        return Holiday::whereBetween('date', [$startDate->toDateString(), $endDate->toDateString()])
            ->pluck('date')
            ->map(fn($d) => Carbon::parse($d)->toDateString())
            ->toArray();
    }
}
