using Connected.Extensions;
using Connected.Services;
using Connected.Utilities;
using Microsoft.AspNetCore.Components;

namespace Connected.Components;

public partial class Drawer : UIComponent, IDisposable, INavigationEventReceiver
{
	private double _height;
	private ElementReference _contentRef;
	private DrawerClipMode _clipMode;
	private bool? _isOpenWhenLarge = null;
	private bool _open;
	private bool _rtl;
	private bool _isRendered;
	private bool _initial = true;
	private bool _keepInitialState;
	private bool _fixed = true;
	private Breakpoint _breakpoint = Breakpoint.Md;
	private Breakpoint _screenBreakpoint = Breakpoint.None;
	private Guid _breakpointListenerSubscriptionId;

	#region EventCallbacks


	[Parameter] public EventCallback<bool> OpenChanged { get; set; }

	public async Task OnNavigation()
	{
		if (Variant == DrawerVariant.Temporary ||
			(Variant == DrawerVariant.Responsive && await Breakpointistener.GetBreakpoint() < Breakpoint))
		{
			await OpenChanged.InvokeAsync(false);
		}
	}

	private void ResizeListener_OnBreakpointChanged(object sender, Breakpoint breakpoint)
	{
		if (!_isRendered)
			return;

		InvokeAsync(() => UpdateBreakpointState(breakpoint));
	}

	private async void UpdateBreakpointState(Breakpoint breakpoint)
	{
		var isStateChanged = false;
		if (breakpoint == Breakpoint.None)
		{
			breakpoint = await Breakpointistener.GetBreakpoint();
		}

		if (breakpoint < Breakpoint && _screenBreakpoint >= Breakpoint && (Variant == DrawerVariant.Responsive || Variant == DrawerVariant.Mini))
		{
			_isOpenWhenLarge = Open;

			await OpenChanged.InvokeAsync(false);
			isStateChanged = true;
		}
		else if (breakpoint >= Breakpoint && _screenBreakpoint < Breakpoint && (Variant == DrawerVariant.Responsive || Variant == DrawerVariant.Mini))
		{
			if (Open && PreserveOpenState)
			{
				DrawerContainer?.FireDrawersChanged();
				isStateChanged = true;
			}
			else if (_isOpenWhenLarge != null)
			{
				await OpenChanged.InvokeAsync(_isOpenWhenLarge.Value);
				_isOpenWhenLarge = null;
				isStateChanged = true;
			}
		}

		if (_breakpoint != _screenBreakpoint)
		{
			isStateChanged = true;
		}
		_screenBreakpoint = breakpoint;

		if (isStateChanged)
		{
			StateHasChanged();
		}
	}

	internal string GetPosition()
	{
		switch (Anchor)
		{
			case Anchor.Start:
				return RightToLeft ? "right" : "left";
			case Anchor.End:
				return RightToLeft ? "left" : "right";
			default: break;
		}

		return Anchor.ToDescription();
	}

	private bool closeOnMouseLeave = false;

	public async void OnMouseEnter()
	{
		if (Variant == DrawerVariant.Mini && !Open && OpenMiniOnHover)
		{
			closeOnMouseLeave = true;
			await OpenChanged.InvokeAsync(true);
		}
	}

	public async void OnMouseLeave()
	{
		if (Variant == DrawerVariant.Mini && Open && closeOnMouseLeave)
		{
			closeOnMouseLeave = false;
			await OpenChanged.InvokeAsync(false);
		}
	}

	#endregion

	#region Content placeholders

	/// <summary>
	/// Child content of component.
	/// </summary>
	[Parameter]
	public RenderFragment ChildContent { get; set; }

	[CascadingParameter] DrawerContainer DrawerContainer { get; set; }

	[Inject] public IBreakpointService Breakpointistener { get; set; }

	[CascadingParameter(Name = "RightToLeft")]
	bool RightToLeft
	{
		get => _rtl;
		set
		{
			if (_rtl != value)
			{
				_rtl = value;
				DrawerContainer?.FireDrawersChanged();
			}
		}
	}

	#endregion

	#region Styling properties

	private CssBuilder CompiledClassList
	{
		get
		{
			return new CssBuilder("drawer")
			  .AddClass($"drawer-fixed", Fixed)
			  .AddClass($"drawer-pos-{GetPosition()}")
			  .AddClass($"drawer--open", Open)
			  .AddClass($"drawer--closed", !Open)
			  .AddClass($"drawer--initial", _initial)
			  .AddClass($"drawer-{Breakpoint.ToDescription()}")
			  .AddClass($"drawer-clipped-{_clipMode.ToDescription()}")
			  //.AddClass($"theme-{Color.ToDescription()}", Color != ThemeColor.Default)
			  .AddClass($"elevation-{Elevation}")
			  .AddClass($"drawer-{Variant.ToDescription()}")
			  .AddClass(ClassList);
		}
	}

	protected StyleBuilder CompiledStyleList
	{
		get
		{
			return new StyleBuilder()
				.AddStyle(StyleList);
			//.AddStyle("width", Width, !string.IsNullOrWhiteSpace(Width) && !Fixed)
			//.AddStyle("--mud-drawer-width", Width, !string.IsNullOrWhiteSpace(Width) && (!Fixed || Variant == DrawerVariant.Temporary))
			//.AddStyle("height", Height, !string.IsNullOrWhiteSpace(Height))
			//.AddStyle("--mud-drawer-content-height",string.IsNullOrWhiteSpace(Height) ? _height.ToPx() : Height,Anchor == Anchor.Bottom || Anchor == Anchor.Top)
			//.AddStyle("visibility", "hidden", string.IsNullOrWhiteSpace(Height) && _height == 0 && (Anchor == Anchor.Bottom || Anchor == Anchor.Top));

		}
	}

	protected CssBuilder CompiledOverlayClass
	{
		get
		{
			return new CssBuilder("drawer-overlay")
			  .AddClass($"drawer-pos-{GetPosition()}")
			  .AddClass($"drawer-overlay--open", Open)
			  .AddClass($"drawer-overlay-{Variant.ToDescription()}")
			  .AddClass($"drawer-overlay-{Breakpoint.ToDescription()}")
			  .AddClass($"drawer-overlay--initial", _initial);
		}
	}

	/// <summary>
	/// A space separated list of class names, added on top of the default class list.
	/// </summary>
	[Parameter]
	public string? ClassList { get; set; }

	/// <summary>
	/// A space separated list of class names, added on top of the default class list.
	/// </summary>
	[Parameter]
	public string? StyleList { get; set; }

	/// <summary>
	/// If true, drawer position will be fixed. (CSS position: fixed;)
	/// </summary>
	[Parameter]
	public bool Fixed
	{
		get => _fixed && DrawerContainer is Layout;
		set
		{
			if (_fixed == value)
				return;
			_fixed = value;
		}
	}

	/// <summary>
	/// The higher the number, the heavier the drop-shadow. 0 for no shadow.
	/// </summary>
	[Parameter]
	public int Elevation { set; get; } = 1;

	/// <summary>
	/// Side from which the drawer will appear.
	/// </summary>
	[Parameter]
	public Anchor Anchor { get; set; } = Anchor.Start;

	/*
	/// <summary>
	/// The color of the component. It supports the theme colors.
	/// </summary>
	[Parameter]
	public ThemeColor Color { get; set; } = ThemeColor.Default;
	*/

	/// <summary>
	/// Variant of the drawer. It specifies how the drawer will be displayed.
	/// </summary>
	[Parameter]
	public DrawerVariant Variant { get; set; } = DrawerVariant.Responsive;

	
	/// <summary>
	/// Show overlay for responsive and temporary drawers.
	/// </summary>
	[Parameter]
	public bool DisableOverlay { get; set; } = false;

	/// <summary>
	/// Preserve open state for responsive drawer when window resized above <see cref="Breakpoint" />.
	/// </summary>
	[Parameter]
	public bool PreserveOpenState { get; set; } = false;

	/// <summary>
	/// Open drawer automatically on mouse enter when <see cref="Variant" /> parameter is set to <see cref="DrawerVariant.Mini" />.
	/// </summary>
	[Parameter]
	public bool OpenMiniOnHover { get; set; }

	/// <summary>
	/// Specify how the drawer should behave inside a MudLayout. It affects the position relative to <b>MudAppbar</b>
	/// </summary>
	[Parameter]
	public DrawerClipMode ClipMode
	{
		get => _clipMode;
		set
		{
			if (_clipMode == value)
			{
				return;
			}
			_clipMode = value;
			if (Fixed)
			{
				DrawerContainer?.FireDrawersChanged();
			}
			StateHasChanged();
		}
	}

	private bool OverlayVisible => _open && !DisableOverlay &&
		(Variant == DrawerVariant.Temporary ||
		 (_screenBreakpoint < Breakpoint && Variant == DrawerVariant.Mini) ||
		 (_screenBreakpoint < Breakpoint && Variant == DrawerVariant.Responsive));

	/// <summary>
	/// Switching point for responsive drawers
	/// </summary>
	[Parameter]
	public Breakpoint Breakpoint
	{
		get => _breakpoint;
		set
		{
			if (value == _breakpoint)
				return;

			_breakpoint = value;
			if (_isRendered)
			{
				UpdateBreakpointState(_screenBreakpoint);
			}

			DrawerContainer?.FireDrawersChanged();
		}
	}

	/// <summary>
	/// Sets the opened state on the drawer. Can be used with two-way binding to close itself on navigation.
	/// </summary>
	[Parameter]
	public bool Open
	{
		get => _open;
		set
		{
			if (_open == value)
			{
				return;
			}
			_open = value;
			if (_isRendered && _initial && !_keepInitialState)
			{
				_initial = false;
			}
			if (_keepInitialState)
			{
				_keepInitialState = false;
			}
			/*if (_isRendered && value && (Anchor == Anchor.Top || Anchor == Anchor.Bottom))
			{
				_ = UpdateHeight();
			}*/

			DrawerContainer?.FireDrawersChanged();
			OpenChanged.InvokeAsync(_open);
		}
	}
	private void CloseDrawer()
	{
		if (Open)
		{
			OpenChanged.InvokeAsync(false);
		}
	}


	#endregion

	#region Lifecycle

	protected override void OnInitialized()
	{
		if (Variant != DrawerVariant.Temporary)
		{
			DrawerContainer?.Add(this);
		}
		base.OnInitialized();
	}

	protected override async Task OnAfterRenderAsync(bool firstRender)
	{
		if (firstRender)
		{
			//await UpdateHeight();
			var result = await Breakpointistener.Subscribe(UpdateBreakpointState);
			//var currentBreakpoint = result.Breakpoint;

			_breakpointListenerSubscriptionId = result.SubscriptionId;

			var refresh = false;

			if (_screenBreakpoint != result.Breakpoint) refresh = true;
			_screenBreakpoint = result.Breakpoint;
			if (_screenBreakpoint < Breakpoint && _open)
			{
				_keepInitialState = true;
				await OpenChanged.InvokeAsync(false);
			}

			_isRendered = true;
			if (Anchor == Anchor.Bottom || Anchor == Anchor.Top || _screenBreakpoint == Breakpoint.None)
			{
				refresh = true;
			}
			if (refresh) StateHasChanged();
		}

		await base.OnAfterRenderAsync(firstRender);
	}

	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	int _disposeCount;
	public virtual void Dispose(bool disposing)
	{
		if (Interlocked.Increment(ref _disposeCount) == 1)
		{
			if (disposing)
			{
				DrawerContainer?.Remove(this);

				if (_breakpointListenerSubscriptionId != default)
				{
					Breakpointistener.Unsubscribe(_breakpointListenerSubscriptionId).AndForget();
				}
			}
		}
	}

	#endregion

}
