using System.Diagnostics.CodeAnalysis;
using Connected.Annotations;
using Connected.Extensions;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;

namespace Connected.Components;

public partial class Carousel<TData> : BindableItemsControlBase<CarouselItem, TData>, IAsyncDisposable
{
	protected string Classname =>
				new CssBuilder("carousel")
					 .AddClass($"carousel-{(BulletsColor ?? _currentColor).ToDescription()}")
							 .AddClass(AdditionalClassList)
							 .Build();

	protected string NavigationButtonsClassName =>
				new CssBuilder()
					.AddClass($"align-self-{ConvertPosition(ArrowsPosition).ToDescription()}", !(NavigationButtonsClass ?? "").Contains("align-self-"))
					.AddClass("carousel-elements-rtl", RightToLeft)
					.AddClass(NavigationButtonsClass)
					.Build();

	protected string BulletsButtonsClassName =>
				new CssBuilder()
					.AddClass(BulletsClass)
					.Build();

	private Timer _timer;
	private bool _autoCycle = true;
	private ThemeColor _currentColor = ThemeColor.Inherit;
	private TimeSpan _cycleTimeout = TimeSpan.FromSeconds(5);
	private void TimerElapsed(object stateInfo) => InvokeAsync(async () => await TimerTickAsync());

	private static Position ConvertPosition(Position position)
	{
		return position switch
		{
			Position.Top => Position.Start,
			Position.Start => Position.Start,
			Position.Bottom => Position.End,
			Position.End => Position.End,
			_ => position
		};
	}

	[CascadingParameter(Name = "RightToLeft")] public bool RightToLeft { get; set; }


	/// <summary>
	/// Gets or Sets if 'Next' and 'Previous' arrows must be visible
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Behavior)]
	public bool ShowArrows { get; set; } = true;

	/// <summary>
	/// Sets the position of the arrows. By default, the position is the Center position
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public Position ArrowsPosition { get; set; } = Position.Center;

	/// <summary>
	/// Gets or Sets if bar with Bullets must be visible
	/// </summary>
	[Category(CategoryTypes.Carousel.Behavior)]
	[Parameter] public bool ShowBullets { get; set; } = true;

	/// <summary>
	/// Sets the position of the bullets. By default, the position is the Bottom position
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Parameter] public Position BulletsPosition { get; set; } = Position.Bottom;

	/// <summary>
	/// Gets or Sets the Bullets color.
	/// If not set, the color is determined based on the <see cref="MudCarouselItem.Color"/> property of the active child.
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Parameter] public ThemeColor? BulletsColor { get; set; }


	/// <summary>
	/// Gets or Sets if bottom bar with Delimiters must be visible.
	/// Deprecated, use ShowBullets instead.
	/// </summary>
	[Category(CategoryTypes.Carousel.Behavior)]
	[Obsolete($"Use {nameof(ShowBullets)} instead", false)]
	[ExcludeFromCodeCoverage]
	[Parameter] public bool ShowDelimiters { get => ShowBullets; set => ShowBullets = value; }

	/// <summary>
	/// Gets or Sets the Delimiters color.
	/// If not set, the color is determined based on the <see cref="MudCarouselItem.Color"/> property of the active child.
	/// Deprecated, use BulletsColor instead.
	/// </summary>
	[Obsolete($"Use {nameof(BulletsColor)} instead", false)]
	[Category(CategoryTypes.Carousel.Appearance)]
	[ExcludeFromCodeCoverage]
	[Parameter] public ThemeColor? DelimitersColor { get => BulletsColor; set => BulletsColor = value; }

	/// <summary>
	/// Gets or Sets automatic cycle on item collection.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Behavior)]
	public bool AutoCycle
	{
		get => _autoCycle;
		set
		{
			_autoCycle = value;

			if (_autoCycle)
				InvokeAsync(async () => await ResetTimerAsync());

			else
				InvokeAsync(async () => await StopTimerAsync());
		}
	}


	/// <summary>
	/// Gets or Sets the Auto Cycle time
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Behavior)]
	public TimeSpan AutoCycleTime
	{
		get => _cycleTimeout;
		set
		{
			_cycleTimeout = value;

			if (_autoCycle == true)
				InvokeAsync(async () => await ResetTimerAsync());

			else
				InvokeAsync(async () => await StopTimerAsync());
		}
	}


	/// <summary>
	/// Gets or Sets custom class(es) for 'Next' and 'Previous' arrows
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public string NavigationButtonsClass { get; set; }

	/// <summary>
	/// Gets or Sets custom class(es) for Bullets buttons
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Parameter] public string BulletsClass { get; set; }

	/// <summary>
	/// Gets or Sets custom class(es) for Delimiters buttons.
	/// Deprecated, use BulletsClass instead.
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Obsolete($"Use {nameof(BulletsClass)} instead", false)]
	[ExcludeFromCodeCoverage]
	[Parameter] public string DelimitersClass { get => BulletsClass; set => BulletsClass = value; }

	/// <summary>
	/// Custom previous navigation icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public string PreviousIcon { get; set; } = Icons.Material.Filled.NavigateBefore;

	/// <summary>
	/// Custom selected bullet icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public string CheckedIcon { get; set; } = Icons.Material.Filled.RadioButtonChecked;

	/// <summary>
	/// Custom unselected bullet icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public string UncheckedIcon { get; set; } = Icons.Material.Filled.RadioButtonUnchecked;

	/// <summary>
	/// Custom next navigation icon.
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public string NextIcon { get; set; } = Icons.Material.Filled.NavigateNext;

	/// <summary>
	/// Gets or Sets the Template for the Left Arrow
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public RenderFragment NextButtonTemplate { get; set; }


	/// <summary>
	/// Gets or Sets the Template for the Right Arrow
	/// </summary>
	[Parameter]
	[Category(CategoryTypes.Carousel.Appearance)]
	public RenderFragment PreviousButtonTemplate { get; set; }


	/// <summary>
	/// Gets or Sets the Template for Bullets
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Parameter] public RenderFragment<bool> BulletTemplate { get; set; }

	/// <summary>
	/// Gets or Sets if swipe gestures are allowed for touch devices.
	/// </summary>
	[Category(CategoryTypes.Carousel.Behavior)]
	[Parameter]
	public bool EnableSwipeGesture { get; set; } = true;

	/// <summary>
	/// Gets or Sets the Template for Delimiters.
	/// Deprecated, use BulletsTemplate instead.
	/// </summary>
	[Category(CategoryTypes.Carousel.Appearance)]
	[Obsolete($"Use {nameof(BulletTemplate)} instead", false)]
	[ExcludeFromCodeCoverage]
	[Parameter] public RenderFragment<bool> DelimiterTemplate { get => BulletTemplate; set => BulletTemplate = value; }


	/// <summary>
	/// Called when selected Index changed on base class
	/// </summary>
	protected override void SelectionChanged()
	{
		InvokeAsync(async () => await ResetTimerAsync());

		_currentColor = SelectedContainer?.Color ?? ThemeColor.Inherit;
	}

	//When an item is added, it automatically checks the color
	public override void AddItem(CarouselItem item)
	{
		Items.Add(item);
		if (Items.Count - 1 == SelectedIndex)
		{
			_currentColor = item.Color;
			StateHasChanged();
		}
	}


	/// <summary>
	/// Provides Selection changes by horizontal swipe gesture
	/// </summary>
	private void OnSwipe(SwipeDirection direction)
	{
		if (!EnableSwipeGesture)
		{
			return;
		}

		switch (direction)
		{
			case SwipeDirection.LeftToRight:
				if (RightToLeft) Next();
				else Previous();
				break;

			case SwipeDirection.RightToLeft:
				if (RightToLeft) Previous();
				else Next();
				break;
		}
	}

	/// <summary>
	/// Immediately starts the AutoCycle timer
	/// </summary>
	private ValueTask StartTimerAsync()
	{
		if (AutoCycle)
			_timer?.Change(AutoCycleTime, TimeSpan.Zero);

		return ValueTask.CompletedTask;
	}

	/// <summary>
	/// Immediately stops the AutoCycle timer
	/// </summary>
	private ValueTask StopTimerAsync()
	{
		_timer?.Change(Timeout.Infinite, Timeout.Infinite);
		return ValueTask.CompletedTask;
	}

	/// <summary>
	/// Stops and restart the AutoCycle timer
	/// </summary>
	private async ValueTask ResetTimerAsync()
	{
		await StopTimerAsync();
		await StartTimerAsync();
	}


	/// <summary>
	/// Changes the SelectedIndex to a next one (or restart on 0)
	/// </summary>
	private async ValueTask TimerTickAsync()
	{
		await InvokeAsync(Next);
	}

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
		await base.OnAfterRenderAsync(firstRender);

		if (firstRender)
		{
			_timer = new Timer(TimerElapsed, null, AutoCycle ? AutoCycleTime : Timeout.InfiniteTimeSpan, AutoCycleTime);
		}
	}

	public async ValueTask DisposeAsync()
	{
		await DisposeAsync(true);
		GC.SuppressFinalize(this);
	}


	protected virtual async ValueTask DisposeAsync(bool disposing)
	{
		if (disposing)
		{
			await StopTimerAsync();

			var timer = _timer;
			if (timer != null)
			{
				_timer = null;
				await timer.DisposeAsync();
			}
		}
	}
}
