<?php

namespace App\Services;

use App\Models\User;
use App\Models\WeekoffRule;
use App\Models\EmployeeWeekoffHistory;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class WeekoffService
{
    public static function assignRule(User $user, WeekoffRule $rule, Carbon $effectiveFrom, ?User $assignedBy = null, ?string $notes = null): User
    {
        // Persist assignment as history entry (append-only)
        EmployeeWeekoffHistory::create([
            'employee_id' => $user->id,
            'weekoff_rule_id' => $rule->id,
            'effective_date' => $effectiveFrom->format('Y-m-d'),
            'assigned_by' => $assignedBy?->id,
            'remark' => $notes,
            'company_id' => $user->company_id ?? null,
        ]);

        // Return latest effective rule for the user
        return $user->fresh();
    }

    public static function getEffectiveRule(User $user, ?Carbon $onDate = null): ?WeekoffRule
    {
        $onDate = $onDate ?: Carbon::today();

        // Look up the most recent history entry whose effective_date <= onDate
        $history = EmployeeWeekoffHistory::where('employee_id', $user->id)
            ->whereDate('effective_date', '<=', $onDate->format('Y-m-d'))
            ->orderByDesc('effective_date')
            ->first();

        if ($history && $history->weekoff_rule_id) {
            $rule = WeekoffRule::find($history->weekoff_rule_id);
            if ($rule && $rule->is_active) {
                return $rule;
            }
        }

        // Fallback to company default rule
        return WeekoffRule::where('is_company_default', true)
            ->where('is_active', true)
            ->orderBy('effective_from', 'desc')
            ->first();
    }

    public static function isWeekoff(User $user, ?Carbon $date = null): bool
    {
        $date = $date ? $date->copy() : Carbon::today();
        $rule = self::getEffectiveRule($user, $date);
        if (!$rule) {
            return false;
        }

        return self::matchesRule($rule, $date);
    }

    public static function resolveWeekoffOccurrence(User $user, ?Carbon $date = null): ?array
    {
        $date = $date ? $date->copy() : Carbon::today();
        $rule = self::getEffectiveRule($user, $date);
 
        if (!$rule || !self::matchesRule($rule, $date)) {
            return null;
        }

        return [
            'rule' => $rule,
            'date' => $date->copy(),
            'weekday' => (int)$date->format('w'),
            'week_key' => self::resolveWeekKey($date),
            'is_half_day' => self::isHalfDay($rule, $date),
        ];
    }

    public static function getWeekoffDatesBetween(User $user, Carbon $startDate, Carbon $endDate): Collection
    {
        $occurrences = collect();
        $cursor = $startDate->copy()->startOfDay();
        $end = $endDate->copy()->startOfDay();
        while ($cursor->lte($end)) {
            $occurrence = self::resolveWeekoffOccurrence($user, $cursor);

            if ($occurrence) {
                $occurrences->push($occurrence);
            }

            $cursor->addDay();
        }

        return $occurrences;
    }

    protected static function matchesRule(WeekoffRule $rule, Carbon $date): bool
    {
        $pattern = $rule->pattern ?: [];
        $weekday = (int)$date->format('w'); // 0 (Sunday) - 6 (Saturday)
        $weekKey = self::resolveWeekKey($date);

        $weekSpecific = data_get($pattern, "weeks.$weekKey");

        if ($weekSpecific === null) {
            $weekSpecific = data_get($pattern, $weekKey);
        }

        if ($weekSpecific === null) {
            $weekSpecific = Arr::get($pattern, 'days', []);
        }

        if (!is_array($weekSpecific)) {
            $weekSpecific = [];
        }

        if (in_array($weekday, $weekSpecific, true)) {
            return true;
        }

        $globalDays = Arr::get($pattern, 'days', []);
        if (!is_array($globalDays)) {
            $globalDays = [];
        }

        return in_array($weekday, $globalDays, true);
    }

    protected static function isHalfDay(WeekoffRule $rule, Carbon $date): bool
    {
        $pattern = $rule->pattern ?: [];
        $weekday = (int)$date->format('w');
        $weekKey = self::resolveWeekKey($date);

        $halfDayWeeks = Arr::get($pattern, "half_days.$weekKey", []);
        $halfDayGlobal = Arr::get($pattern, 'half_days.days', []);

        $halfDayWeeks = is_array($halfDayWeeks) ? $halfDayWeeks : [];
        $halfDayGlobal = is_array($halfDayGlobal) ? $halfDayGlobal : [];

        if (in_array($weekday, $halfDayWeeks, true)) {
            return true;
        }

        return in_array($weekday, $halfDayGlobal, true);
    }

    protected static function resolveWeekKey(Carbon $date): string
    {
        $day = (int)$date->format('j');
        $weekIndex = (int)ceil($day / 7);
        $weekIndex = max(1, min(5, $weekIndex));

        return "week_{$weekIndex}";
    }
}

