#nullable enable
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Navigation;
using Wino.Core.ViewModels.Data;
using Wino.Mail.Api.Contracts.Common;
using Wino.Messaging.UI;

namespace Wino.Core.ViewModels;

public partial class WinoAccountManagementPageViewModel : CoreBaseViewModel,
    IRecipient<WinoAccountProfileUpdatedMessage>,
    IRecipient<WinoAccountProfileDeletedMessage>,
    IRecipient<WinoAccountAddOnPurchasedMessage>
{
    private readonly IWinoAccountProfileService _profileService;
    private readonly IWinoAccountDataSyncService _syncService;
    private readonly IMailDialogService _dialogService;
    private readonly IStoreManagementService _storeManagementService;
    private readonly WinoAddOnItemViewModel _aiPackAddOn;
    private readonly WinoAddOnItemViewModel _unlimitedAccountsAddOn;

    public ObservableCollection<WinoAddOnItemViewModel> AddOns { get; } = [];

    [ObservableProperty]
    public partial bool IsBusy { get; set; }

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(IsSignedOut))]
    public partial bool IsSignedIn { get; set; }

    [ObservableProperty]
    public partial string AccountEmail { get; set; } = string.Empty;

    [ObservableProperty]
    public partial string AccountStatusText { get; set; } = string.Empty;

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(PurchaseAddOnCommand))]
    public partial bool IsCheckoutInProgress { get; set; }

    public bool IsSignedOut => !IsSignedIn;

    public WinoAccountManagementPageViewModel(IWinoAccountProfileService profileService,
                                              IWinoAccountDataSyncService syncService,
                                              IMailDialogService dialogService,
                                              IStoreManagementService storeManagementService)
    {
        _profileService = profileService;
        _syncService = syncService;
        _dialogService = dialogService;
        _storeManagementService = storeManagementService;

        _aiPackAddOn = CreateAddOnItem(WinoAddOnProductType.AI_PACK);
        _unlimitedAccountsAddOn = CreateAddOnItem(WinoAddOnProductType.UNLIMITED_ACCOUNTS);
        AddOns.Add(_aiPackAddOn);
        AddOns.Add(_unlimitedAccountsAddOn);
    }

    public override void OnNavigatedTo(NavigationMode mode, object parameters)
    {
        base.OnNavigatedTo(mode, parameters);
        _ = InitializeAsync();
    }

    [RelayCommand]
    private async Task RegisterAsync()
    {
        var account = await _dialogService.ShowWinoAccountRegistrationDialogAsync();
        if (account == null)
        {
            return;
        }

        _dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
                                      string.Format(Translator.WinoAccount_RegisterSuccessMessage, account.Email),
                                      InfoBarMessageType.Success);
    }

    [RelayCommand]
    private async Task SignInAsync()
    {
        var account = await _dialogService.ShowWinoAccountLoginDialogAsync();
        if (account == null)
        {
            return;
        }

        _dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
                                      string.Format(Translator.WinoAccount_LoginSuccessMessage, account.Email),
                                      InfoBarMessageType.Success);
    }

    [RelayCommand]
    private async Task SignOutAsync()
    {
        var account = await _profileService.GetActiveAccountAsync().ConfigureAwait(false);
        if (account == null)
        {
            _dialogService.InfoBarMessage(Translator.GeneralTitle_Warning,
                                          Translator.WinoAccount_SignOut_NoAccountMessage,
                                          InfoBarMessageType.Warning);
            return;
        }

        await _profileService.SignOutAsync().ConfigureAwait(false);

        _dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
                                      string.Format(Translator.WinoAccount_SignOut_SuccessMessage, account.Email),
                                      InfoBarMessageType.Success);
    }

    [RelayCommand]
    private async Task ChangePasswordAsync()
    {
        var account = await _profileService.GetActiveAccountAsync();
        if (account == null)
        {
            _dialogService.InfoBarMessage(Translator.GeneralTitle_Warning,
                                          Translator.WinoAccount_SignOut_NoAccountMessage,
                                          InfoBarMessageType.Warning);
            return;
        }

        var shouldContinue = await _dialogService.ShowConfirmationDialogAsync(
            string.Format(Translator.WinoAccount_ChangePassword_ConfirmationMessage, account.Email),
            Translator.WinoAccount_ChangePassword_Title,
            Translator.WinoAccount_ChangePassword_Action);

        if (!shouldContinue)
        {
            return;
        }

        var response = await _profileService.ForgotPasswordAsync(account.Email);
        if (!response.IsSuccess)
        {
            _dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
                                          TranslateForgotPasswordError(response.ErrorCode),
                                          InfoBarMessageType.Error);
            return;
        }

        _dialogService.InfoBarMessage(Translator.GeneralTitle_Info,
                                      string.Format(Translator.WinoAccount_ForgotPasswordDialog_SuccessMessage, account.Email),
                                      InfoBarMessageType.Success);
    }

    private static string TranslateForgotPasswordError(string? errorCode)
        => errorCode switch
        {
            ApiErrorCodes.EmailNotRegistered => Translator.WinoAccount_Error_EmailNotRegistered,
            ApiErrorCodes.ValidationFailed => Translator.WinoAccount_Error_ValidationFailed,
            _ when string.IsNullOrWhiteSpace(errorCode) => Translator.GeneralTitle_Error,
            _ => errorCode!
        };

    [RelayCommand(CanExecute = nameof(CanPurchaseAddOn))]
    private async Task PurchaseAddOnAsync(WinoAddOnItemViewModel? addOn)
    {
        if (addOn == null)
        {
            return;
        }

        await ExecuteUIThread(() =>
        {
            IsCheckoutInProgress = true;
            addOn.IsPurchaseInProgress = true;
        });

        try
        {
            var purchaseResult = await _storeManagementService.PurchaseAsync(addOn.ProductType);

            if (purchaseResult == StorePurchaseResult.NotPurchased)
            {
                _dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
                                              Translator.WinoAccount_Management_PurchaseStartFailed,
                                              InfoBarMessageType.Error);
                return;
            }

            var syncResult = await _profileService.SyncStoreEntitlementsAsync().ConfigureAwait(false);
            if (!syncResult.IsSuccess && !string.Equals(syncResult.ErrorCode, "MissingAccessToken", StringComparison.Ordinal))
            {
                _dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
                                              TranslateStoreSyncError(syncResult.ErrorCode),
                                              InfoBarMessageType.Error);
                return;
            }

            await HandleAddOnPurchasedAsync().ConfigureAwait(false);
        }
        catch (OperationCanceledException)
        {
        }
        catch (Exception)
        {
            _dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
                                          Translator.WinoAccount_Management_PurchaseStartFailed,
                                          InfoBarMessageType.Error);
        }
        finally
        {
            await ExecuteUIThread(() =>
            {
                IsCheckoutInProgress = false;
                addOn.IsPurchaseInProgress = false;
            });
        }
    }

    private bool CanPurchaseAddOn(WinoAddOnItemViewModel? addOn)
        => addOn != null && !addOn.IsPurchased && !addOn.IsLoading && !IsCheckoutInProgress;

    [RelayCommand]
    private async Task ExportSettingsAsync()
    {
        try
        {
            var result = await _dialogService.ShowWinoAccountExportDialogAsync().ConfigureAwait(false);
            if (result == null)
            {
                return;
            }

            _dialogService.InfoBarMessage(
                Translator.GeneralTitle_Info,
                BuildExportSuccessMessage(result),
                InfoBarMessageType.Success);
        }
        catch (Exception ex)
        {
            _dialogService.InfoBarMessage(
                Translator.GeneralTitle_Error,
                ex.Message,
                InfoBarMessageType.Error);
        }
    }

    [RelayCommand]
    private async Task ImportSettingsAsync()
    {
        await ExecuteUIThread(() => IsBusy = true);

        try
        {
            var result = await _syncService.ImportAsync(new WinoAccountSyncSelection());

            if (!result.HasAnyRemoteData)
            {
                _dialogService.InfoBarMessage(
                    Translator.GeneralTitle_Info,
                    Translator.WinoAccount_Management_NoRemoteSettings,
                    InfoBarMessageType.Information);
                return;
            }

            var messageType = result.FailedPreferenceCount > 0
                ? InfoBarMessageType.Warning
                : InfoBarMessageType.Success;

            _dialogService.InfoBarMessage(
                result.FailedPreferenceCount > 0 ? Translator.GeneralTitle_Warning : Translator.GeneralTitle_Info,
                BuildImportMessage(result),
                messageType);
        }
        catch (Exception ex)
        {
            _dialogService.InfoBarMessage(
                Translator.GeneralTitle_Error,
                ex.Message,
                InfoBarMessageType.Error);
        }
        finally
        {
            await ExecuteUIThread(() => IsBusy = false);
        }
    }

    protected override void RegisterRecipients()
    {
        base.RegisterRecipients();

        Messenger.Register<WinoAccountProfileUpdatedMessage>(this);
        Messenger.Register<WinoAccountProfileDeletedMessage>(this);
        Messenger.Register<WinoAccountAddOnPurchasedMessage>(this);
    }

    protected override void UnregisterRecipients()
    {
        base.UnregisterRecipients();

        Messenger.Unregister<WinoAccountProfileUpdatedMessage>(this);
        Messenger.Unregister<WinoAccountProfileDeletedMessage>(this);
        Messenger.Unregister<WinoAccountAddOnPurchasedMessage>(this);
    }

    public void Receive(WinoAccountProfileUpdatedMessage message)
        => _ = LoadAsync();

    public void Receive(WinoAccountProfileDeletedMessage message)
        => _ = LoadAsync();

    public void Receive(WinoAccountAddOnPurchasedMessage message)
        => _ = HandleAddOnPurchasedAsync();

    private async Task InitializeAsync()
    {
        await LoadAsync().ConfigureAwait(false);
    }

    private async Task LoadAsync()
    {
        WinoAccount? cachedAccount = null;

        try
        {
            cachedAccount = await _profileService.GetActiveAccountAsync().ConfigureAwait(false);

            if (cachedAccount != null)
            {
                await ApplyAccountStateAsync(cachedAccount).ConfigureAwait(false);
            }

            await ExecuteUIThread(() => IsBusy = true);
            await ResetAddOnStatesAsync().ConfigureAwait(false);
            var loadAiPackTask = LoadAiPackAddOnAsync();
            var loadUnlimitedAccountsTask = LoadUnlimitedAccountsAddOnAsync();

            var resolvedAccount = cachedAccount;

            if (cachedAccount == null || IsAccessTokenExpired(cachedAccount))
            {
                try
                {
                    var account = await _profileService.GetAuthenticatedAccountAsync().ConfigureAwait(false);
                    if (account != null)
                    {
                        resolvedAccount = account;

                        var refreshedProfileResult = await _profileService.RefreshProfileAsync().ConfigureAwait(false);
                        if (refreshedProfileResult.IsSuccess && refreshedProfileResult.Account != null)
                        {
                            resolvedAccount = refreshedProfileResult.Account;
                        }
                    }
                }
                catch (Exception)
                {
                    resolvedAccount ??= cachedAccount;
                }
            }

            await ApplyAccountStateAsync(resolvedAccount).ConfigureAwait(false);
            await Task.WhenAll(loadAiPackTask, loadUnlimitedAccountsTask).ConfigureAwait(false);
        }
        catch (Exception)
        {
            if (cachedAccount == null)
            {
                _dialogService.InfoBarMessage(Translator.GeneralTitle_Error,
                                              Translator.WinoAccount_Management_LoadFailed,
                                              InfoBarMessageType.Error);
                await ResetStateAsync().ConfigureAwait(false);
            }
        }
        finally
        {
            await ExecuteUIThread(() => IsBusy = false);
        }
    }

    private async Task ApplyAccountStateAsync(Wino.Core.Domain.Entities.Shared.WinoAccount? account)
    {
        await ExecuteUIThread(() =>
        {
            IsSignedIn = account != null;
            AccountEmail = account?.Email ?? string.Empty;
            AccountStatusText = account == null
                ? string.Empty
                : string.Format(Translator.WinoAccount_Management_StatusLabel, account.AccountStatus);
        });
    }

    private async Task HandleAddOnPurchasedAsync()
    {
        await LoadAsync().ConfigureAwait(false);

        _dialogService.InfoBarMessage(Translator.Info_PurchaseThankYouTitle,
                                      Translator.Info_PurchaseThankYouMessage,
                                      InfoBarMessageType.Success);
    }

    private async Task ResetStateAsync()
    {
        await ExecuteUIThread(() =>
        {
            IsSignedIn = false;
            AccountEmail = string.Empty;
            AccountStatusText = string.Empty;
            IsCheckoutInProgress = false;
            PurchaseAddOnCommand.NotifyCanExecuteChanged();
        });

        await ResetAddOnStatesAsync().ConfigureAwait(false);
    }

    private WinoAddOnItemViewModel CreateAddOnItem(WinoAddOnProductType productType)
    {
        return new WinoAddOnItemViewModel(productType)
        {
            PurchaseCommand = PurchaseAddOnCommand,
            UsageLimit = 1
        };
    }

    private async Task ResetAddOnStatesAsync()
    {
        await ExecuteUIThread(() =>
        {
            ResetAddOnItem(_aiPackAddOn);
            ResetAddOnItem(_unlimitedAccountsAddOn);
            PurchaseAddOnCommand.NotifyCanExecuteChanged();
        });
    }

    private static void ResetAddOnItem(WinoAddOnItemViewModel addOn)
    {
        addOn.IsLoading = true;
        addOn.IsPurchased = false;
        addOn.IsPurchaseInProgress = false;
        addOn.HasUsageData = false;
        addOn.ErrorText = string.Empty;
        addOn.UsageCount = 0;
        addOn.UsageLimit = 1;
        addOn.UsagePercentage = 0;
        addOn.RenewalText = string.Empty;
        addOn.UsageResetText = string.Empty;
    }

    private static string TranslateStoreSyncError(string? errorCode)
        => errorCode switch
        {
            _ => Translator.WinoAccount_Management_StoreSyncFailed
        };

    private static string BuildExportSuccessMessage(WinoAccountSyncExportResult result)
    {
        var parts = new Collection<string>();

        if (result.IncludedPreferences)
        {
            parts.Add(Translator.WinoAccount_Management_ExportPreferencesSucceeded);
        }

        if (result.IncludedAccounts)
        {
            parts.Add(string.Format(Translator.WinoAccount_Management_ExportAccountsSucceeded, result.ExportedMailboxCount));
        }

        if (parts.Count == 0)
        {
            parts.Add(Translator.WinoAccount_Management_ExportSucceeded);
        }

        return string.Join(" ", parts);
    }

    private static string BuildImportMessage(WinoAccountSyncImportResult result)
    {
        var parts = new Collection<string>();

        if (result.HadRemotePreferences)
        {
            parts.Add(result.FailedPreferenceCount > 0
                ? string.Format(Translator.WinoAccount_Management_ImportPartial, result.AppliedPreferenceCount, result.FailedPreferenceCount)
                : string.Format(Translator.WinoAccount_Management_ImportPreferencesSucceeded, result.AppliedPreferenceCount));
        }

        if (result.ImportedMailboxCount > 0)
        {
            parts.Add(string.Format(Translator.WinoAccount_Management_ImportAccountsSucceeded, result.ImportedMailboxCount));
        }

        if (result.SkippedDuplicateMailboxCount > 0)
        {
            parts.Add(string.Format(Translator.WinoAccount_Management_ImportDuplicateAccountsSkipped, result.SkippedDuplicateMailboxCount));
        }

        if (parts.Count == 0)
        {
            parts.Add(Translator.WinoAccount_Management_ImportEmpty);
        }

        if (result.ImportedMailboxCount > 0)
        {
            parts.Add(Translator.WinoAccount_Management_ImportReloginReminder);
        }

        return string.Join(" ", parts);
    }

    private static bool IsAccessTokenExpired(WinoAccount account)
        => string.IsNullOrWhiteSpace(account.AccessToken) || account.AccessTokenExpiresAtUtc <= DateTime.UtcNow;

    private async Task LoadUnlimitedAccountsAddOnAsync()
    {
        try
        {
            var hasUnlimitedAccounts = await _storeManagementService.HasProductAsync(WinoAddOnProductType.UNLIMITED_ACCOUNTS).ConfigureAwait(false);
            await ExecuteUIThread(() =>
            {
                _unlimitedAccountsAddOn.IsPurchased = hasUnlimitedAccounts;
                _unlimitedAccountsAddOn.ErrorText = string.Empty;
            });
        }
        catch (Exception)
        {
            await ExecuteUIThread(() =>
            {
                _unlimitedAccountsAddOn.ErrorText = Translator.WinoAccount_Management_AddOnLoadFailed;
            });
        }
        finally
        {
            await ExecuteUIThread(() =>
            {
                _unlimitedAccountsAddOn.IsLoading = false;
                PurchaseAddOnCommand.NotifyCanExecuteChanged();
            });
        }
    }

    private async Task LoadAiPackAddOnAsync()
    {
        try
        {
            var hasAiPack = await _storeManagementService.HasProductAsync(WinoAddOnProductType.AI_PACK).ConfigureAwait(false);

            await ExecuteUIThread(() =>
            {
                _aiPackAddOn.IsPurchased = hasAiPack;
                _aiPackAddOn.ErrorText = string.Empty;
            });

            if (!hasAiPack)
            {
                return;
            }

            var aiStatusResponse = await _profileService.GetAiStatusAsync().ConfigureAwait(false);
            if (!aiStatusResponse.IsSuccess || aiStatusResponse.Result == null)
            {
                await ExecuteUIThread(() =>
                {
                    _aiPackAddOn.HasUsageData = false;
                    _aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AiPackUsageLoadFailed;
                });
                return;
            }

            var aiStatus = aiStatusResponse.Result;
            if (aiStatus.MonthlyLimit is not int usageLimit || usageLimit <= 0 || aiStatus.Used is not int usageCount)
            {
                await ExecuteUIThread(() =>
                {
                    _aiPackAddOn.HasUsageData = false;
                    _aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AiPackUsageLoadFailed;
                });
                return;
            }

            await ExecuteUIThread(() =>
            {
                _aiPackAddOn.HasUsageData = true;
                _aiPackAddOn.ErrorText = string.Empty;
                _aiPackAddOn.UsageCount = usageCount;
                _aiPackAddOn.UsageLimit = usageLimit;
                _aiPackAddOn.UsagePercentage = usageLimit > 0 ? (double)usageCount / usageLimit * 100 : 0;
                _aiPackAddOn.RenewalText = aiStatus.CurrentPeriodEndUtc is DateTimeOffset renewalDateUtc
                    ? string.Format(Translator.WinoAccount_Management_AiPackRenews, renewalDateUtc.LocalDateTime)
                    : string.Empty;
                _aiPackAddOn.UsageResetText = aiStatus.CurrentPeriodEndUtc is DateTimeOffset resetDateUtc
                    ? string.Format(Translator.WinoAccount_Management_AiPackResets, resetDateUtc.LocalDateTime)
                    : string.Empty;
            });
        }
        catch (Exception)
        {
            await ExecuteUIThread(() =>
            {
                _aiPackAddOn.HasUsageData = false;
                _aiPackAddOn.ErrorText = Translator.WinoAccount_Management_AddOnLoadFailed;
            });
        }
        finally
        {
            await ExecuteUIThread(() =>
            {
                _aiPackAddOn.IsLoading = false;
                PurchaseAddOnCommand.NotifyCanExecuteChanged();
            });
        }
    }
}
