<?php

namespace App\Http\Controllers;

use App\Models\CalendarInstance;
use App\Models\EventMeta;
use Illuminate\Support\Facades\Response;
use Carbon\Carbon;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Reader;

class IcsController extends Controller
{
    public function download(string $calendarUri)
    {
        $instance = CalendarInstance::where('uri', $calendarUri)
            ->with('meta')
            ->firstOrFail();

        $isRemote = (bool) ($instance->meta?->is_remote ?? false);
        $isShared = (bool) ($instance->meta?->is_shared ?? false);
        if ($isRemote || !$isShared) {
            abort(404);
        }

        $calendar = $instance->calendar()->with(['events.meta.venue'])->firstOrFail();
        $timezone = $instance->timezone ?? 'UTC';

        $ical = $this->generateICalendarFeed($calendar->events, $timezone);

        return Response::make($ical, 200, [
            'Content-Type' => 'text/calendar; charset=utf-8',
            'Content-Disposition' => 'inline; filename="' . $calendarUri . '.ics"',
        ]);
    }

    protected function generateICalendarFeed($events, string $tz): string
    {
        $vcalendar = new VCalendar();
        $vcalendar->add('VERSION', '2.0');
        $vcalendar->add('PRODID', '-//Kithkin Calendar//EN');
        $vcalendar->add('CALSCALE', 'GREGORIAN');
        $vcalendar->add('METHOD', 'PUBLISH');

        foreach ($events as $event) {
            $ical = $event->calendardata ?? null;

            if ($ical) {
                try {
                    $parsed = Reader::read($ical);
                    foreach ($parsed->select('VEVENT') as $vevent) {
                        $cloned = clone $vevent;
                        $this->applyLocationProperties($cloned, $event->meta);
                        $vcalendar->add($cloned);
                    }
                    continue;
                } catch (\Throwable $e) {
                    // fall through to meta-based output
                }
            }

            $meta = $event->meta;
            if (!$meta) {
                continue;
            }

            $vevent = $vcalendar->add('VEVENT', []);
            $vevent->add('UID', $event->uid);
            $vevent->add('SUMMARY', $meta->title ?? '(Untitled)');
            $vevent->add('DESCRIPTION', $meta->description ?? '');

            if ($meta->all_day && $meta->start_on && $meta->end_on) {
                $vevent->add('DTSTART', Carbon::parse($meta->start_on), ['VALUE' => 'DATE']);
                $vevent->add('DTEND', Carbon::parse($meta->end_on), ['VALUE' => 'DATE']);
            } elseif ($meta->start_at && $meta->end_at) {
                $start = Carbon::parse($meta->start_at)->timezone($tz);
                $end = Carbon::parse($meta->end_at)->timezone($tz);
                $vevent->add('DTSTART', $start, ['TZID' => $tz]);
                $vevent->add('DTEND', $end, ['TZID' => $tz]);
            } else {
                continue;
            }
            if ($event->lastmodified) {
                $vevent->add('DTSTAMP', Carbon::createFromTimestamp($event->lastmodified)->utc());
            }
            $this->applyLocationProperties($vevent, $meta);
        }

        return $vcalendar->serialize();
    }

    protected function applyLocationProperties($vevent, ?EventMeta $meta): void
    {
        if (!$meta) {
            return;
        }

        $venue = $meta->venue;
        $label = trim((string) ($meta->location_label ?? $meta->location ?? $venue?->display_name ?? ''));

        $address = trim((string) ($venue?->raw_address ?? ''));
        if ($address === '') {
            $address = trim(implode("\n", array_filter([
                $venue?->street,
                trim(implode(' ', array_filter([$venue?->city, $venue?->state, $venue?->postal]))),
                $venue?->country,
            ])));
        }

        $locationText = trim(implode("\n", array_filter([
            $label !== '' ? $label : null,
            $address !== '' ? $address : null,
        ])));

        if (!isset($vevent->LOCATION) && $locationText !== '') {
            $vevent->add('LOCATION', $locationText);
        }

        $lat = $venue?->lat;
        $lon = $venue?->lon;
        if (!is_numeric($lat) || !is_numeric($lon)) {
            return;
        }

        if (!isset($vevent->GEO)) {
            $vevent->add('GEO', sprintf('%.6f;%.6f', $lat, $lon));
        }

        if (!isset($vevent->{'X-APPLE-STRUCTURED-LOCATION'})) {
            $params = [
                'VALUE' => 'URI',
            ];

            if ($address !== '') {
                $params['X-ADDRESS'] = $address;
            }
            if ($label !== '') {
                $params['X-TITLE'] = $label;
            }

            if (!empty($params['X-ADDRESS']) || !empty($params['X-TITLE'])) {
                $params['X-APPLE-REFERENCEFRAME'] = '1';
                $params['X-APPLE-RADIUS'] = '150';
                $vevent->add(
                    'X-APPLE-STRUCTURED-LOCATION',
                    sprintf('geo:%.6f,%.6f', $lat, $lon),
                    $params
                );
            }
        }
    }

    protected function escape(?string $text): string
    {
        return str_replace(['\\', ';', ',', "\n"], ['\\\\', '\;', '\,', '\n'], $text ?? '');
    }
}
