<?php

namespace WPEventGenius_Vendor\Spatie\IcalendarGenerator\Components;

use Closure;
use DateInterval;
use DateTimeZone;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\ComponentPayload;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Properties\DurationProperty;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Properties\Parameter;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Properties\TextProperty;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Timezones\HasTimezones;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Timezones\TimezoneRangeCollection;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Timezones\TimezoneTransition;
use WPEventGenius_Vendor\Spatie\IcalendarGenerator\Timezones\TimezoneTransitionsResolver;
class Calendar extends Component implements HasTimezones
{
    /** @var \Spatie\IcalendarGenerator\Components\Event[] */
    private array $events = [];
    /** @var \Spatie\IcalendarGenerator\Components\Timezone[] */
    private array $timezones = [];
    private ?string $name = null;
    private ?string $description = null;
    private bool $withoutTimezone = \false;
    private bool $withoutAutoTimezoneComponents = \false;
    private ?DateInterval $refreshInterval = null;
    private ?string $productIdentifier = null;
    private ?string $source = null;
    public static function create(string $name = null): Calendar
    {
        return new self($name);
    }
    public function __construct(string $name = null)
    {
        $this->name = $name;
    }
    public function getComponentType(): string
    {
        return 'VCALENDAR';
    }
    public function getRequiredProperties(): array
    {
        return ['VERSION', 'PRODID'];
    }
    public function name(string $name): Calendar
    {
        $this->name = $name;
        return $this;
    }
    public function description(string $description): Calendar
    {
        $this->description = $description;
        return $this;
    }
    public function productIdentifier(string $identifier): Calendar
    {
        $this->productIdentifier = $identifier;
        return $this;
    }
    /**
     * @param $event \Spatie\IcalendarGenerator\Components\Event|array|Closure
     *
     * @return \Spatie\IcalendarGenerator\Components\Calendar
     */
    public function event($event): Calendar
    {
        if (is_null($event)) {
            return $this;
        }
        $events = array_map(function ($eventToResolve) {
            if (!is_callable($eventToResolve)) {
                return $eventToResolve;
            }
            $newEvent = new Event();
            $eventToResolve($newEvent);
            return $newEvent;
        }, is_array($event) ? $event : [$event]);
        $this->events = array_merge($this->events, $events);
        return $this;
    }
    /**
     * @param $timezone \Spatie\IcalendarGenerator\Components\Timezone|array
     *
     * @return \Spatie\IcalendarGenerator\Components\Calendar
     */
    public function timezone($timezone)
    {
        if (is_null($timezone)) {
            return $this;
        }
        $this->timezones = array_merge($this->timezones, is_array($timezone) ? $timezone : [$timezone]);
        return $this;
    }
    public function withoutTimezone(): Calendar
    {
        $this->withoutTimezone = \true;
        return $this;
    }
    public function withoutAutoTimezoneComponents(): self
    {
        $this->withoutAutoTimezoneComponents = \true;
        return $this;
    }
    public function refreshInterval(int $minutes): Calendar
    {
        $this->refreshInterval = new DateInterval("PT{$minutes}M");
        return $this;
    }
    /**
     * Identifies a location where a client can retrieve updated data for the calendar.
     *
     * @link https://datatracker.ietf.org/doc/html/rfc7986#section-5.8
     */
    public function source(string $source): self
    {
        $this->source = $source;
        return $this;
    }
    public function get(): string
    {
        return $this->toString();
    }
    public function getTimezoneRangeCollection(): TimezoneRangeCollection
    {
        return TimezoneRangeCollection::create()->add(...array_map(fn(Event $event) => $event->getTimezoneRangeCollection(), $this->resolveEvents()));
    }
    protected function payload(): ComponentPayload
    {
        return ComponentPayload::create($this->getComponentType())->property(TextProperty::create('VERSION', '2.0'))->property(TextProperty::create('PRODID', $this->productIdentifier ?? 'spatie/icalendar-generator'))->optional($this->name, fn() => TextProperty::create('NAME', $this->name)->addAlias('X-WR-CALNAME'))->optional($this->description, fn() => TextProperty::create('DESCRIPTION', $this->description)->addAlias('X-WR-CALDESC'))->optional($this->source, fn() => TextProperty::create('SOURCE', $this->source)->addParameter(new Parameter('VALUE', 'URI')))->optional($this->refreshInterval, fn() => DurationProperty::create('REFRESH-INTERVAL', $this->refreshInterval)->addParameter(new Parameter('VALUE', 'DURATION')))->optional($this->refreshInterval, fn() => DurationProperty::create('X-PUBLISHED-TTL', $this->refreshInterval))->subComponent(...$this->resolveTimezones())->subComponent(...$this->resolveEvents());
    }
    private function resolveEvents(): array
    {
        if ($this->withoutTimezone === \false) {
            return $this->events;
        }
        return array_map(fn(Event $event) => $event->withoutTimezone(), $this->events);
    }
    private function resolveTimezones(): array
    {
        if ($this->withoutAutoTimezoneComponents) {
            return $this->timezones;
        }
        $timezones = [];
        foreach ($this->getTimezoneRangeCollection()->get() as $timezoneIdentifier => ['min' => $min, 'max' => $max]) {
            $transitionsResolver = new TimezoneTransitionsResolver(new DateTimeZone($timezoneIdentifier), $min, $max);
            $entries = array_map(fn(TimezoneTransition $transition) => TimezoneEntry::createFromTransition($transition), $transitionsResolver->getTransitions());
            $timezones[] = Timezone::create($timezoneIdentifier)->entry($entries);
        }
        return $timezones;
    }
}
