<?php

namespace App\Services\Client;

use App\Models\AvailabilityRule;
use App\Models\MealPackage;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;

class MealPackageAvailabilityService
{
    // Check the date is coverd by the rule
    private static function isDateCoveredByRule(Carbon $date, AvailabilityRule  $rule): bool
    {
        $dateOnly = $date->copy()->startOfDay();

        switch ($rule['pattern_type'] ?? 'date_range') {
            case 'date_range':
                $start = Carbon::parse($rule['start_date'])->startOfDay();
                $end = Carbon::parse($rule['end_date'])->startOfDay();
                return $dateOnly->gte($start) && $dateOnly->lte($end);

            case 'weekly':
                return isset($rule['day_of_week']) && $dateOnly->dayOfWeek === (int) $rule['day_of_week'];

            case 'monthly':
                return isset($rule['day_of_month']) && $dateOnly->day === (int) $rule['day_of_month'];

            case 'yearly':
                return isset($rule['day_of_month'], $rule['month_of_year']) &&
                    $dateOnly->day === (int)$rule['day_of_month'] &&
                    $dateOnly->month === (int)$rule['month_of_year'];

            default:
                return false;
        }
    }

    // Check the rule is valid or not
    private static function isRuleValidForDate(Carbon $date, AvailabilityRule $rule, bool $defaultAvailable): bool
    {
        if (isset($rule["rule_mode"]) && $rule["rule_mode"] === ($defaultAvailable ? 'active' : 'block')) {
            return false;
        }

        if (empty($rule['valid_from']) && empty($rule['valid_until'])) {
            return true;
        }

        $date = $date->copy()->startOfDay();

        if (!empty($rule['valid_from'])) {
            $validFrom = Carbon::parse($rule['valid_from'])->startOfDay();
            if ($date->lt($validFrom)) return false;
        }

        if (!empty($rule['valid_until'])) {
            $validUntil = Carbon::parse($rule['valid_until'])->startOfDay();
            if ($date->gt($validUntil)) return false;
        }

        return true;
    }


    // Scans all rules and determines which dates are available for the item.
    private static function checkItem(Collection $rules, string $checkIn, string $checkOut, bool $defaultAvailable): array
    {
        $checkIn = Carbon::parse($checkIn)->startOfDay();
        $checkOut = Carbon::parse($checkOut)->startOfDay();

        $dates = [];

        for ($date = $checkIn->copy(); $date->lt($checkOut); $date->addDay()) {

            $covered = $defaultAvailable;

            foreach ($rules as $rule) {

                if (!self::isRuleValidForDate($date, $rule, $defaultAvailable)) {
                    continue;
                }

                if (self::isDateCoveredByRule($date, $rule)) {
                    $covered = !$defaultAvailable;
                    break;
                }
            }

            $dates[] =   (object)['date' => $date->toDateString(), 'available' => $covered];
        }

        return $dates;
    }

    // Scans all rules and determines which dates are available for the variant of item.
    private static function checkItemVariant(Collection $rules, array $checkDates, bool $defaultAvailable): array
    {
        $dates = [];

        foreach ($checkDates as $date) {

            if (!$date->available) {
                continue;
            }

            $date = Carbon::parse($date->date)->startOfDay();

            $covered = $defaultAvailable;

            foreach ($rules as $rule) {

                if (!self::isRuleValidForDate($date, $rule, $defaultAvailable)) {
                    continue;
                }

                if (self::isDateCoveredByRule($date, $rule)) {
                    $covered = !$defaultAvailable;
                    break;
                }
            }

            $dates[] =   (object)['date' => $date->toDateString(), 'available' => $covered];
        }

        return $dates;
    }

    // Filter all available items
    public static function filterAvailbleItems(Collection $items,  string $checkIn, string $checkOut)
    {

        // if no any item just return
        if ($items->isEmpty()) {
            // dump('NO Meal Item found for Filter');
            return collect();
        }

        // dump('---------------------------> New filtering');
        // dump($checkIn . ' : ' . $checkOut);
        // dump($items);

        // Filter items by availability (check-in / check-out)
        $items = $items->map(function ($item, $key) use ($checkIn, $checkOut) {



            // dump('///////////////////////////////////////////////////////////////////////////////// ' . $item->title);

            $rules = $item->availabilityRules;
            $default = $item->default_availability;
            $allow = $item->is_allow_select_days;
            $is_variant = $item->is_variant;

            // dump($item->default_availability . ' : ' . $allow . ' : ' . $is_variant);
            // dump($rules);

            // If default status is blocked and there is no any rulse(active) that mean the item is not available
            if (!$default && $rules->isEmpty()) {
                // dump($item->title . ' -> Default BLocked and no Rulse');
                return null;
            }

            // get available dates of item
            $available_dates = self::checkItem($rules, $checkIn, $checkOut, $default);

            // dump($item->title);
            // dump($available_dates);

            $not_avaialbe = True;
            // Check the item is available for at least one day or if not allow and not availbe all days then retruen making not avaible the item
            foreach ($available_dates as $date) {

                //if use can select it can have at least one available date
                if ($allow && $date->available) {
                    $not_avaialbe = False;
                    break;
                };

                //if the user can't select the item can't have one day that are not available
                if (!$allow && !$date->available) {
                    // dump($item->title . ' -> No any available dates');
                    return null;
                }

                // if user can select but no date are left then return null
                if ($date->available) {
                    $not_avaialbe = False;
                    // dump($item->title . ' avaialbe');
                }
                // dump($item->title . ' status ' . ($date->available ? 'TRUE' : 'False') . $not_avaialbe);
            }

            if ($not_avaialbe) {
                return null;
            }

            // Check wether the item  is varinat one. if not return the item
            if (!$is_variant) {
                if ($allow) {
                    $item->setAvailableDates(collect($available_dates));
                }
                // dump('No Variants', $available_dates, $item->getAvailableDatesAttribute());
                return $item;
            }

            // Load variants <------------------------------------
            $item->load(['variants' => fn($varinat) => $varinat->where('is_published', true)->orderBy('sort_order')]);
            // dump($item->variants);

            if ($item->variants->isEmpty()) {
                // dump('Configure Error return null bcz of no variants');
                return null;
            }

            $item->variants = $item->variants->map(function ($variant, $key) use ($available_dates, $allow) {
                // dump('Variant : ' . $variant->title . '------------------->');

                $default_v = $variant->default_availability;
                $rules_v = $variant->availabilityRules;

                if (!$default_v && $rules_v->isEmpty()) {
                    // dump($variant->title . ' -> Default BLocked and no Rulse in Variant');
                    return null;
                }


                $available_dates_v = self::checkItemVariant($rules_v, $available_dates, $default_v);
                // dump($available_dates_v);

                $not_avaialbe = True;
                foreach ($available_dates_v as $date) {

                    //if use can select it can have at least one available date
                    if ($allow && $date->available) {
                        $not_avaialbe = False;
                        break;
                    };

                    //if the user can't select the item can't have one day that are not available
                    if (!$allow && !$date->available) {
                        // dump($variant->title . ' -> No any available dates for vairiant');
                        return null;
                    }

                    // if user can select but no date are left then return null
                    if ($date->available) {
                        $not_avaialbe = False;
                    }
                }

                if ($not_avaialbe) {
                    return null;
                }

                if ($allow) {
                    $variant->setAvailableDates(collect($available_dates_v));
                }
                // dump('Results of variant', $available_dates_v, $variant->getAvailableDatesAttribute());
                return $variant;
            })->filter()->values();

            if ($item->variants->isEmpty()) {
                // dump('No valid variants left');
                return null;
            }

            if ($allow) {
                $item->setAvailableDates(collect($available_dates));
            }
            // dump('Results of items', $available_dates, $item->getAvailableDatesAttribute());
            return $item;
        });


        return $items->filter()->map(function ($item, $key) {
            return [
                'id' => $item->id,
                'title' => $item->title,
                'description' => $item->description,
                'tag' => $item->tag,
                'icon_value' => $item->icon_value,
                'is_icon' => $item->is_icon,
                'is_allow_select_days' => $item->is_allow_select_days,
                'price' => $item->price,
                'is_variant' => $item->is_variant,
                'available_dates' => $item->available_dates ?? [],
                'images' => $item->images->map(fn($img) => [
                    'url' => $img->url,
                    'alt_text' => $img->alt_text,
                ]),
                'variants' => $item->variants->map(fn($v) => [
                    'id' => $v->id,
                    'title' => $v->title,
                    'description' => $v->description,
                    'price' => $v->price,
                    'unit' => $v->unit,
                    'icon_value' => $v->icon_value,
                    'is_icon' => $v->is_icon,
                    'available_dates' => $v->available_dates ?? [],
                ]),
            ];
        })->values();
    }

    public static function getPublicMealPackages()
    {
        $items = MealPackage::where('is_published', true)->orderBy('sort_order')->get();

        return $items->map(function ($item) {

            return [
                'id' => $item->id,
                'title' => $item->title,
                'description' => $item->description,

                'price' => $item->price,

                // UI representation (icon OR image)
                'icon_value' => $item->icon_value,
                'is_icon' => $item->is_icon,

                // UI control
                'is_variant' => $item->is_variant

            ];
        })->values();
    }
}
