<?php

namespace App\Services\Leave;

use App\Models\LeaveType;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;

class LeaveRuleValidator
{
    public function __construct(
        private readonly LeaveEligibilityService $eligibility,
        private readonly LeaveDayCalculator $dayCalculator,
    ) {}

    /**
     * Validate leave request against leave type rules.
     *
     * Returns the day-calculation result so controllers can persist `total_days`
     * without recalculating.
     *
     * @return array{calendar_days: float, chargeable_days: float, breakdown: array}
     */
    public function validateForCreate(Request $request, User $user, LeaveType $leaveType, Carbon $startDate, Carbon $endDate, bool $isHalfDay): array
    {
        $leaveType->loadMissing(['rule', 'departments', 'roles', 'users']);

        if (! $this->isEligible($user, $leaveType)) {
            throw ValidationException::withMessages([
                'leave_type_id' => 'This leave type is not assigned to the selected employee.',
            ]);
        }

        $rule = $leaveType->rule;

        if ($rule?->gender_restriction && $user->gender && $user->gender !== $rule->gender_restriction) {
            throw ValidationException::withMessages([
                'leave_type_id' => 'This leave type is restricted by gender.',
            ]);
        }

        if (! $leaveType->probation_allowed && $this->isOnProbation($user)) {
            throw ValidationException::withMessages([
                'leave_type_id' => 'This leave type is not allowed during probation.',
            ]);
        }

        if (! $leaveType->notice_period_allowed && $this->isOnNoticePeriod($user)) {
            throw ValidationException::withMessages([
                'leave_type_id' => 'This leave type is not allowed during notice period.',
            ]);
        }

        if ($rule?->requires_attachment) {
            $file = $request->file('attachment');
            if (! $file) {
                throw ValidationException::withMessages([
                    'attachment' => 'Attachment is required for this leave type.',
                ]);
            }
        }

        $calc = $this->dayCalculator->calculate($user, $leaveType, $startDate, $endDate, $isHalfDay);

        $min = $rule?->min_days_per_request;
        if ($min !== null && (float) $calc['chargeable_days'] < (float) $min) {
            throw ValidationException::withMessages([
                'start_date' => "Minimum {$min} day(s) required per request for this leave type.",
            ]);
        }

        $max = $rule?->max_days_per_request;
        if ($max !== null && (float) $calc['chargeable_days'] > (float) $max) {
            throw ValidationException::withMessages([
                'end_date' => "Maximum {$max} day(s) allowed per request for this leave type.",
            ]);
        }
        
        if($request->has('start_date') && $request->has('end_date')) {
            $start = Carbon::parse($request->input('start_date'))->startOfDay();
            $end = Carbon::parse($request->input('end_date'))->startOfDay();
            $totalleaveDays = $this->dayCalculator->calculate($user, $leaveType, $start, $end, $isHalfDay)['chargeable_days'];  

            $availableLeaveDays = $this->eligibility->availableLeaveDays($user, $leaveType);
            
            if ($totalleaveDays > $availableLeaveDays) {
                throw ValidationException::withMessages([
                    'start_date' => "The user has only {$availableLeaveDays} day(s) available for this leave type.",
                ]);
            }
        }

        return $calc;
    }

    private function isEligible(User $user, LeaveType $leaveType): bool
    {
        if ($leaveType->apply_to_all) {
            return true;
        }

        return $this->eligibility->eligibleUsersQuery($leaveType)->where('id', $user->id)->exists();
    }

    private function isOnProbation(User $user): bool
    {
        if (array_key_exists('is_on_probation', $user->getAttributes())) {
            return (bool) $user->getAttribute('is_on_probation');
        }
        if (array_key_exists('probation_end_date', $user->getAttributes()) && $user->getAttribute('probation_end_date')) {
            return Carbon::parse($user->getAttribute('probation_end_date'))->endOfDay()->gte(now());
        }
        return false;
    }

    private function isOnNoticePeriod(User $user): bool
    {
        if (array_key_exists('is_on_notice_period', $user->getAttributes())) {
            return (bool) $user->getAttribute('is_on_notice_period');
        }
        if (array_key_exists('notice_period_end_date', $user->getAttributes()) && $user->getAttribute('notice_period_end_date')) {
            return Carbon::parse($user->getAttribute('notice_period_end_date'))->endOfDay()->gte(now());
        }
        return false;
    }
}

