<?php

namespace App\Models;

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

/**
 * Employee Leave Balance Model
 *
 * Tracks leave balance for employees by year and type.
 * Handles accrual, usage, and restoration of leave days.
 */
class EmployeeLeaveBalance extends Model
{
    use HasFactory, SoftDeletes, HasAuditFields;

    protected $table = 'employee_leave_balances';

    /**
     * Leave type constants
     */
    public const TYPE_CL = 'CL';  // Casual Leave
    public const TYPE_SL = 'SL';  // Sick Leave
    public const TYPE_PL = 'PL';  // Privilege Leave

    protected $fillable = [
        'employee_id',
        'year',
        'leave_type',
        'opening_balance',
        'accrued',
        'used',
        'lapsed',
        'current_balance',
        'last_accrual_month',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'opening_balance' => 'decimal:2',
        'accrued' => 'decimal:2',
        'used' => 'decimal:2',
        'lapsed' => 'decimal:2',
        'current_balance' => 'decimal:2',
        'year' => 'integer',
        'last_accrual_month' => 'integer',
    ];

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

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

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

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

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

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

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

    /**
     * Scope to filter balances with available leave
     */
    public function scopeWithAvailableBalance(Builder $query): Builder
    {
        return $query->where('current_balance', '>', 0);
    }

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

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

    /**
     * Check if has available balance
     */
    public function hasBalance(): bool
    {
        return $this->current_balance > 0;
    }

    /**
     * Check if already accrued for month
     */
    public function hasAccruedForMonth(int $month): bool
    {
        return $this->last_accrual_month >= $month;
    }

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

    /**
     * Get or create leave balance for an employee
     */
    public static function getOrCreateBalance(int $employeeId, int $year, string $leaveType = 'CL'): self
    {
        return self::firstOrCreate(
            [
                'employee_id' => $employeeId,
                'year' => $year,
                'leave_type' => $leaveType,
            ],
            [
                'opening_balance' => 0,
                'accrued' => 0,
                'used' => 0,
                'lapsed' => 0,
                'current_balance' => 0,
                'last_accrual_month' => 0,
            ]
        );
    }

    /**
     * Accrue leave for an employee (called when they work 20+ days in a month)
     */
    public static function accrueLeave(
        int $employeeId,
        int $year,
        int $month,
        float $amount = 1.0,
        string $leaveType = 'CL'
    ): bool {
        $balance = self::getOrCreateBalance($employeeId, $year, $leaveType);

        // Check if already accrued for this month
        if ($balance->last_accrual_month >= $month) {
            return false; // Already accrued
        }

        $balance->accrued += $amount;
        $balance->current_balance += $amount;
        $balance->last_accrual_month = $month;
        $balance->save();

        return true;
    }

    /**
     * Use leave (deduct from balance)
     */
    public static function useLeave(
        int $employeeId,
        int $year,
        float $days,
        string $leaveType = 'CL'
    ): bool {
        $balance = self::getOrCreateBalance($employeeId, $year, $leaveType);

        if ($balance->current_balance < $days) {
            return false; // Insufficient balance
        }

        $balance->used += $days;
        $balance->current_balance -= $days;
        $balance->save();

        return true;
    }

    /**
     * Restore leave (when a leave request is cancelled/rejected)
     */
    public static function restoreLeave(
        int $employeeId,
        int $year,
        float $days,
        string $leaveType = 'CL'
    ): bool {
        $balance = self::getOrCreateBalance($employeeId, $year, $leaveType);

        $balance->used -= $days;
        $balance->current_balance += $days;
        $balance->save();

        return true;
    }

    /**
     * Get available balance for an employee
     */
    public static function getAvailableBalance(
        int $employeeId,
        int $year,
        string $leaveType = 'CL'
    ): float {
        $balance = self::where('employee_id', $employeeId)
            ->where('year', $year)
            ->where('leave_type', $leaveType)
            ->first();

        return $balance ? (float) $balance->current_balance : 0;
    }

    /**
     * Lapse unused leaves at year end
     */
    public static function lapseLeaves(
        int $employeeId,
        int $year,
        string $leaveType = 'CL'
    ): bool {
        $balance = self::where('employee_id', $employeeId)
            ->where('year', $year)
            ->where('leave_type', $leaveType)
            ->first();

        if (!$balance || $balance->current_balance <= 0) {
            return false;
        }

        $balance->lapsed = $balance->current_balance;
        $balance->current_balance = 0;
        $balance->save();

        return true;
    }

    /**
     * Carry forward leaves to new year
     */
    public static function carryForward(
        int $employeeId,
        int $fromYear,
        int $toYear,
        string $leaveType = 'CL',
        float $maxCarryForward = 5.0
    ): ?self {
        $oldBalance = self::where('employee_id', $employeeId)
            ->where('year', $fromYear)
            ->where('leave_type', $leaveType)
            ->first();

        if (!$oldBalance) {
            return null;
        }

        $carryForwardAmount = min($oldBalance->current_balance, $maxCarryForward);

        return self::firstOrCreate(
            [
                'employee_id' => $employeeId,
                'year' => $toYear,
                'leave_type' => $leaveType,
            ],
            [
                'opening_balance' => $carryForwardAmount,
                'accrued' => 0,
                'used' => 0,
                'lapsed' => 0,
                'current_balance' => $carryForwardAmount,
                'last_accrual_month' => 0,
            ]
        );
    }
}
