<?php

namespace App\Services\Meet;

use App\Models\CalendarConnection;
use App\Models\Room;
use App\Models\User;
use App\Services\Meet\Calendar\GoogleCalendarProvider;
use App\Services\Meet\Calendar\MailCalendarProvider;
use App\Services\Meet\Calendar\MicrosoftCalendarProvider;
use Illuminate\Support\Facades\Log;

class CalendarService
{
    public function __construct(
        protected GoogleCalendarProvider $google,
        protected MicrosoftCalendarProvider $microsoft,
        protected MailCalendarProvider $mail,
    ) {}

    public function syncRoomCreated(Room $room, User $host): void
    {
        if (! $room->scheduled_at) {
            return;
        }

        if ($host->email) {
            $eventId = $this->mail->createEventForMailbox($host->email, $room);
            if ($eventId) {
                $room->update(['calendar_event_id' => $eventId]);
            }
        }

        $connection = $this->connectionFor($host);
        if ($connection && in_array($connection->provider, ['google', 'microsoft'], true)) {
            $this->provider($connection)->createEvent($connection, $room, $host);
        }
    }

    public function syncRoomUpdated(Room $room, User $host): void
    {
        if (! $room->scheduled_at) {
            return;
        }

        if ($host->email) {
            if ($room->calendar_event_id) {
                $this->mail->updateEventForMailbox($host->email, $room);
            } else {
                $this->syncRoomCreated($room, $host);
            }
        }

        $connection = $this->connectionFor($host);
        if ($connection && $room->calendar_event_id && in_array($connection->provider, ['google', 'microsoft'], true)) {
            $this->provider($connection)->updateEvent($connection, $room, $host);
        }
    }

    public function syncRoomCancelled(Room $room, User $host): void
    {
        if ($host->email && $room->calendar_event_id) {
            $this->mail->deleteEventForMailbox($host->email, $room);
        }

        $connection = $this->connectionFor($host);
        if ($connection && $room->calendar_event_id && in_array($connection->provider, ['google', 'microsoft'], true)) {
            $this->provider($connection)->deleteEvent($connection, $room);
        }

        $room->update(['calendar_event_id' => null]);
    }

    public function connectionFor(User $user, ?string $provider = null): ?CalendarConnection
    {
        $query = CalendarConnection::where('user_ref', $user->ownerRef());

        if ($provider) {
            $query->where('provider', $provider);
        }

        return $query->orderByDesc('updated_at')->first();
    }

    protected function provider(CalendarConnection $connection): GoogleCalendarProvider|MicrosoftCalendarProvider|MailCalendarProvider
    {
        return match ($connection->provider) {
            'microsoft' => $this->microsoft,
            'mail' => $this->mail,
            default => $this->google,
        };
    }

    public function connectMail(User $user, int $organizationId, ?string $mailboxEmail = null): CalendarConnection
    {
        $email = strtolower(trim($mailboxEmail ?: (string) $user->email));

        return CalendarConnection::updateOrCreate(
            ['user_ref' => $user->ownerRef(), 'provider' => 'mail'],
            [
                'owner_ref' => $user->ownerRef(),
                'organization_id' => $organizationId,
                'calendar_id' => $email,
                'access_token' => null,
                'refresh_token' => null,
                'expires_at' => null,
                'metadata' => ['source' => 'ladill_mail'],
            ],
        );
    }

    public function storeTokens(User $user, int $organizationId, string $provider, array $tokens): CalendarConnection
    {
        return CalendarConnection::updateOrCreate(
            ['user_ref' => $user->ownerRef(), 'provider' => $provider],
            [
                'owner_ref' => $user->ownerRef(),
                'organization_id' => $organizationId,
                'access_token' => $tokens['access_token'] ?? null,
                'refresh_token' => $tokens['refresh_token'] ?? null,
                'expires_at' => isset($tokens['expires_in']) ? now()->addSeconds((int) $tokens['expires_in']) : null,
                'calendar_id' => $tokens['calendar_id'] ?? 'primary',
                'metadata' => $tokens['metadata'] ?? null,
            ],
        );
    }

    public function oauthRedirectUrl(string $provider): ?string
    {
        return match ($provider) {
            'google' => $this->google->authorizeUrl(),
            'microsoft' => $this->microsoft->authorizeUrl(),
            default => null,
        };
    }

    public function handleOAuthCallback(string $provider, string $code, User $user, int $organizationId): ?CalendarConnection
    {
        try {
            $tokens = match ($provider) {
                'google' => $this->google->exchangeCode($code),
                'microsoft' => $this->microsoft->exchangeCode($code),
                default => null,
            };

            if (! $tokens) {
                return null;
            }

            return $this->storeTokens($user, $organizationId, $provider, $tokens);
        } catch (\Throwable $e) {
            Log::warning('Calendar OAuth failed', ['provider' => $provider, 'error' => $e->getMessage()]);

            return null;
        }
    }
}
