<?php
/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Core\Util\DateRange;

use BookiFlex\Core\Exception\InvalidArgumentException;
use DateTimeInterface;
use FilterIterator;

defined( 'ABSPATH' ) || exit;

/**
 * The WeeklyDayFilter class is responsible for filtering out days of the week that are not allowed.
 * It extends the FilterIterator class and overrides the accept method to filter out days of the week.
 */
class WeeklyDayFilter extends FilterIterator
{
    protected DateRange $originDateRange;

    private bool $mon = true;
    private bool $tue = true;
    private bool $wed = true;
    private bool $thu = true;
    private bool $fri = true;
    private bool $sat = true;
    private bool $sun = true;

    /**
     * @param DateRange $dateRange
     * @param array $allowDays as ['mon' => true, 'tue' => false, 'wed' => true]
     */
    public function __construct(DateRange $dateRange, array $allowDays = [])
    {
        parent::__construct($dateRange);
        $this->originDateRange = $dateRange;

        foreach ($allowDays as $day => $value) {
            if (isset($this->$day)) {
                $this->$day = (bool) $value;
            }
        }
    }

    public function accept(): bool
    {
        $date = $this->getInnerIterator()->current();

        if ($this->isAllowedDay($date)) {
            return true;
        }

        return false;
    }

    public function getOriginDateRange(): DateRange
    {
        return $this->originDateRange;
    }

    /**
     * @return bool
     */
    public function isMon(): bool
    {
        return $this->mon;
    }

    /**
     * @return bool
     */
    public function isTue(): bool
    {
        return $this->tue;
    }

    /**
     * @return bool
     */
    public function isWed(): bool
    {
        return $this->wed;
    }

    /**
     * @return bool
     */
    public function isThu(): bool
    {
        return $this->thu;
    }

    /**
     * @return bool
     */
    public function isFri(): bool
    {
        return $this->fri;
    }

    /**
     * @return bool
     */
    public function isSat(): bool
    {
        return $this->sat;
    }

    /**
     * @return bool
     */
    public function isSun(): bool
    {
        return $this->sun;
    }

    protected function isAllowedDay(DateTimeInterface $date): bool
    {
        $dayOfWeek = strtolower($date->format('D'));

        return $this->$dayOfWeek;
    }

    /**
     * @param string $dateFrom in format 'Y-m-d'
     * @param string|null $dateTo in format 'Y-m-d'. If not set then we extend until the end of the year
     * @param array $allowedDays as ['mon' => true, 'tue' => false, 'wed' => true]
     *
     * @throws InvalidArgumentException
     */
    public static function createFilteredDateRange(
        string $dateFrom,
        ?string $dateTo,
        array $allowedDays
    ): self {
        $today = new \DateTimeImmutable('today');

        $from = (\DateTimeImmutable::createFromFormat('Y-m-d', $dateFrom))
            ->setTime(0, 0);

        // It is forbidden to make changes for the past dates
        if ($from < $today) {
            $from = clone $today;
        }

        // If dateTo not set then we extend until the end of the year
        $to = (!$dateTo)
            ? (\DateTimeImmutable::createFromFormat('Y-m-d', $from->format('Y') . '-12-31'))
                ->setTime(23, 59, 59)
            : (\DateTimeImmutable::createFromFormat('Y-m-d', $dateTo))
                ->setTime(23, 59, 59);

        if ($today > $to) {
            throw new \InvalidArgumentException('It is forbidden to make changes for the past dates.');
        }

        if ($today > $from) {
            $from = $today;
        }

        $dateRange = new DateRange($from, $to, DateRange::DAY_STRATEGY);

        return new WeeklyDayFilter($dateRange, $allowedDays);
    }
}
