using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using CommunityToolkit.WinUI;
using Itenso.TimePeriod;
using Microsoft.UI;
using Microsoft.UI.Composition;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using SkiaSharp;
using SkiaSharp.Views.Windows;
using Windows.ApplicationModel.DataTransfer;
using Windows.Foundation;
using Windows.UI;
using Wino.Calendar.ViewModels.Data;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar;

namespace Wino.Calendar.Controls;

public sealed partial class CalendarPeriodControl : UserControl, INotifyPropertyChanged
{
    private static readonly TimeSpan SizeRefreshDebounceInterval = TimeSpan.FromMilliseconds(75);
    private const double SizeChangeThreshold = 0.5d;
    private const double TimedHourColumnWidth = 64d;
    private const double TimedGridIntervalMinutes = 30d;
    private const double TimedSelectionIntervalMinutes = 30d;
    private const double TimedItemRightSpacing = 10d;
    private VisibleDateRange _currentRange = new(
        CalendarDisplayType.Month,
        DateOnly.FromDateTime(DateTime.Today),
        DateOnly.FromDateTime(DateTime.Today),
        DateOnly.FromDateTime(DateTime.Today),
        DateOnly.FromDateTime(DateTime.Today),
        1,
        true,
        true,
        [DateOnly.FromDateTime(DateTime.Today)]);

    private TimedCalendarLayoutResult _timedLayout = new([], 0, []);
    private MonthCalendarLayoutResult _monthLayout = new(0, 0, [], []);
    private INotifyCollectionChanged? _observableItemsSource;
    private double _timedDayWidth;
    private double _timedAllDayHeight;
    private double _monthCellWidth;
    private double _monthCellHeight;
    private bool _hasPresentedState;
    private bool _refreshPending = true;
    private bool _refreshScheduled;
    private CalendarDropTargetInfo? _hoverTarget;
    private CalendarDragPackage? _activeDragPackage;
    private CalendarDisplayType _lastDisplayMode = CalendarDisplayType.Month;
    private DateOnly _lastDisplayDate = DateOnly.FromDateTime(DateTime.Today);
    private DayOfWeek _lastFirstDayOfWeek = DayOfWeek.Monday;
    private readonly DispatcherQueueTimer _sizeRefreshTimer;

    [GeneratedDependencyProperty]
    public partial VisibleDateRange? VisibleRange { get; set; }

    [GeneratedDependencyProperty]
    public partial CalendarSettings? CalendarSettings { get; set; }

    [GeneratedDependencyProperty]
    public partial IReadOnlyList<CalendarItemViewModel>? CalendarItems { get; set; }

    [GeneratedDependencyProperty]
    public partial string? TimedHeaderDateFormat { get; set; }

    [GeneratedDependencyProperty]
    public partial Brush? DefaultHourBackground { get; set; }

    [GeneratedDependencyProperty]
    public partial Brush? WorkHourBackground { get; set; }

    [GeneratedDependencyProperty]
    public partial Brush? SelectedSlotBackground { get; set; }

    [GeneratedDependencyProperty]
    public partial Brush? HoverSlotBackground { get; set; }

    [GeneratedDependencyProperty]
    public partial DateTime? SelectedDateTime { get; set; }

    public CalendarPeriodControl()
    {
        InitializeComponent();

        _sizeRefreshTimer = DispatcherQueue.CreateTimer();
        _sizeRefreshTimer.Interval = SizeRefreshDebounceInterval;
        _sizeRefreshTimer.IsRepeating = false;
        _sizeRefreshTimer.Tick += SizeRefreshTimerTick;
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler<CalendarEmptySlotTappedEventArgs>? EmptySlotTapped;
    public event EventHandler<CalendarItemDroppedEventArgs>? CalendarItemDropped;

    private ObservableCollection<HeaderTextLayout> TimedHeaderTextsCollection { get; } = [];
    private ObservableCollection<HeaderTextLayout> MonthHeaderTextsCollection { get; } = [];
    private ObservableCollection<TimedItemLayout> TimedItemsCollection { get; } = [];
    private ObservableCollection<TimedItemLayout> TimedAllDayItemsCollection { get; } = [];
    private ObservableCollection<MonthCellLabelLayout> MonthCellLabelsCollection { get; } = [];
    private ObservableCollection<MonthItemLayout> MonthItemsCollection { get; } = [];

    public IEnumerable<HeaderTextLayout> TimedHeaderTexts => TimedHeaderTextsCollection;
    public IEnumerable<HeaderTextLayout> MonthHeaderTexts => MonthHeaderTextsCollection;
    public IEnumerable<TimedItemLayout> TimedItems => TimedItemsCollection;
    public IEnumerable<TimedItemLayout> TimedAllDayItems => TimedAllDayItemsCollection;
    public IEnumerable<MonthCellLabelLayout> MonthCellLabels => MonthCellLabelsCollection;
    public IEnumerable<MonthItemLayout> MonthItems => MonthItemsCollection;

    public double TimedDayWidth
    {
        get => _timedDayWidth;
        private set
        {
            if (_timedDayWidth == value)
            {
                return;
            }

            _timedDayWidth = value;
            OnPropertyChanged();
        }
    }

    public double MonthCellWidth
    {
        get => _monthCellWidth;
        private set
        {
            if (_monthCellWidth == value)
            {
                return;
            }

            _monthCellWidth = value;
            OnPropertyChanged();
        }
    }

    public double MonthCellHeight
    {
        get => _monthCellHeight;
        private set
        {
            if (_monthCellHeight == value)
            {
                return;
            }

            _monthCellHeight = value;
            OnPropertyChanged();
        }
    }

    public double TimedAllDayHeight
    {
        get => _timedAllDayHeight;
        private set
        {
            if (_timedAllDayHeight == value)
            {
                return;
            }

            _timedAllDayHeight = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(HasTimedAllDayItems));
        }
    }

    public bool HasTimedAllDayItems => TimedAllDayHeight > 0d;

    public double TimelineHeight => TimedCalendarLayoutCalculator.GetTimelineHeight(GetHourHeight());

    partial void OnVisibleRangeChanged(VisibleDateRange? newValue) => RequestRefresh();
    partial void OnCalendarSettingsChanged(CalendarSettings? newValue) => RequestRefresh();
    partial void OnTimedHeaderDateFormatChanged(string? newValue) => RequestRefresh();
    partial void OnSelectedSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
    partial void OnHoverSlotBackgroundChanged(Brush? newValue) => InvalidateStructureCanvases();
    partial void OnSelectedDateTimeChanged(DateTime? newValue) => InvalidateStructureCanvases();

    partial void OnCalendarItemsChanged(IReadOnlyList<CalendarItemViewModel>? newValue)
    {
        DetachCurrentItemsSource();
        AttachItemsSource(newValue);
        RequestRefresh();
    }

    private void ControlSizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (!HasMeaningfulSizeChange(e))
        {
            return;
        }

        var isLiveResize = _hasPresentedState &&
                           e.PreviousSize.Width > 0 &&
                           e.PreviousSize.Height > 0;

        if (isLiveResize)
        {
            _refreshPending = true;
            _sizeRefreshTimer.Stop();
            _sizeRefreshTimer.Start();
            return;
        }

        if (!_refreshPending)
        {
            return;
        }

        QueueRefresh();
    }

    private IEnumerable<CalendarItemViewModel> CurrentItems => CalendarItems ?? [];

    private void AttachItemsSource(IReadOnlyList<CalendarItemViewModel>? itemsSource)
    {
        if (itemsSource is INotifyCollectionChanged observableItemsSource)
        {
            _observableItemsSource = observableItemsSource;
            _observableItemsSource.CollectionChanged += ItemsSourceCollectionChanged;
        }
    }

    private void DetachItemsSource(IReadOnlyList<CalendarItemViewModel>? itemsSource)
    {
        var observableItemsSource = itemsSource as INotifyCollectionChanged;

        if (observableItemsSource is not null)
        {
            observableItemsSource.CollectionChanged -= ItemsSourceCollectionChanged;
        }

        if (ReferenceEquals(_observableItemsSource, observableItemsSource))
        {
            _observableItemsSource = null;
        }
    }

    private void DetachCurrentItemsSource()
    {
        if (_observableItemsSource is not null)
        {
            _observableItemsSource.CollectionChanged -= ItemsSourceCollectionChanged;
            _observableItemsSource = null;
        }
    }

    private void ItemsSourceCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) => RequestRefresh();

    private void RequestRefresh()
    {
        _refreshPending = true;
        QueueRefresh();
    }

    private void InvalidateStructureCanvases()
    {
        TimedAllDayCanvas.Invalidate();
        TimedStructureCanvas.Invalidate();
        MonthStructureCanvas.Invalidate();
    }

    private void Refresh()
    {
        if (!_refreshPending || !IsLoaded || ActualWidth <= 0 || VisibleRange is null || CalendarSettings is null)
        {
            return;
        }

        var transition = GetTransitionInfo();
        _currentRange = CreateLayoutRange(VisibleRange, CalendarSettings);

        if (VisibleRange.DisplayType == CalendarDisplayType.Month)
        {
            RefreshMonthView();
        }
        else
        {
            RefreshTimedView();
        }

        RunTransition(transition);
        _hasPresentedState = true;
        _refreshPending = false;
        _lastDisplayMode = VisibleRange.DisplayType;
        _lastDisplayDate = VisibleRange.AnchorDate;
        _lastFirstDayOfWeek = CalendarSettings.FirstDayOfWeek;

        Debug.WriteLine($"Refreshed control.");
    }

    private static bool HasMeaningfulSizeChange(SizeChangedEventArgs e)
        => Math.Abs(e.NewSize.Width - e.PreviousSize.Width) > SizeChangeThreshold ||
           Math.Abs(e.NewSize.Height - e.PreviousSize.Height) > SizeChangeThreshold;

    private void QueueRefresh()
    {
        if (_refreshScheduled)
        {
            return;
        }

        _refreshScheduled = true;
        DispatcherQueue.TryEnqueue(() =>
        {
            _refreshScheduled = false;
            Refresh();
        });
    }

    private void ControlUnloaded(object sender, RoutedEventArgs e)
    {
        DetachCurrentItemsSource();
        _sizeRefreshTimer.Stop();
        _sizeRefreshTimer.Tick -= SizeRefreshTimerTick;
    }

    private void SizeRefreshTimerTick(DispatcherQueueTimer sender, object args)
    {
        sender.Stop();
        QueueRefresh();
    }

    private void RefreshTimedView()
    {
        TimedRoot.Visibility = Visibility.Visible;
        MonthRoot.Visibility = Visibility.Collapsed;
        ResetTimedVisualState();

        var timedSurfaceWidth = GetTimedSurfaceWidth();

        TimedDayWidth = _currentRange.Dates.Count == 0 ? 0d : timedSurfaceWidth / _currentRange.Dates.Count;
        TimedAllDayHeight = TimedCalendarLayoutCalculator.GetAllDayHeight(
            TimedCalendarLayoutCalculator.GetAllDayLaneCount(_currentRange.Dates, CurrentItems));
        TimedScrollContentGrid.Width = ActualWidth;
        TimedViewport.Width = timedSurfaceWidth;
        TimedViewport.Height = TimelineHeight;
        TimedAllDayHost.Width = timedSurfaceWidth;
        TimedAllDayItemsCanvas.Width = timedSurfaceWidth;
        TimedAllDayItemsCanvas.Height = TimedAllDayHeight;

        _timedLayout = TimedCalendarLayoutCalculator.Calculate(_currentRange, CurrentItems, timedSurfaceWidth, GetHourHeight());

        ReplaceCollection(
            TimedHeaderTextsCollection,
            _timedLayout.VisibleDates.Select(date =>
                new HeaderTextLayout(
                    GetTimedHeaderText(date),
                    TimedDayWidth)));

        var eventTemplate = (DataTemplate)Resources["CalendarEventTemplate"];

        ReplaceCollection(TimedAllDayItemsCollection, TimedCalendarLayoutCalculator.CalculateAllDayItems(_currentRange, CurrentItems, timedSurfaceWidth).Select(item =>
        {
            PrepareDisplayMetadata(item.Item, item.Date);
            item.Template = eventTemplate;
            return item;
        }));

        ReplaceCollection(TimedItemsCollection, _timedLayout.Items.Select(item =>
        {
            PrepareDisplayMetadata(item.Item, item.Date);
            item.Template = eventTemplate;
            return item;
        }));
        RenderHourLabels();
        RenderTimedAllDayItems();
        RenderTimedItems();

        TimedHeaderCanvas.Invalidate();
        TimedStructureCanvas.Invalidate();
        TimedAllDayCanvas.Invalidate();

        TimedAllDayHost.InvalidateArrange();
    }

    private void RefreshMonthView()
    {
        TimedRoot.Visibility = Visibility.Collapsed;
        MonthRoot.Visibility = Visibility.Visible;

        var availableMonthHeight = Math.Max(0d, ActualHeight - MonthHeadersItemsControl.ActualHeight);
        _monthLayout = MonthCalendarLayoutCalculator.Calculate(_currentRange, CurrentItems, ActualWidth, availableMonthHeight);

        MonthCellWidth = _monthLayout.CellWidth;
        MonthCellHeight = _monthLayout.CellHeight;

        MonthViewport.Width = ActualWidth;
        MonthViewport.Height = availableMonthHeight;

        ReplaceCollection(
            MonthHeaderTextsCollection,
            Enumerable.Range(0, MonthCalendarLayoutCalculator.ColumnCount)
                .Select(index =>
                {
                    var day = (DayOfWeek)(((int)CalendarSettings!.FirstDayOfWeek + index) % 7);
                    return new HeaderTextLayout(
                        CalendarSettings.CultureInfo.DateTimeFormat.AbbreviatedDayNames[(int)day],
                        MonthCellWidth);
                }));

        ReplaceCollection(
            MonthCellLabelsCollection,
            _monthLayout.Cells.Select(cell =>
                new MonthCellLabelLayout(
                    cell.Date.Day.ToString(CalendarSettings!.CultureInfo),
                    cell.Date.Month == VisibleRange!.AnchorDate.Month && cell.Date.Year == VisibleRange.AnchorDate.Year ? 1d : 0.55d,
                    new LayoutRect(cell.Bounds.X + 4, cell.Bounds.Y + 2, cell.Bounds.Width, cell.Bounds.Height))));

        var monthEventTemplate = (DataTemplate)Resources["MonthEventTemplate"];
        ReplaceCollection(MonthItemsCollection, _monthLayout.Items.Select(item =>
        {
            PrepareDisplayMetadata(item.Item, item.Date);
            item.Template = monthEventTemplate;
            return item;
        }));
        RenderMonthCellLabels();
        RenderMonthItems();

        MonthStructureCanvas.Invalidate();
    }

    private void PrepareDisplayMetadata(CalendarItemViewModel item, DateOnly date)
    {
        if (CalendarSettings is null || item is not ICalendarItemViewModel calendarItemViewModel)
        {
            return;
        }

        calendarItemViewModel.DisplayingPeriod = new TimeRange(
            date.ToDateTime(TimeOnly.MinValue),
            date.AddDays(1).ToDateTime(TimeOnly.MinValue));
        calendarItemViewModel.CalendarSettings = CalendarSettings;
    }

    private static VisibleDateRange CreateLayoutRange(VisibleDateRange visibleRange, CalendarSettings calendarSettings)
    {
        if (visibleRange.DisplayType != CalendarDisplayType.Month)
        {
            return visibleRange;
        }

        var start = AlignToWeekStart(visibleRange.StartDate, calendarSettings.FirstDayOfWeek);
        var end = AlignToWeekEnd(visibleRange.EndDate, calendarSettings.FirstDayOfWeek);
        var totalDays = end.DayNumber - start.DayNumber + 1;

        if (totalDays <= 35)
        {
            end = start.AddDays(34);
        }
        else if (totalDays < 42)
        {
            end = start.AddDays(41);
        }

        var dates = Enumerable.Range(0, end.DayNumber - start.DayNumber + 1)
                              .Select(offset => start.AddDays(offset))
                              .ToArray();

        return new VisibleDateRange(
            visibleRange.DisplayType,
            visibleRange.AnchorDate,
            start,
            end,
            visibleRange.PrimaryDate,
            dates.Length,
            dates.Contains(DateOnly.FromDateTime(DateTime.Today)),
            start.Year == end.Year && start.Month == end.Month,
            dates);
    }

    private static DateOnly AlignToWeekStart(DateOnly date, DayOfWeek firstDayOfWeek)
    {
        var offset = ((int)date.DayOfWeek - (int)firstDayOfWeek + 7) % 7;
        return date.AddDays(-offset);
    }

    private static DateOnly AlignToWeekEnd(DateOnly date, DayOfWeek firstDayOfWeek)
    {
        var lastDayOfWeek = (DayOfWeek)(((int)firstDayOfWeek + 6) % 7);
        var offset = ((int)lastDayOfWeek - (int)date.DayOfWeek + 7) % 7;
        return date.AddDays(offset);
    }

    private void TimedHeaderCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
    {
        using var borderPaint = CreateLinePaint();
        var canvas = e.Surface.Canvas;
        canvas.Clear(SKColors.Transparent);

        var timedSurfaceWidth = GetTimedSurfaceWidth();

        if (_timedLayout.VisibleDates.Count == 0 || timedSurfaceWidth <= 0)
        {
            return;
        }

        var scaleX = (float)(e.Info.Width / timedSurfaceWidth);
        var height = e.Info.Height;
        var dayWidth = (float)(_timedLayout.DayWidth * scaleX);

        for (var index = 1; index < _timedLayout.VisibleDates.Count; index++)
        {
            var x = dayWidth * index;
            canvas.DrawLine(x, 0, x, height, borderPaint);
        }

        canvas.DrawLine(0, height - 1, e.Info.Width, height - 1, borderPaint);
    }

    private void TimedAllDayCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
    {
        using var borderPaint = CreateLinePaint();
        using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
        var canvas = e.Surface.Canvas;
        canvas.Clear(SKColors.Transparent);

        var timedSurfaceWidth = GetTimedSurfaceWidth();

        if (_timedLayout.VisibleDates.Count == 0 || timedSurfaceWidth <= 0 || TimedAllDayHeight <= 0)
        {
            return;
        }

        var scaleX = (float)(e.Info.Width / timedSurfaceWidth);
        var height = e.Info.Height;
        var dayWidth = (float)(_timedLayout.DayWidth * scaleX);

        var hoveredTimedAllDayRect = GetHoveredTimedAllDayRect(dayWidth, height);
        if (hoveredTimedAllDayRect.HasValue && hoverFillPaint.Color.Alpha > 0)
        {
            canvas.DrawRect(hoveredTimedAllDayRect.Value, hoverFillPaint);
        }

        for (var index = 1; index < _timedLayout.VisibleDates.Count; index++)
        {
            var x = dayWidth * index;
            canvas.DrawLine(x, 0, x, height, borderPaint);
        }

        canvas.DrawLine(0, height - 1, e.Info.Width, height - 1, borderPaint);
    }

    private void TimedStructureCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
    {
        using var linePaint = CreateLinePaint();
        using var minorLinePaint = CreateMinorLinePaint();
        using var defaultFillPaint = CreateFillPaint(DefaultHourBackground ?? new SolidColorBrush(Colors.Transparent));
        using var workFillPaint = CreateFillPaint(WorkHourBackground ?? new SolidColorBrush(Colors.Transparent));
        using var selectedFillPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
        using var hoverFillPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));
        var canvas = e.Surface.Canvas;
        canvas.Clear(SKColors.Transparent);

        var timedSurfaceWidth = GetTimedSurfaceWidth();

        if (_timedLayout.VisibleDates.Count == 0 || timedSurfaceWidth <= 0)
        {
            return;
        }

        var hourHeight = GetHourHeight();
        var timelineHeight = TimedCalendarLayoutCalculator.GetTimelineHeight(hourHeight);
        var scaleX = (float)(e.Info.Width / timedSurfaceWidth);
        var scaleY = (float)(e.Info.Height / timelineHeight);
        var dayWidth = (float)(_timedLayout.DayWidth * scaleX);
        var isWorkingHoursEnabled = CalendarSettings?.IsWorkingHoursEnabled == true;
        var workDayStartHour = CalendarSettings?.WorkingHourStart.TotalHours ?? 9d;
        var workDayEndHour = CalendarSettings?.WorkingHourEnd.TotalHours ?? 17d;
        var intervalHeight = (float)(GetTimedGridIntervalHeight() * scaleY);
        var intervalCount = (int)(24d * 60d / TimedGridIntervalMinutes);

        for (var dayIndex = 0; dayIndex < _timedLayout.VisibleDates.Count; dayIndex++)
        {
            var x = dayIndex * dayWidth;
            var isWorkingDay = isWorkingHoursEnabled &&
                               CalendarSettings?.WorkingDays.Contains(_timedLayout.VisibleDates[dayIndex].DayOfWeek) == true;

            for (var intervalIndex = 0; intervalIndex < intervalCount; intervalIndex++)
            {
                var intervalStartHour = (intervalIndex * TimedGridIntervalMinutes) / 60d;
                var y = intervalIndex * intervalHeight;
                var fillPaint = isWorkingDay && intervalStartHour >= workDayStartHour && intervalStartHour < workDayEndHour
                    ? workFillPaint
                    : defaultFillPaint;
                canvas.DrawRect(x, y, dayWidth, intervalHeight, fillPaint);
            }
        }

        var hoveredTimedSlotRect = GetHoveredTimedSlotRect(dayWidth, intervalHeight, intervalCount);
        if (hoveredTimedSlotRect.HasValue && hoverFillPaint.Color.Alpha > 0)
        {
            canvas.DrawRect(hoveredTimedSlotRect.Value, hoverFillPaint);
        }

        var selectedTimedSlotRect = GetSelectedTimedSlotRect(dayWidth, intervalHeight, intervalCount);
        if (selectedTimedSlotRect.HasValue && selectedFillPaint.Color.Alpha > 0)
        {
            canvas.DrawRect(selectedTimedSlotRect.Value, selectedFillPaint);
        }

        for (var intervalIndex = 0; intervalIndex <= intervalCount; intervalIndex++)
        {
            var y = intervalIndex * intervalHeight;
            var paint = intervalIndex % 2 == 0 ? linePaint : minorLinePaint;
            canvas.DrawLine(0, y, e.Info.Width, y, paint);
        }

        for (var index = 0; index <= _timedLayout.VisibleDates.Count; index++)
        {
            var x = dayWidth * index;
            canvas.DrawLine(x, 0, x, e.Info.Height, linePaint);
        }
    }

    private void MonthStructureCanvasPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
    {
        using var linePaint = CreateLinePaint();
        using var todayPaint = new SKPaint
        {
            Style = SKPaintStyle.Fill,
            Color = new SKColor(0, 120, 215, 26),
            IsAntialias = true
        };
        using var selectedPaint = CreateFillPaint(SelectedSlotBackground ?? new SolidColorBrush(Colors.Transparent));
        using var hoverPaint = CreateFillPaint(HoverSlotBackground ?? new SolidColorBrush(Colors.Transparent));

        var canvas = e.Surface.Canvas;
        canvas.Clear(SKColors.Transparent);

        if (_monthLayout.CellWidth <= 0 || _monthLayout.CellHeight <= 0 || MonthViewport.ActualWidth <= 0 || MonthViewport.ActualHeight <= 0)
        {
            return;
        }

        var cellWidth = (float)(e.Info.Width / MonthCalendarLayoutCalculator.ColumnCount);
        var cellHeight = (float)(e.Info.Height / MonthCalendarLayoutCalculator.RowCount);
        var today = DateOnly.FromDateTime(DateTime.Now.Date);

        foreach (var cell in _monthLayout.Cells)
        {
            if (cell.Date != today)
            {
                continue;
            }

            canvas.DrawRect((float)cell.Bounds.X, (float)cell.Bounds.Y, (float)cell.Bounds.Width, (float)cell.Bounds.Height, todayPaint);
        }

        var hoveredMonthCellRect = GetHoveredMonthCellRect();
        if (hoveredMonthCellRect.HasValue && hoverPaint.Color.Alpha > 0)
        {
            canvas.DrawRect(hoveredMonthCellRect.Value, hoverPaint);
        }

        var selectedMonthCellRect = GetSelectedMonthCellRect();
        if (selectedMonthCellRect.HasValue && selectedPaint.Color.Alpha > 0)
        {
            canvas.DrawRect(selectedMonthCellRect.Value, selectedPaint);
        }

        for (var row = 0; row <= MonthCalendarLayoutCalculator.RowCount; row++)
        {
            var y = row * cellHeight;
            canvas.DrawLine(0, y, e.Info.Width, y, linePaint);
        }

        for (var column = 0; column <= MonthCalendarLayoutCalculator.ColumnCount; column++)
        {
            var x = column * cellWidth;
            canvas.DrawLine(x, 0, x, e.Info.Height, linePaint);
        }
    }

    private static void ReplaceCollection<T>(ObservableCollection<T> target, IEnumerable<T> items)
    {
        target.Clear();

        foreach (var item in items)
        {
            target.Add(item);
        }
    }

    private void RenderHourLabels()
    {
        HourLabelsCanvas.Children.Clear();
        HourLabelsCanvas.Height = TimelineHeight;

        var hourHeight = GetHourHeight();
        var labelWidth = Math.Max(0d, TimedHourColumnWidth - 10d);

        for (var hour = 0; hour <= 24; hour++)
        {
            var textBlock = new TextBlock
            {
                Width = labelWidth,
                Text = GetTimedHourLabelText(hour),
                TextAlignment = TextAlignment.Right,
                Opacity = 0.72
            };

            var y = hour == 24
                ? Math.Max(0d, TimelineHeight - 20d)
                : Math.Max(0d, (hour * hourHeight) - 10d);

            Canvas.SetLeft(textBlock, 0d);
            Canvas.SetTop(textBlock, y);
            HourLabelsCanvas.Children.Add(textBlock);
        }
    }

    private void RenderTimedItems()
    {
        TimedItemsCanvas.Children.Clear();

        foreach (var item in TimedItemsCollection)
        {
            var presenter = new ContentPresenter
            {
                Width = Math.Max(0d, item.Bounds.Width - TimedItemRightSpacing),
                Height = item.Bounds.Height,
                Content = item.Item,
                ContentTemplate = item.Template
            };

            Canvas.SetLeft(presenter, item.Bounds.X);
            Canvas.SetTop(presenter, item.Bounds.Y);
            TimedItemsCanvas.Children.Add(presenter);
        }
    }

    private void RenderTimedAllDayItems()
    {
        TimedAllDayItemsCanvas.Children.Clear();

        foreach (var item in TimedAllDayItemsCollection)
        {
            var presenter = new ContentPresenter
            {
                Width = item.Bounds.Width,
                Height = item.Bounds.Height,
                Content = item.Item,
                ContentTemplate = item.Template
            };

            Canvas.SetLeft(presenter, item.Bounds.X);
            Canvas.SetTop(presenter, item.Bounds.Y);
            TimedAllDayItemsCanvas.Children.Add(presenter);
        }
    }

    private void RenderMonthCellLabels()
    {
        MonthCellLabelsCanvas.Children.Clear();

        foreach (var label in MonthCellLabelsCollection)
        {
            var textBlock = new TextBlock
            {
                Width = label.Bounds.Width,
                Height = label.Bounds.Height,
                Opacity = label.LabelOpacity,
                Text = label.DayText
            };

            Canvas.SetLeft(textBlock, label.Bounds.X);
            Canvas.SetTop(textBlock, label.Bounds.Y);
            MonthCellLabelsCanvas.Children.Add(textBlock);
        }
    }

    private void RenderMonthItems()
    {
        MonthItemsCanvas.Children.Clear();

        foreach (var item in MonthItemsCollection)
        {
            var presenter = new ContentPresenter
            {
                Width = item.Bounds.Width,
                Height = item.Bounds.Height,
                Content = item.Item,
                ContentTemplate = item.Template
            };

            Canvas.SetLeft(presenter, item.Bounds.X);
            Canvas.SetTop(presenter, item.Bounds.Y);
            MonthItemsCanvas.Children.Add(presenter);
        }
    }

    private void TimedInteractionLayerTapped(object sender, TappedRoutedEventArgs e)
    {
        if (_timedLayout.VisibleDates.Count == 0 || _timedLayout.DayWidth <= 0)
        {
            return;
        }

        var position = e.GetPosition(TimedViewport);
        var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
        var intervalHeight = GetTimedSelectionIntervalHeight();
        var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1));
        var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes);
        var clickedDate = _timedLayout.VisibleDates[dayIndex].ToDateTime(TimeOnly.MinValue).Add(slotStart);
        var anchorPoint = TimedViewport.TransformToVisual(Root).TransformPoint(position);

        EmptySlotTapped?.Invoke(
            this,
            new CalendarEmptySlotTappedEventArgs(
                clickedDate,
                anchorPoint,
                new Size(_timedLayout.DayWidth, intervalHeight)));
    }

    private void MonthInteractionLayerTapped(object sender, TappedRoutedEventArgs e)
    {
        if (_monthLayout.Cells.Count == 0 || _monthLayout.CellWidth <= 0 || _monthLayout.CellHeight <= 0)
        {
            return;
        }

        var position = e.GetPosition(MonthViewport);
        var column = Math.Clamp((int)(position.X / _monthLayout.CellWidth), 0, MonthCalendarLayoutCalculator.ColumnCount - 1);
        var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1);
        var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1);
        var cell = _monthLayout.Cells[cellIndex];
        var anchorPoint = MonthViewport.TransformToVisual(Root).TransformPoint(position);

        EmptySlotTapped?.Invoke(
            this,
            new CalendarEmptySlotTappedEventArgs(
                cell.Date.ToDateTime(TimeOnly.MinValue),
                anchorPoint,
                new Size(cell.Bounds.Width, cell.Bounds.Height)));
    }

    private void TimedViewportPointerMoved(object sender, PointerRoutedEventArgs e)
        => UpdateHoverTargetForActiveDrag(() => ResolveTimedDropTarget(e.GetCurrentPoint(TimedViewport).Position, _activeDragPackage?.CalendarItemViewModel));

    private void TimedAllDayHostPointerMoved(object sender, PointerRoutedEventArgs e)
        => UpdateHoverTargetForActiveDrag(() => ResolveTimedAllDayDropTarget(e.GetCurrentPoint(TimedAllDayHost).Position, _activeDragPackage?.CalendarItemViewModel));

    private void MonthViewportPointerMoved(object sender, PointerRoutedEventArgs e)
        => UpdateHoverTargetForActiveDrag(() => ResolveMonthDropTarget(e.GetCurrentPoint(MonthViewport).Position, _activeDragPackage?.CalendarItemViewModel));

    private void CalendarDropTargetPointerExited(object sender, PointerRoutedEventArgs e)
    {
        if (_activeDragPackage == null)
        {
            SetHoverTarget(null);
        }
    }

    private void UpdateHoverTargetForActiveDrag(Func<CalendarDropTargetInfo?> resolveHoverTarget)
    {
        if (_activeDragPackage == null)
        {
            SetHoverTarget(null);
            return;
        }

        SetHoverTarget(resolveHoverTarget());
    }

    private void TimedViewportDragOver(object sender, DragEventArgs e)
    {
        if (!TryGetDragPackage(e, out var dragPackage))
        {
            return;
        }

        var hoverTarget = ResolveTimedDropTarget(e.GetPosition(TimedViewport), dragPackage.CalendarItemViewModel);

        UpdateDragOverState(e, dragPackage, hoverTarget);
    }

    private void TimedAllDayHostDragOver(object sender, DragEventArgs e)
    {
        if (!TryGetDragPackage(e, out var dragPackage))
        {
            return;
        }

        var hoverTarget = ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), dragPackage.CalendarItemViewModel);

        UpdateDragOverState(e, dragPackage, hoverTarget);
    }

    private void MonthViewportDragOver(object sender, DragEventArgs e)
    {
        if (!TryGetDragPackage(e, out var dragPackage))
        {
            return;
        }

        var hoverTarget = ResolveMonthDropTarget(e.GetPosition(MonthViewport), dragPackage.CalendarItemViewModel);

        UpdateDragOverState(e, dragPackage, hoverTarget);
    }

    private void TimedViewportDrop(object sender, DragEventArgs e)
        => HandleDrop(e, ResolveTimedDropTarget(e.GetPosition(TimedViewport), _activeDragPackage?.CalendarItemViewModel));

    private void TimedAllDayHostDrop(object sender, DragEventArgs e)
        => HandleDrop(e, ResolveTimedAllDayDropTarget(e.GetPosition(TimedAllDayHost), _activeDragPackage?.CalendarItemViewModel));

    private void MonthViewportDrop(object sender, DragEventArgs e)
        => HandleDrop(e, ResolveMonthDropTarget(e.GetPosition(MonthViewport), _activeDragPackage?.CalendarItemViewModel));

    private void CalendarDropTargetDragLeave(object sender, DragEventArgs e)
    {
        _activeDragPackage = null;
        SetHoverTarget(null);
    }

    private bool TryGetDragPackage(DragEventArgs e, out CalendarDragPackage dragPackage)
    {
        dragPackage = null;

        if (!e.DataView.Properties.ContainsKey(nameof(CalendarDragPackage)))
        {
            e.AcceptedOperation = DataPackageOperation.None;
            _activeDragPackage = null;
            SetHoverTarget(null);
            return false;
        }

        dragPackage = e.DataView.Properties[nameof(CalendarDragPackage)] as CalendarDragPackage;

        if (dragPackage?.CalendarItemViewModel?.CanDragDrop != true)
        {
            e.AcceptedOperation = DataPackageOperation.None;
            _activeDragPackage = null;
            SetHoverTarget(null);
            return false;
        }

        return true;
    }

    private void UpdateDragOverState(DragEventArgs e, CalendarDragPackage dragPackage, CalendarDropTargetInfo? hoverTarget)
    {
        _activeDragPackage = dragPackage;
        SetHoverTarget(hoverTarget);

        if (hoverTarget.HasValue)
        {
            e.AcceptedOperation = DataPackageOperation.Move;
        }
        else
        {
            e.AcceptedOperation = DataPackageOperation.None;
        }
    }

    private void HandleDrop(DragEventArgs e, CalendarDropTargetInfo? hoverTarget)
    {
        try
        {
            if (_activeDragPackage?.CalendarItemViewModel?.CanDragDrop != true || !hoverTarget.HasValue)
            {
                e.AcceptedOperation = DataPackageOperation.None;
                return;
            }

            e.AcceptedOperation = DataPackageOperation.Move;
            CalendarItemDropped?.Invoke(
                this,
                new CalendarItemDroppedEventArgs(
                    _activeDragPackage.CalendarItemViewModel,
                    hoverTarget.Value.TargetStart,
                    hoverTarget.Value.Kind));
        }
        finally
        {
            _activeDragPackage = null;
            SetHoverTarget(null);
        }
    }

    private CalendarDropTargetInfo? ResolveTimedDropTarget(Point position, CalendarItemViewModel? draggedItem)
    {
        if (draggedItem?.IsAllDayEvent == true ||
            _timedLayout.VisibleDates.Count == 0 ||
            _timedLayout.DayWidth <= 0)
        {
            return null;
        }

        var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
        var intervalHeight = GetTimedSelectionIntervalHeight();
        var slotIndex = Math.Clamp((int)(position.Y / intervalHeight), 0, (int)((24d * 60d / TimedSelectionIntervalMinutes) - 1));
        var date = _timedLayout.VisibleDates[dayIndex];
        var slotStart = TimeSpan.FromMinutes(slotIndex * TimedSelectionIntervalMinutes);

        return new CalendarDropTargetInfo(
            CalendarDropTargetKind.TimedSlot,
            date,
            dayIndex,
            slotIndex,
            date.ToDateTime(TimeOnly.MinValue).Add(slotStart));
    }

    private CalendarDropTargetInfo? ResolveTimedAllDayDropTarget(Point position, CalendarItemViewModel? draggedItem)
    {
        if (draggedItem is { IsAllDayEvent: false } ||
            _timedLayout.VisibleDates.Count == 0 ||
            _timedLayout.DayWidth <= 0 ||
            TimedAllDayHeight <= 0)
        {
            return null;
        }

        var dayIndex = Math.Clamp((int)(position.X / _timedLayout.DayWidth), 0, _timedLayout.VisibleDates.Count - 1);
        var date = _timedLayout.VisibleDates[dayIndex];

        return new CalendarDropTargetInfo(
            CalendarDropTargetKind.TimedAllDay,
            date,
            dayIndex,
            -1,
            date.ToDateTime(TimeOnly.MinValue));
    }

    private CalendarDropTargetInfo? ResolveMonthDropTarget(Point position, CalendarItemViewModel? draggedItem)
    {
        if (_monthLayout.Cells.Count == 0 || _monthLayout.CellWidth <= 0 || _monthLayout.CellHeight <= 0)
        {
            return null;
        }

        var column = Math.Clamp((int)(position.X / _monthLayout.CellWidth), 0, MonthCalendarLayoutCalculator.ColumnCount - 1);
        var row = Math.Clamp((int)(position.Y / _monthLayout.CellHeight), 0, MonthCalendarLayoutCalculator.RowCount - 1);
        var cellIndex = Math.Clamp((row * MonthCalendarLayoutCalculator.ColumnCount) + column, 0, _monthLayout.Cells.Count - 1);
        var cell = _monthLayout.Cells[cellIndex];
        var targetStart = cell.Date.ToDateTime(TimeOnly.MinValue);

        if (draggedItem is { IsAllDayEvent: false })
        {
            targetStart = targetStart.Add(draggedItem.StartDate.TimeOfDay);
        }

        return new CalendarDropTargetInfo(
            CalendarDropTargetKind.MonthCell,
            cell.Date,
            -1,
            -1,
            targetStart);
    }

    private void SetHoverTarget(CalendarDropTargetInfo? hoverTarget)
    {
        if (_hoverTarget == hoverTarget)
        {
            return;
        }

        _hoverTarget = hoverTarget;
        InvalidateStructureCanvases();
    }

    private SKRect? GetSelectedTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
    {
        if (SelectedDateTime is not DateTime selectedDateTime || _timedLayout.VisibleDates.Count == 0)
        {
            return null;
        }

        var dayIndex = FindVisibleDateIndex(DateOnly.FromDateTime(selectedDateTime));
        if (dayIndex < 0)
        {
            return null;
        }

        var slotIndex = (int)Math.Floor(selectedDateTime.TimeOfDay.TotalMinutes / TimedSelectionIntervalMinutes);
        slotIndex = Math.Clamp(slotIndex, 0, intervalCount - 1);

        var x = dayIndex * dayWidth;
        var y = slotIndex * intervalHeight;
        return new SKRect(x, y, x + dayWidth, y + intervalHeight);
    }

    private SKRect? GetHoveredTimedSlotRect(float dayWidth, float intervalHeight, int intervalCount)
    {
        if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedSlot } hoverTarget)
        {
            return null;
        }

        var slotIndex = Math.Clamp(hoverTarget.SlotIndex, 0, intervalCount - 1);
        var x = hoverTarget.DayIndex * dayWidth;
        var y = slotIndex * intervalHeight;
        return new SKRect(x, y, x + dayWidth, y + intervalHeight);
    }

    private SKRect? GetHoveredTimedAllDayRect(float dayWidth, float height)
    {
        if (_hoverTarget is not { Kind: CalendarDropTargetKind.TimedAllDay } hoverTarget)
        {
            return null;
        }

        var x = hoverTarget.DayIndex * dayWidth;
        return new SKRect(x, 0, x + dayWidth, height);
    }

    private SKRect? GetSelectedMonthCellRect()
    {
        if (SelectedDateTime is not DateTime selectedDateTime)
        {
            return null;
        }

        var selectedDate = DateOnly.FromDateTime(selectedDateTime);
        foreach (var cell in _monthLayout.Cells)
        {
            if (cell.Date != selectedDate)
            {
                continue;
            }

            return new SKRect(
                (float)cell.Bounds.X,
                (float)cell.Bounds.Y,
                (float)(cell.Bounds.X + cell.Bounds.Width),
                (float)(cell.Bounds.Y + cell.Bounds.Height));
        }

        return null;
    }

    private SKRect? GetHoveredMonthCellRect()
    {
        if (_hoverTarget is not { Kind: CalendarDropTargetKind.MonthCell } hoverTarget)
        {
            return null;
        }

        foreach (var cell in _monthLayout.Cells)
        {
            if (cell.Date != hoverTarget.Date)
            {
                continue;
            }

            return new SKRect(
                (float)cell.Bounds.X,
                (float)cell.Bounds.Y,
                (float)(cell.Bounds.X + cell.Bounds.Width),
                (float)(cell.Bounds.Y + cell.Bounds.Height));
        }

        return null;
    }

    private int FindVisibleDateIndex(DateOnly date)
    {
        for (var index = 0; index < _timedLayout.VisibleDates.Count; index++)
        {
            if (_timedLayout.VisibleDates[index] == date)
            {
                return index;
            }
        }

        return -1;
    }

    private double GetTimedSurfaceWidth() => Math.Max(0d, ActualWidth - TimedHourColumnWidth);

    private string GetTimedHeaderText(DateOnly date)
    {
        if (!string.IsNullOrWhiteSpace(TimedHeaderDateFormat) && CalendarSettings is not null)
        {
            try
            {
                return date.ToDateTime(TimeOnly.MinValue).ToString(TimedHeaderDateFormat, CalendarSettings.CultureInfo);
            }
            catch (FormatException)
            {
            }
        }

        return CalendarSettings?.GetTimedDayHeaderText(date) ?? date.ToDateTime(TimeOnly.MinValue).ToString("ddd dd");
    }

    private string GetTimedHourLabelText(int hour)
        => CalendarSettings?.GetTimedHourLabelText(hour) ?? $"{hour:00}:00";

    private CalendarTransitionInfo GetTransitionInfo()
    {
        if (!_hasPresentedState || VisibleRange is null || CalendarSettings is null)
        {
            return new CalendarTransitionInfo(CalendarTransitionKind.None, 0);
        }

        if (_lastDisplayMode != VisibleRange.DisplayType)
        {
            return new CalendarTransitionInfo(CalendarTransitionKind.ModeChange, 0);
        }

        if (_lastDisplayDate != VisibleRange.AnchorDate)
        {
            return new CalendarTransitionInfo(
                CalendarTransitionKind.Navigation,
                VisibleRange.AnchorDate.CompareTo(_lastDisplayDate));
        }

        if (_lastFirstDayOfWeek != CalendarSettings.FirstDayOfWeek)
        {
            return new CalendarTransitionInfo(CalendarTransitionKind.Refresh, 0);
        }

        return new CalendarTransitionInfo(CalendarTransitionKind.None, 0);
    }

    private void RunTransition(CalendarTransitionInfo transition)
    {
        if (transition.Kind == CalendarTransitionKind.None || VisibleRange is null)
        {
            return;
        }

        if (VisibleRange.DisplayType != CalendarDisplayType.Month)
        {
            RunTimedTransition(transition);
            return;
        }

        var target = (UIElement)MonthRoot;
        var visual = ElementCompositionPreview.GetElementVisual(target);
        var compositor = visual.Compositor;

        visual.CenterPoint = new Vector3((float)(target.RenderSize.Width * 0.5), (float)(target.RenderSize.Height * 0.5), 0f);
        visual.StopAnimation(nameof(visual.Offset));
        visual.StopAnimation(nameof(visual.Opacity));
        visual.StopAnimation(nameof(visual.Scale));

        switch (transition.Kind)
        {
            case CalendarTransitionKind.Navigation:
                StartNavigationTransition(compositor, visual, transition.Direction, target.RenderSize.Width);
                break;
            case CalendarTransitionKind.ModeChange:
                StartModeTransition(compositor, visual);
                break;
            case CalendarTransitionKind.Refresh:
                StartRefreshTransition(compositor, visual);
                break;
        }
    }

    private void RunTimedTransition(CalendarTransitionInfo transition)
    {
        var contentVisual = ElementCompositionPreview.GetElementVisual(TimedScrollViewer);
        var compositor = contentVisual.Compositor;

        PrepareAnimatedVisual(contentVisual, TimedScrollViewer);

        switch (transition.Kind)
        {
            case CalendarTransitionKind.Navigation:
                StartTimedNavigationTransition(compositor, transition.Direction);
                break;
            case CalendarTransitionKind.ModeChange:
                StartTimedModeTransition(compositor);
                break;
            case CalendarTransitionKind.Refresh:
                StartTimedRefreshTransition(compositor);
                break;
        }
    }

    private void ResetTimedVisualState()
    {
        ResetAnimatedElement(TimedScrollViewer);

        // TODO: This is buggy.
        // ResetAnimatedElement(TimedAllDayHost);
    }

    private static void StartNavigationTransition(Compositor compositor, Visual visual, int direction, double width)
    {
        var travel = (float)Math.Max(48d, Math.Min(160d, width * 0.08d));
        var startX = direction >= 0 ? travel : -travel;

        var offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
        offsetAnimation.InsertKeyFrame(0f, new Vector3(startX, 0f, 0f));
        offsetAnimation.InsertKeyFrame(1f, Vector3.Zero);
        offsetAnimation.Duration = TimeSpan.FromMilliseconds(220);

        var opacityAnimation = compositor.CreateScalarKeyFrameAnimation();
        opacityAnimation.InsertKeyFrame(0f, 0.72f);
        opacityAnimation.InsertKeyFrame(1f, 1f);
        opacityAnimation.Duration = offsetAnimation.Duration;

        visual.StartAnimation(nameof(visual.Offset), offsetAnimation);
        visual.StartAnimation(nameof(visual.Opacity), opacityAnimation);
    }

    private void StartTimedNavigationTransition(Compositor compositor, int direction)
    {
        var width = Math.Max(TimedRoot.RenderSize.Width, ActualWidth);
        var travel = (float)Math.Max(56d, Math.Min(184d, width * 0.09d));
        var signedTravel = direction >= 0 ? travel : -travel;
        var clipInset = (float)Math.Max(18d, Math.Min(64d, width * 0.05d));

        StartTimedElementTransition(compositor, TimedScrollViewer, signedTravel, 0f, 0.68f, TimeSpan.FromMilliseconds(240), direction >= 0 ? 0f : clipInset, direction >= 0 ? clipInset : 0f, animateScale: false);
        if (HasTimedAllDayItems)
        {
            // TODO: This is buggy.
            // StartTimedElementTransition(compositor, TimedAllDayHost, signedTravel, 0f, 0.68f, TimeSpan.FromMilliseconds(240), direction >= 0 ? 0f : clipInset, direction >= 0 ? clipInset : 0f, animateScale: false);
        }
    }

    private static void StartModeTransition(Compositor compositor, Visual visual)
    {
        var offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
        offsetAnimation.InsertKeyFrame(0f, new Vector3(0f, 18f, 0f));
        offsetAnimation.InsertKeyFrame(1f, Vector3.Zero);
        offsetAnimation.Duration = TimeSpan.FromMilliseconds(260);

        var opacityAnimation = compositor.CreateScalarKeyFrameAnimation();
        opacityAnimation.InsertKeyFrame(0f, 0f);
        opacityAnimation.InsertKeyFrame(1f, 1f);
        opacityAnimation.Duration = offsetAnimation.Duration;

        var scaleAnimation = compositor.CreateVector3KeyFrameAnimation();
        scaleAnimation.InsertKeyFrame(0f, new Vector3(0.985f, 0.985f, 1f));
        scaleAnimation.InsertKeyFrame(1f, new Vector3(1f, 1f, 1f));
        scaleAnimation.Duration = offsetAnimation.Duration;

        visual.StartAnimation(nameof(visual.Offset), offsetAnimation);
        visual.StartAnimation(nameof(visual.Opacity), opacityAnimation);
        visual.StartAnimation(nameof(visual.Scale), scaleAnimation);
    }

    private void StartTimedModeTransition(Compositor compositor)
    {
        StartTimedElementTransition(compositor, TimedScrollViewer, 0f, 18f, 0f, TimeSpan.FromMilliseconds(240), 0f, 0f, animateScale: false);
        if (HasTimedAllDayItems)
        {
            // TODO: This is buggy.
            // StartTimedElementTransition(compositor, TimedAllDayHost, 0f, 18f, 0f, TimeSpan.FromMilliseconds(240), 0f, 0f, animateScale: false);
        }
    }

    private static void StartRefreshTransition(Compositor compositor, Visual visual)
    {
        var opacityAnimation = compositor.CreateScalarKeyFrameAnimation();
        opacityAnimation.InsertKeyFrame(0f, 0.82f);
        opacityAnimation.InsertKeyFrame(1f, 1f);
        opacityAnimation.Duration = TimeSpan.FromMilliseconds(160);

        visual.StartAnimation(nameof(visual.Opacity), opacityAnimation);
    }

    private void StartTimedRefreshTransition(Compositor compositor)
    {
        StartOpacityTransition(compositor, ElementCompositionPreview.GetElementVisual(TimedScrollViewer), 0.8f, TimeSpan.FromMilliseconds(160));
        if (HasTimedAllDayItems)
        {
            // TODO: This is buggy.
            // StartOpacityTransition(compositor, ElementCompositionPreview.GetElementVisual(TimedAllDayHost), 0.8f, TimeSpan.FromMilliseconds(160));
        }
    }

    private static void PrepareAnimatedVisual(Visual visual, UIElement target)
    {
        visual.CenterPoint = new Vector3((float)(target.RenderSize.Width * 0.5), (float)(target.RenderSize.Height * 0.5), 0f);
        visual.StopAnimation(nameof(visual.Offset));
        visual.StopAnimation(nameof(visual.Opacity));
        visual.StopAnimation(nameof(visual.Scale));
    }

    private static void ResetAnimatedElement(UIElement target)
    {
        var visual = ElementCompositionPreview.GetElementVisual(target);
        PrepareAnimatedVisual(visual, target);

        visual.Offset = Vector3.Zero;
        visual.Opacity = 1f;
        visual.Scale = new Vector3(1f, 1f, 1f);

        if (visual.Clip is InsetClip clip)
        {
            clip.StopAnimation(nameof(clip.LeftInset));
            clip.StopAnimation(nameof(clip.RightInset));
            clip.LeftInset = 0f;
            clip.RightInset = 0f;
        }
    }

    private static void StartTimedElementTransition(Compositor compositor, UIElement target, float offsetX, float offsetY, float startingOpacity, TimeSpan duration, float leftInset, float rightInset, bool animateScale = true)
    {
        var visual = ElementCompositionPreview.GetElementVisual(target);
        PrepareAnimatedVisual(visual, target);

        var easing = compositor.CreateCubicBezierEasingFunction(new Vector2(0.16f, 1f), new Vector2(0.3f, 1f));
        var fadeEasing = compositor.CreateCubicBezierEasingFunction(new Vector2(0.2f, 0f), new Vector2(0f, 1f));
        var clip = visual.Clip as InsetClip ?? compositor.CreateInsetClip();

        clip.LeftInset = leftInset;
        clip.RightInset = rightInset;
        visual.Clip = clip;

        var offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
        offsetAnimation.InsertKeyFrame(0f, new Vector3(offsetX, offsetY, 0f));
        offsetAnimation.InsertKeyFrame(1f, Vector3.Zero, easing);
        offsetAnimation.Duration = duration;

        var opacityAnimation = compositor.CreateScalarKeyFrameAnimation();
        opacityAnimation.InsertKeyFrame(0f, startingOpacity);
        opacityAnimation.InsertKeyFrame(1f, 1f, fadeEasing);
        opacityAnimation.Duration = duration;

        var leftInsetAnimation = compositor.CreateScalarKeyFrameAnimation();
        leftInsetAnimation.InsertKeyFrame(1f, 0f, easing);
        leftInsetAnimation.Duration = duration;

        var rightInsetAnimation = compositor.CreateScalarKeyFrameAnimation();
        rightInsetAnimation.InsertKeyFrame(1f, 0f, easing);
        rightInsetAnimation.Duration = duration;

        visual.StartAnimation(nameof(visual.Offset), offsetAnimation);
        visual.StartAnimation(nameof(visual.Opacity), opacityAnimation);

        if (animateScale)
        {
            var scaleAnimation = compositor.CreateVector3KeyFrameAnimation();
            scaleAnimation.InsertKeyFrame(0f, new Vector3(0.996f, 0.996f, 1f));
            scaleAnimation.InsertKeyFrame(1f, new Vector3(1f, 1f, 1f), easing);
            scaleAnimation.Duration = duration;
            visual.StartAnimation(nameof(visual.Scale), scaleAnimation);
        }
        else
        {
            visual.Scale = new Vector3(1f, 1f, 1f);
        }

        clip.StartAnimation(nameof(clip.LeftInset), leftInsetAnimation);
        clip.StartAnimation(nameof(clip.RightInset), rightInsetAnimation);
    }

    private static void StartOpacityTransition(Compositor compositor, Visual visual, float startingOpacity, TimeSpan duration)
    {
        visual.StopAnimation(nameof(visual.Opacity));

        var easing = compositor.CreateCubicBezierEasingFunction(new Vector2(0.2f, 0f), new Vector2(0f, 1f));
        var opacityAnimation = compositor.CreateScalarKeyFrameAnimation();
        opacityAnimation.InsertKeyFrame(0f, startingOpacity);
        opacityAnimation.InsertKeyFrame(1f, 1f, easing);
        opacityAnimation.Duration = duration;

        visual.StartAnimation(nameof(visual.Opacity), opacityAnimation);
    }

    private static SKPaint CreateLinePaint()
    {
        var strokeColor = GetStrokeColor();

        return new SKPaint
        {
            Color = new SKColor(strokeColor.R, strokeColor.G, strokeColor.B, (byte)Math.Max(40, strokeColor.A / 2)),
            IsAntialias = false,
            StrokeWidth = 1
        };
    }

    private static SKPaint CreateMinorLinePaint()
    {
        var strokeColor = GetStrokeColor();

        return new SKPaint
        {
            Color = new SKColor(strokeColor.R, strokeColor.G, strokeColor.B, (byte)Math.Max(20, strokeColor.A / 4)),
            IsAntialias = false,
            StrokeWidth = 1
        };
    }

    private static SKPaint CreateFillPaint(Brush brush)
    {
        return new SKPaint
        {
            Color = ToSkColor(brush),
            Style = SKPaintStyle.Fill,
            IsAntialias = false
        };
    }

    private static SKColor ToSkColor(Brush brush)
    {
        return brush is SolidColorBrush solidColorBrush
            ? new SKColor(solidColorBrush.Color.R, solidColorBrush.Color.G, solidColorBrush.Color.B, solidColorBrush.Color.A)
            : SKColors.Transparent;
    }

    private static double GetTimedGridIntervalHeight(double hourHeight) => hourHeight * (TimedGridIntervalMinutes / 60d);

    private double GetTimedGridIntervalHeight() => GetTimedGridIntervalHeight(GetHourHeight());

    private double GetTimedSelectionIntervalHeight() => GetHourHeight() * (TimedSelectionIntervalMinutes / 60d);

    private double GetHourHeight() => CalendarSettings?.HourHeight ?? 60d;

    private static Color GetStrokeColor()
    {
        if (Application.Current.Resources.TryGetValue("CardStrokeColorDefaultBrush", out var resource) &&
            resource is SolidColorBrush solidBrush)
        {
            return solidBrush.Color;
        }

        return Color.FromArgb(96, 210, 210, 210);
    }

    private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private readonly record struct CalendarTransitionInfo(CalendarTransitionKind Kind, int Direction);

    private readonly record struct CalendarDropTargetInfo(
        CalendarDropTargetKind Kind,
        DateOnly Date,
        int DayIndex,
        int SlotIndex,
        DateTime TargetStart);

    private enum CalendarTransitionKind
    {
        None,
        Navigation,
        ModeChange,
        Refresh
    }
}
