<?php

namespace App\Services\Event;

use App\Models\Event;
use Carbon\Carbon;
use DateTimeZone;
use Illuminate\Support\Str;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Reader;
use Sabre\VObject\Recur\EventIterator;

class EventRecurrence
{
    /**
     * Build a VCALENDAR string from core fields and optional recurrence.
     */
    public function buildCalendar(array $data): string
    {
        $vcalendar = new VCalendar();
        $vcalendar->add('PRODID', '-//Kithkin//Laravel CalDAV//EN');
        $vcalendar->add('VERSION', '2.0');
        $vcalendar->add('CALSCALE', 'GREGORIAN');
        $vevent = $vcalendar->add('VEVENT', []);

        $uid = $data['uid'];
        $startUtc = $data['start_utc'];
        $endUtc = $data['end_utc'];
        $tzid = $data['tzid'] ?? null;

        $vevent->add('UID', $uid);
        $vevent->add('DTSTAMP', $startUtc->copy()->utc());

        if ($tzid) {
            $startLocal = $startUtc->copy()->tz($tzid);
            $endLocal = $endUtc->copy()->tz($tzid);
            $vevent->add('DTSTART', $startLocal, ['TZID' => $tzid]);
            $vevent->add('DTEND', $endLocal, ['TZID' => $tzid]);
        } else {
            $vevent->add('DTSTART', $startUtc->copy()->utc());
            $vevent->add('DTEND', $endUtc->copy()->utc());
        }

        if (!empty($data['summary'])) {
            $vevent->add('SUMMARY', $data['summary']);
        }

        if (!empty($data['description'])) {
            $vevent->add('DESCRIPTION', $data['description']);
        }

        if (!empty($data['location'])) {
            $vevent->add('LOCATION', $data['location']);
        }

        $rrule = $data['rrule'] ?? null;
        if ($rrule) {
            $vevent->add('RRULE', $rrule);
        }

        $exdates = $data['exdate'] ?? [];
        if (!empty($exdates)) {
            foreach ($exdates as $ex) {
                $dt = Carbon::parse($ex, $tzid ?: 'UTC');
                if ($tzid) {
                    $vevent->add('EXDATE', $dt, ['TZID' => $tzid]);
                } else {
                    $vevent->add('EXDATE', $dt->utc());
                }
            }
        }

        $rdates = $data['rdate'] ?? [];
        if (!empty($rdates)) {
            foreach ($rdates as $r) {
                $dt = Carbon::parse($r, $tzid ?: 'UTC');
                if ($tzid) {
                    $vevent->add('RDATE', $dt, ['TZID' => $tzid]);
                } else {
                    $vevent->add('RDATE', $dt->utc());
                }
            }
        }

        return $vcalendar->serialize();
    }

    /**
     * Check if a stored event contains recurrence data.
     */
    public function isRecurring(Event $event): bool
    {
        $extra = $event->meta?->extra ?? [];
        if (!empty($extra['rrule'])) {
            return true;
        }

        return Str::contains($event->calendardata ?? '', ['RRULE', 'RDATE', 'EXDATE']);
    }

    /**
     * Expand recurring instances within the requested range.
     *
     * Returns an array of ['start' => Carbon, 'end' => Carbon, 'recurrence_id' => string|null]
     */
    public function expand(Event $event, Carbon $rangeStart, Carbon $rangeEnd): array
    {
        $vcalendar = $this->readCalendar($event->calendardata);
        if (!$vcalendar || empty($vcalendar->VEVENT)) {
            return [];
        }

        $vevent = $vcalendar->VEVENT;
        $uid = (string) $vevent->UID;

        $startTz = $vevent->DTSTART?->getDateTime()?->getTimezone()
            ?? new DateTimeZone('UTC');

        $iter = new EventIterator($vcalendar, $uid);
        $iter->fastForward($rangeStart->copy()->setTimezone($startTz)->toDateTime());

        $items = [];
        while ($iter->valid()) {
            $start = Carbon::instance($iter->getDTStart());
            $end = Carbon::instance($iter->getDTEnd());

            if ($start->gt($rangeEnd)) {
                break;
            }

            $startUtc = $start->copy()->utc();
            $endUtc = $end->copy()->utc();
            $items[] = [
                'start' => $startUtc,
                'end' => $endUtc,
                'recurrence_id' => $startUtc->format('Ymd\\THis\\Z'),
            ];

            $iter->next();
        }

        return $items;
    }

    /**
     * Resolve a single occurrence by its DTSTART.
     */
    public function resolveOccurrence(Event $event, Carbon $occurrenceStart): ?array
    {
        $rangeStart = $occurrenceStart->copy()->subDay();
        $rangeEnd = $occurrenceStart->copy()->addDay();

        foreach ($this->expand($event, $rangeStart, $rangeEnd) as $occ) {
            if ($occ['start']->equalTo($occurrenceStart)) {
                return $occ;
            }
        }

        return null;
    }

    public function extractRrule(Event $event): ?string
    {
        $vcalendar = $this->readCalendar($event->calendardata);
        if (!$vcalendar || empty($vcalendar->VEVENT)) {
            return null;
        }

        $vevent = $vcalendar->VEVENT;
        return isset($vevent->RRULE) ? (string) $vevent->RRULE : null;
    }

    private function readCalendar(?string $ical): ?VCalendar
    {
        if (!$ical) {
            return null;
        }

        try {
            return Reader::read($ical);
        } catch (\Throwable $e) {
            return null;
        }
    }
}
