/*
 * Copyright (c) 2025 ETH Zürich, IT Services
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts.Events;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;

namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
	public partial class ProctoringFinalizationDialog : Window, IProctoringFinalizationDialog
	{
		private readonly bool requiresPassword;
		private readonly IText text;

		private WindowClosedEventHandler closed;
		private WindowClosingEventHandler closing;
		private bool initialized;

		public string QuitPassword => Password.Password;

		public event CancellationRequestedEventHandler CancellationRequested;

		event WindowClosedEventHandler IWindow.Closed
		{
			add { closed += value; }
			remove { closed -= value; }
		}

		event WindowClosingEventHandler IWindow.Closing
		{
			add { closing += value; }
			remove { closing -= value; }
		}

		public ProctoringFinalizationDialog(bool requiresPassword, IText text)
		{
			this.requiresPassword = requiresPassword;
			this.text = text;

			InitializeComponent();
			InitializeDialog();
		}

		public void BringToForeground()
		{
			Dispatcher.Invoke(Activate);
		}

		public new void Show()
		{
			Dispatcher.Invoke(() =>
			{
				InitializeBounds();
				ShowDialog();
			});
		}

		public void Update(RemainingWorkUpdatedEventArgs status)
		{
			Dispatcher.Invoke(() =>
			{
				if (status.HasFailed)
				{
					ShowFailure(status);
				}
				else if (status.IsFinished)
				{
					Close();
				}
				else
				{
					ShowProgress(status);
				}
			});
		}

		private void InitializeBounds()
		{
			Left = 0;
			Top = 0;
			Height = SystemParameters.PrimaryScreenHeight;
			Width = SystemParameters.PrimaryScreenWidth;
		}

		private void InitializeCancellation()
		{
			Button.Click += Button_Click;
			Button.Content = text.Get(TextKey.ProctoringFinalizationDialog_Abort);

			if (requiresPassword)
			{
				PasswordPanel.Visibility = Visibility.Visible;
				PasswordLabel.Text = text.Get(TextKey.ProctoringFinalizationDialog_PasswordMessage);
				Password.KeyDown += Password_KeyDown;
				Password.Focus();

				Height += PasswordPanel.ActualHeight + PasswordPanel.Margin.Top + PasswordPanel.Margin.Bottom;
			}

			initialized = true;
		}

		private void InitializeDialog()
		{
			Closed += (o, args) => closed?.Invoke();
			Closing += (o, args) => closing?.Invoke();
			Title = text.Get(TextKey.ProctoringFinalizationDialog_Title);

			InitializeBounds();

			SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
		}

		private void ShowFailure(RemainingWorkUpdatedEventArgs status)
		{
			ButtonPanel.Visibility = Visibility.Visible;
			Button.Click -= Button_Click;
			Button.Click += (o, args) => Close();
			Button.Content = text.Get(TextKey.ProctoringFinalizationDialog_Confirm);
			Button.Focus();

			// TODO: Revert once cache handling has been specified and changed!
			CachePath.Text = status.CachePath ?? "-";
			CachePath.Visibility = Visibility.Collapsed;

			Cursor = Cursors.Arrow;
			FailurePanel.Visibility = Visibility.Visible;
			Height -= PasswordPanel.IsVisible ? PasswordPanel.ActualHeight + PasswordPanel.Margin.Top + PasswordPanel.Margin.Bottom : 0;
			Message.Text = text.Get(TextKey.ProctoringFinalizationDialog_FailureMessage);
			PasswordPanel.Visibility = Visibility.Collapsed;
			ProgressPanel.Visibility = Visibility.Collapsed;
		}

		private void ShowProgress(RemainingWorkUpdatedEventArgs status)
		{
			ButtonPanel.Visibility = status.AllowCancellation ? Visibility.Visible : Visibility.Collapsed;
			Cursor = Cursors.Wait;
			FailurePanel.Visibility = Visibility.Collapsed;
			Info.Text = text.Get(TextKey.ProctoringFinalizationDialog_InfoMessage);
			ProgressPanel.Visibility = Visibility.Visible;

			if (status.AllowCancellation && !initialized)
			{
				InitializeCancellation();
			}
			else if (!status.AllowCancellation)
			{
				PasswordPanel.Visibility = Visibility.Collapsed;
			}

			if (status.IsWaiting)
			{
				UpdateWaitingProgress(status);
			}
			else
			{
				UpdateProgress(status);
			}
		}

		private void UpdateProgress(RemainingWorkUpdatedEventArgs status)
		{
			var count = $"{status.Progress}";
			var total = $"{status.Total}";

			Percentage.Text = $"{status.Progress / (double) (status.Total > 0 ? status.Total : 1) * 100:N0}%";
			Progress.IsIndeterminate = false;
			Progress.Maximum = status.Total;
			Progress.Value = status.Progress;

			if (status.Next.HasValue)
			{
				Status.Text = text.Get(TextKey.ProctoringFinalizationDialog_StatusAndTime).Replace("%%_TIME_%%", $"{status.Next.Value.ToLongTimeString()}");
			}
			else
			{
				Status.Text = text.Get(TextKey.ProctoringFinalizationDialog_Status);
			}

			Status.Text = Status.Text.Replace("%%_COUNT_%%", count).Replace("%%_TOTAL_%%", total);
		}

		private void UpdateWaitingProgress(RemainingWorkUpdatedEventArgs status)
		{
			var count = $"{status.Total - status.Progress}";
			var time = status.Resume?.ToLongTimeString();

			Percentage.Text = "";
			Progress.IsIndeterminate = true;

			if (status.Resume.HasValue)
			{
				Status.Text = text.Get(TextKey.ProctoringFinalizationDialog_StatusWaitingAndTime).Replace("%%_COUNT_%%", count).Replace("%%_TIME_%%", time);
			}
			else
			{
				Status.Text = text.Get(TextKey.ProctoringFinalizationDialog_StatusWaiting).Replace("%%_COUNT_%%", count);
			}
		}

		private void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == nameof(SystemParameters.WorkArea))
			{
				Dispatcher.InvokeAsync(InitializeBounds);
			}
		}

		private void Button_Click(object sender, RoutedEventArgs e)
		{
			CancellationRequested?.Invoke();
		}

		private void Password_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Enter)
			{
				CancellationRequested?.Invoke();
			}
		}
	}
}
