using FluentAssertions;
using Itenso.TimePeriod;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Tests.Helpers;
using Wino.Services;
using Xunit;

namespace Wino.Core.Tests.Services;

/// <summary>
/// Tests for CalendarService, focusing on the GetCalendarEventsAsync method.
/// Note: Recurring event occurrences are now synced from the server as individual instances,
/// not calculated locally from recurrence patterns.
/// </summary>
public class CalendarServiceTests : IAsyncLifetime
{
    private InMemoryDatabaseService _databaseService = null!;
    private CalendarService _calendarService = null!;
    private AccountCalendar _testCalendar = null!;

    public async Task InitializeAsync()
    {
        _databaseService = new InMemoryDatabaseService();
        await _databaseService.InitializeAsync();
        _calendarService = new CalendarService(_databaseService);

        // Create a test calendar
        _testCalendar = new AccountCalendar
        {
            Id = Guid.NewGuid(),
            AccountId = Guid.NewGuid(),
            Name = "Test Calendar",
            TimeZone = "UTC",
            IsPrimary = true,
            BackgroundColorHex = "#FF5733",
            TextColorHex = "#FFFFFF"
        };

        await _calendarService.InsertAccountCalendarAsync(_testCalendar);
    }

    public async Task DisposeAsync()
    {
        await _databaseService.DisposeAsync();
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithNoEvents_ReturnsEmptyList()
    {
        // Arrange
        var period = new TimeRange(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(7));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().BeEmpty();
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithSingleNonRecurringEvent_ReturnsEvent()
    {
        // Arrange
        var startDate = new DateTime(2025, 1, 15, 10, 0, 0, DateTimeKind.Utc);
        var calendarItem = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Team Meeting",
            Description = "Weekly sync",
            StartDate = startDate,
            DurationInSeconds = 3600, // 1 hour
            CalendarId = _testCalendar.Id,
            IsHidden = false
        };

        await _calendarService.CreateNewCalendarItemAsync(calendarItem, null);

        var period = new TimeRange(
            new DateTime(2025, 1, 15, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 16, 0, 0, 0, DateTimeKind.Utc));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().HaveCount(1);
        result[0].Title.Should().Be("Team Meeting");
        result[0].StartDate.Should().Be(startDate);
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithNonRecurringEvent_OutsidePeriod_ReturnsEmpty()
    {
        // Arrange
        var startDate = new DateTime(2025, 1, 15, 10, 0, 0, DateTimeKind.Utc);
        var calendarItem = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Team Meeting",
            StartDate = startDate,
            DurationInSeconds = 3600,
            CalendarId = _testCalendar.Id,
            IsHidden = false
        };

        await _calendarService.CreateNewCalendarItemAsync(calendarItem, null);

        // Query for a different week
        var period = new TimeRange(
            new DateTime(2025, 1, 22, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 29, 0, 0, 0, DateTimeKind.Utc));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().BeEmpty();
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithHiddenEvent_ExcludesFromResults()
    {
        // Arrange
        var startDate = new DateTime(2025, 1, 15, 10, 0, 0, DateTimeKind.Utc);
        var hiddenEvent = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Hidden Event",
            StartDate = startDate,
            DurationInSeconds = 3600,
            CalendarId = _testCalendar.Id,
            IsHidden = true
        };

        await _calendarService.CreateNewCalendarItemAsync(hiddenEvent, null);

        var period = new TimeRange(
            new DateTime(2025, 1, 15, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 16, 0, 0, 0, DateTimeKind.Utc));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().BeEmpty("because hidden events should be excluded");
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithAllDayEvent_ReturnsEvent()
    {
        // Arrange
        var startDate = new DateTime(2025, 1, 15, 0, 0, 0, DateTimeKind.Utc); // Midnight
        var allDayEvent = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Company Holiday",
            StartDate = startDate,
            DurationInSeconds = 86400, // 24 hours
            CalendarId = _testCalendar.Id,
            IsHidden = false
        };

        await _calendarService.CreateNewCalendarItemAsync(allDayEvent, null);

        var period = new TimeRange(
            new DateTime(2025, 1, 15, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 16, 0, 0, 0, DateTimeKind.Utc));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().HaveCount(1);
        result[0].Title.Should().Be("Company Holiday");
        result[0].IsAllDayEvent.Should().BeTrue();
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithMultipleCalendars_ReturnsOnlyRequestedCalendarEvents()
    {
        // Arrange - Create another calendar
        var secondCalendar = new AccountCalendar
        {
            Id = Guid.NewGuid(),
            AccountId = _testCalendar.AccountId,
            Name = "Second Calendar",
            TimeZone = "UTC",
            IsPrimary = false,
            BackgroundColorHex = "#00FF00",
            TextColorHex = "#000000"
        };

        await _calendarService.InsertAccountCalendarAsync(secondCalendar);

        // Add events to both calendars
        var startDate = new DateTime(2025, 1, 15, 10, 0, 0, DateTimeKind.Utc);

        var event1 = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Calendar 1 Event",
            StartDate = startDate,
            DurationInSeconds = 3600,
            CalendarId = _testCalendar.Id,
            IsHidden = false
        };

        var event2 = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Calendar 2 Event",
            StartDate = startDate,
            DurationInSeconds = 3600,
            CalendarId = secondCalendar.Id,
            IsHidden = false
        };

        await _calendarService.CreateNewCalendarItemAsync(event1, null);
        await _calendarService.CreateNewCalendarItemAsync(event2, null);

        var period = new TimeRange(
            new DateTime(2025, 1, 15, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 16, 0, 0, 0, DateTimeKind.Utc));

        // Act - Query only the first calendar
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().HaveCount(1);
        result[0].Title.Should().Be("Calendar 1 Event");
        result[0].CalendarId.Should().Be(_testCalendar.Id);
    }

    [Fact]
    public async Task GetCalendarEventsAsync_WithRecurringChildEvent_ReturnsChildAsRecurringChild()
    {
        // Arrange - Create a parent and child event
        var parentId = Guid.NewGuid();
        var parentEvent = new CalendarItem
        {
            Id = parentId,
            Title = "Parent Recurring Event",
            StartDate = new DateTime(2025, 1, 15, 10, 0, 0, DateTimeKind.Utc),
            DurationInSeconds = 3600,
            CalendarId = _testCalendar.Id,
            IsHidden = false,
            Recurrence = "RRULE:FREQ=DAILY"
        };

        var childEvent = new CalendarItem
        {
            Id = Guid.NewGuid(),
            Title = "Occurrence Instance",
            StartDate = new DateTime(2025, 1, 16, 10, 0, 0, DateTimeKind.Utc),
            DurationInSeconds = 3600,
            CalendarId = _testCalendar.Id,
            RecurringCalendarItemId = parentId,
            IsHidden = false
        };

        await _calendarService.CreateNewCalendarItemAsync(parentEvent, null);
        await _calendarService.CreateNewCalendarItemAsync(childEvent, null);

        var period = new TimeRange(
            new DateTime(2025, 1, 16, 0, 0, 0, DateTimeKind.Utc),
            new DateTime(2025, 1, 17, 0, 0, 0, DateTimeKind.Utc));

        // Act
        var result = await _calendarService.GetCalendarEventsAsync(_testCalendar, period);

        // Assert
        result.Should().HaveCount(1);
        result[0].Title.Should().Be("Occurrence Instance");
        result[0].IsRecurringChild.Should().BeTrue();
        result[0].RecurringCalendarItemId.Should().Be(parentId);
    }
}
