<?php

namespace App\Services;

use App\Models\GoogleMeetConfiguration;
use App\Models\MeetingLog;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

/**
 * MeetProvider
 * 
 * Handles direct API communication with Google Calendar API
 * for creating and managing Google Meet video conferences.
 * 
 * @see https://developers.google.com/calendar/api/v3/reference/events
 */
class MeetProvider
{
    /**
     * Google Calendar API base URL.
     */
    const API_URL = 'https://www.googleapis.com/calendar/v3';

    /**
     * OAuth2 service instance.
     *
     * @var OAuth2Service
     */
    protected OAuth2Service $oauthService;

    /**
     * Create a new MeetProvider instance.
     *
     * @param OAuth2Service $oauthService
     */
    public function __construct(OAuth2Service $oauthService)
    {
        $this->oauthService = $oauthService;
    }

    /**
     * Create a Google Meet video conference.
     *
     * @param GoogleMeetConfiguration $config
     * @param string $summary Meeting title
     * @param string $startTime ISO 8601 datetime string
     * @param string $endTime ISO 8601 datetime string
     * @param array $attendees Array of email addresses
     * @param string|null $description Meeting description
     * @return array
     * @throws \Exception
     */
    public function createMeeting(
        GoogleMeetConfiguration $config,
        string $summary,
        string $startTime,
        string $endTime,
        array $attendees = [],
        ?string $description = null
    ): array {
        // Ensure we have a valid access token
        $accessToken = $this->oauthService->getValidAccessToken($config);

        if (!$accessToken) {
            throw new \Exception('No valid access token available. Please authenticate with Google.');
        }

        // Validate datetime format
        $startTime = $this->validateDateTime($startTime);
        $endTime = $this->validateDateTime($endTime);

        $startTimeMs = microtime(true);

        try {
            // Build event data
            $event = $this->buildEventData($summary, $startTime, $endTime, $attendees, $description);

            // Create calendar event with Meet conference
            $response = Http::withToken($accessToken)
                ->post(self::API_URL . '/calendars/primary/events', $event);

            if (!$response->successful()) {
                $error = $this->parseError($response);
                throw new \Exception($error);
            }

            $responseData = $response->json();
            $executionTime = (int) ((microtime(true) - $startTimeMs) * 1000);

            // Extract meeting details
            $meetingDetails = $this->extractMeetingDetails($responseData);

            // Log successful creation
            MeetingLog::logMeetingCreation(
                true,
                [
                    'summary' => $summary,
                    'attendees_count' => count($attendees),
                    'meeting_link' => $meetingDetails['meeting_link'],
                    'meeting_id' => $meetingDetails['meeting_id'],
                    'response_body' => $responseData,
                ],
                $executionTime
            );

            Log::info('Google Meet created successfully', [
                'meeting_id' => $meetingDetails['meeting_id'],
                'summary' => $summary,
                'execution_time_ms' => $executionTime,
            ]);

            return [
                'success' => true,
                'meeting_link' => $meetingDetails['meeting_link'],
                'meeting_id' => $meetingDetails['meeting_id'],
                'calendar_event_id' => $responseData['id'],
                'summary' => $summary,
                'start_time' => $startTime,
                'end_time' => $endTime,
                'attendees' => $attendees,
                'hangout_link' => $meetingDetails['meeting_link'],
                'response_data' => $responseData,
            ];
        } catch (\Exception $e) {
            $executionTime = (int) ((microtime(true) - $startTimeMs) * 1000);

            // Log failed creation
            MeetingLog::logMeetingCreation(
                false,
                [
                    'summary' => $summary,
                    'attendees_count' => count($attendees),
                ],
                $executionTime,
                $e->getMessage()
            );

            Log::error('Google Meet creation failed', [
                'error' => $e->getMessage(),
                'summary' => $summary,
                'execution_time_ms' => $executionTime,
            ]);

            throw $e;
        }
    }

    /**
     * Test API connectivity.
     *
     * @param GoogleMeetConfiguration $config
     * @return array
     */
    public function testConnection(GoogleMeetConfiguration $config): array
    {
        $startTimeMs = microtime(true);

        try {
            // Ensure we have a valid access token
            $accessToken = $this->oauthService->getValidAccessToken($config);

            if (!$accessToken) {
                throw new \Exception('No valid access token available. Please authenticate with Google.');
            }

            // Test by getting calendar list
            $response = Http::withToken($accessToken)
                ->get(self::API_URL . '/users/me/calendarList', [
                    'maxResults' => 1,
                ]);

            if (!$response->successful()) {
                $error = $this->parseError($response);
                throw new \Exception($error);
            }

            $executionTime = (int) ((microtime(true) - $startTimeMs) * 1000);

            // Log successful test
            MeetingLog::logTest(
                true,
                ['response_body' => $response->json()],
                $executionTime
            );

            return [
                'success' => true,
                'message' => 'Successfully connected to Google Calendar API',
                'calendar_count' => $response->json('items', []) ? count($response->json('items')) : 0,
                'execution_time_ms' => $executionTime,
            ];
        } catch (\Exception $e) {
            $executionTime = (int) ((microtime(true) - $startTimeMs) * 1000);

            // Log failed test
            MeetingLog::logTest(
                false,
                [],
                $executionTime,
                $e->getMessage()
            );

            return [
                'success' => false,
                'message' => 'Failed to connect to Google Calendar API: ' . $e->getMessage(),
                'error' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ];
        }
    }

    /**
     * Validate and format datetime string.
     *
     * @param string $datetime
     * @return string ISO 8601 formatted datetime
     * @throws \Exception
     */
    public function validateDateTime(string $datetime): string
    {
        try {
            $date = Carbon::parse($datetime);
            return $date->toIso8601String();
        } catch (\Exception $e) {
            throw new \Exception("Invalid datetime format: {$datetime}. Expected ISO 8601 format.");
        }
    }

    /**
     * Build event data for Google Calendar API.
     *
     * @param string $summary
     * @param string $startTime
     * @param string $endTime
     * @param array $attendees
     * @param string|null $description
     * @return array
     */
    protected function buildEventData(
        string $summary,
        string $startTime,
        string $endTime,
        array $attendees = [],
        ?string $description = null
    ): array {
        $event = [
            'summary' => strip_tags($summary),
            'description' => $description ? strip_tags($description) : '',
            'start' => [
                'dateTime' => $startTime,
                'timeZone' => config('app.timezone', 'UTC'),
            ],
            'end' => [
                'dateTime' => $endTime,
                'timeZone' => config('app.timezone', 'UTC'),
            ],
            'conferenceData' => [
                'createRequest' => [
                    'requestId' => uniqid('meet-', true),
                    'conferenceSolutionKey' => [
                        'type' => 'hangoutsMeet',
                    ],
                ],
            ],
            'reminders' => [
                'useDefault' => false,
                'overrides' => [
                    ['method' => 'email', 'minutes' => 30],
                    ['method' => 'popup', 'minutes' => 10],
                ],
            ],
        ];

        // Add attendees if provided
        if (!empty($attendees)) {
            $event['attendees'] = array_map(function ($email) {
                return ['email' => $email];
            }, array_filter($attendees, 'filter_var', FILTER_VALIDATE_EMAIL));
        }

        return $event;
    }

    /**
     * Extract meeting details from API response.
     *
     * @param array $responseData
     * @return array
     */
    protected function extractMeetingDetails(array $responseData): array
    {
        $meetingLink = null;
        $meetingId = null;

        // Extract from conferenceData
        if (isset($responseData['conferenceData']['entryPoints'])) {
            foreach ($responseData['conferenceData']['entryPoints'] as $entryPoint) {
                if ($entryPoint['entryPointType'] === 'video') {
                    $meetingLink = $entryPoint['uri'];
                    break;
                }
            }
        }

        // Extract from hangoutLink (fallback)
        if (!$meetingLink && isset($responseData['hangoutLink'])) {
            $meetingLink = $responseData['hangoutLink'];
        }

        // Extract meeting ID from link
        if ($meetingLink) {
            if (preg_match('/\/([a-z]{3}-[a-z]{4}-[a-z]{3})$/i', $meetingLink, $matches)) {
                $meetingId = $matches[1];
            } elseif (preg_match('/[?&]id=([^&]+)/', $meetingLink, $matches)) {
                $meetingId = $matches[1];
            }
        }

        // Get meeting ID from conferenceData
        if (!$meetingId && isset($responseData['conferenceData']['id'])) {
            $meetingId = $responseData['conferenceData']['id'];
        }

        return [
            'meeting_link' => $meetingLink,
            'meeting_id' => $meetingId,
        ];
    }

    /**
     * Parse error from API response.
     *
     * @param \Illuminate\Http\Client\Response $response
     * @return string
     */
    protected function parseError($response): string
    {
        $data = $response->json();

        if (isset($data['error']['message'])) {
            return $data['error']['message'];
        }

        if (isset($data['error_description'])) {
            return $data['error_description'];
        }

        if (isset($data['error'])) {
            if (is_string($data['error'])) {
                return $data['error'];
            }
            return json_encode($data['error']);
        }

        return "API request failed with status {$response->status()}";
    }

    /**
     * Delete a calendar event.
     *
     * @param GoogleMeetConfiguration $config
     * @param string $eventId
     * @return bool
     */
    public function deleteEvent(GoogleMeetConfiguration $config, string $eventId): bool
    {
        $accessToken = $this->oauthService->getValidAccessToken($config);

        if (!$accessToken) {
            return false;
        }

        $response = Http::withToken($accessToken)
            ->delete(self::API_URL . '/calendars/primary/events/' . $eventId);

        return $response->successful();
    }

    /**
     * Get a calendar event by ID.
     *
     * @param GoogleMeetConfiguration $config
     * @param string $eventId
     * @return array|null
     */
    public function getEvent(GoogleMeetConfiguration $config, string $eventId): ?array
    {
        $accessToken = $this->oauthService->getValidAccessToken($config);

        if (!$accessToken) {
            return null;
        }

        $response = Http::withToken($accessToken)
            ->get(self::API_URL . '/calendars/primary/events/' . $eventId);

        if (!$response->successful()) {
            return null;
        }

        return $response->json();
    }
}
