using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Graph.Models;
using Serilog;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Extensions;
using Wino.Services;

namespace Wino.Core.Integration.Processors;

public class OutlookChangeProcessor(IDatabaseService databaseService,
                                    IFolderService folderService,
                                    ICalendarService calendarService,
                                    IMailService mailService,
                                    IAccountService accountService,
                                    IMimeFileService mimeFileService) : DefaultChangeProcessor(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
    , IOutlookChangeProcessor
{

    public Task<string> ResetAccountDeltaTokenAsync(Guid accountId)
        => AccountService.UpdateSyncIdentifierRawAsync(accountId, string.Empty);

    public async Task<string> ResetFolderDeltaTokenAsync(Guid folderId)
    {
        var folder = await FolderService.GetFolderAsync(folderId);

        folder.DeltaToken = null;

        await FolderService.UpdateFolderAsync(folder);

        return string.Empty;
    }

    public Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier)
        => Connection.ExecuteAsync("UPDATE MailItemFolder SET DeltaToken = ? WHERE Id = ?", synchronizationIdentifier, folderId);

    public async Task ManageCalendarEventAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
    {
        // We parse the occurrences based on the parent event.
        // There is literally no point to store them because
        // type=Exception events are the exceptional childs of recurrency parent event.

        if (calendarEvent.Type == EventType.Occurrence) return;

        var savingItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);

        Guid savingItemId = Guid.Empty;
        bool isNewItem = savingItem == null;

        if (savingItem != null)
            savingItemId = savingItem.Id;
        else
        {
            savingItemId = Guid.NewGuid();
            savingItem = new CalendarItem() { Id = savingItemId };
        }

        DateTimeOffset eventStartDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.Start);
        DateTimeOffset eventEndDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.End);

        var durationInSeconds = (eventEndDateTimeOffset - eventStartDateTimeOffset).TotalSeconds;

        // Store dates as UTC in the database
        savingItem.RemoteEventId = calendarEvent.Id;
        savingItem.StartDate = eventStartDateTimeOffset.UtcDateTime;
        savingItem.DurationInSeconds = durationInSeconds;

        // Store the timezone information from the event
        // This preserves the original timezone from Outlook, allowing proper reconstruction later
        // If no timezone is provided, null will indicate UTC
        savingItem.StartTimeZone = calendarEvent.Start?.TimeZone;
        savingItem.EndTimeZone = calendarEvent.End?.TimeZone;

        savingItem.Title = calendarEvent.Subject;
        savingItem.Description = calendarEvent.Body?.Content;
        savingItem.Location = calendarEvent.Location?.DisplayName;

        if (calendarEvent.Type == EventType.Exception && !string.IsNullOrEmpty(calendarEvent.SeriesMasterId))
        {
            // This is a recurring event exception.
            // We need to find the parent event and set it as recurring event id.

            var parentEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.SeriesMasterId);

            if (parentEvent != null)
            {
                savingItem.RecurringCalendarItemId = parentEvent.Id;
            }
            else
            {
                Log.Warning($"Parent recurring event is missing for event. Skipping creation of {calendarEvent.Id}");
                return;
            }
        }

        // Convert the recurrence pattern to string for parent recurring events.
        if (calendarEvent.Type == EventType.SeriesMaster && calendarEvent.Recurrence != null)
        {
            savingItem.Recurrence = OutlookIntegratorExtensions.ToRfc5545RecurrenceString(calendarEvent.Recurrence);
        }

        savingItem.HtmlLink = calendarEvent.WebLink;
        savingItem.CalendarId = assignedCalendar.Id;
        savingItem.OrganizerEmail = calendarEvent.Organizer?.EmailAddress?.Address;
        savingItem.OrganizerDisplayName = calendarEvent.Organizer?.EmailAddress?.Name;
        savingItem.IsHidden = false;

        // Set timestamps
        if (calendarEvent.CreatedDateTime.HasValue)
            savingItem.CreatedAt = calendarEvent.CreatedDateTime.Value;

        if (calendarEvent.LastModifiedDateTime.HasValue)
            savingItem.UpdatedAt = calendarEvent.LastModifiedDateTime.Value;

        // Set visibility
        if (calendarEvent.Sensitivity != null)
        {
            savingItem.Visibility = calendarEvent.Sensitivity.Value switch
            {
                Sensitivity.Normal => CalendarItemVisibility.Public,
                Sensitivity.Personal => CalendarItemVisibility.Private,
                Sensitivity.Private => CalendarItemVisibility.Private,
                Sensitivity.Confidential => CalendarItemVisibility.Confidential,
                _ => CalendarItemVisibility.Public
            };
        }
        else
        {
            savingItem.Visibility = CalendarItemVisibility.Public;
        }

        // Set IsLocked based on whether the user is the organizer
        // Read-only events are those where the current user is not the organizer
        savingItem.IsLocked = calendarEvent.IsOrganizer.HasValue && !calendarEvent.IsOrganizer.Value;

        if (calendarEvent.ResponseStatus?.Response != null)
        {
            switch (calendarEvent.ResponseStatus.Response.Value)
            {
                case ResponseType.None:
                case ResponseType.NotResponded:
                    savingItem.Status = CalendarItemStatus.NotResponded;
                    break;
                case ResponseType.TentativelyAccepted:
                    savingItem.Status = CalendarItemStatus.Tentative;
                    break;
                case ResponseType.Accepted:
                case ResponseType.Organizer:
                    savingItem.Status = CalendarItemStatus.Confirmed;
                    break;
                case ResponseType.Declined:
                    savingItem.Status = CalendarItemStatus.Cancelled;
                    savingItem.IsHidden = true;
                    break;
                default:
                    break;
            }
        }
        else
        {
            savingItem.Status = CalendarItemStatus.Confirmed;
        }

        // Prepare attendees list
        List<CalendarEventAttendee> attendees = null;
        if (calendarEvent.Attendees != null)
        {
            attendees = calendarEvent.Attendees.Select(a => a.CreateAttendee(savingItemId)).ToList();
        }

        // Use CalendarService to create or update the event
        if (isNewItem)
        {
            // New item - use CreateNewCalendarItemAsync
            await CalendarService.CreateNewCalendarItemAsync(savingItem, attendees).ConfigureAwait(false);
        }
        else
        {
            // Existing item - use UpdateCalendarItemAsync
            await CalendarService.UpdateCalendarItemAsync(savingItem, attendees).ConfigureAwait(false);
        }
    }
}
