
using Http.Core;
using Http.Core.Extensions;
using PCUT.Entities;
using PCUT.Helpers;
using PCUT.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Input;
using static Http.Core.Constants.HttpConstants;
using PCUT.Entities.ApiResponse;
using Windows.Foundation;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
using Http.Core.Exceptions;

namespace PCUT.ViewModels
{
    internal class UpsertUserViewModel : NotificationBase
    {
        public event UpsertProcessedEventHandler Processed;

        private const string AdminRole = "admin";
        private const string UserRole = "user";
        private const string ActiveStatus = "Active";
        private const string InactiveStatus = "Inactive";
        private const string AddTitle = "ADD USER";
        private const string EditTitle = "EDIT USER";

        public List<string> Roles { get; } = new List<string> { AdminRole, UserRole };
        public List<string> Statuses { get; } = new List<string> { ActiveStatus, InactiveStatus };

        public DateTimeOffset MinimumDate { get; }
        public ICommand Command { get; }

        private UserProfile _profile;

        public UpsertUserViewModel()
        {
            _isActive = true;
            var minimumDate = DateTimeOffset.Now.AddDays(1);
            minimumDate -= minimumDate.TimeOfDay;
            MinimumDate = minimumDate;
            _expiredAt = minimumDate;
            Command = new RelayCommand(async () => await UpsertUserAsync());
        }

        private bool _addMode; // true: add, false: edit
        public bool AddMode
        {
            get { return _addMode; }
            set
            {
                SetProperty(ref _addMode, value);
                Title = value ? AddTitle : EditTitle;
            }
        }

        private string _title;
        public string Title
        {
            get { return _title; }
            private set { SetProperty(ref _title, value); }
        }

        private string _accountName;
        public string AccountName
        {
            get { return _accountName; }
            set { SetProperty(ref _accountName, value); }
        }

        private string _username;
        public string Username
        {
            get { return _username; }
            set { SetProperty(ref _username, value); }
        }

        private string _phone;
        public string Phone
        {
            get { return _phone; }
            set { SetProperty(ref _phone, value); }
        }

        private string _role;
        public string Role
        {
            get { return _role; }
            set { SetProperty(ref _role, value); }
        }

        private bool _isActive;
        public bool IsActive
        {
            get { return _isActive; }
            set { SetProperty(ref _isActive, value); }
        }

        private bool _isPermittedEdit;
        public bool IsPermittedEdit
        {
            get { return _isPermittedEdit; }
            set { SetProperty(ref _isPermittedEdit, value); }
        }

        private DateTimeOffset? _expiredAt;
        public DateTimeOffset? ExpiredAt
        {
            get { return _expiredAt; }
            set
            {
                if (value != null)
                    value -= value?.TimeOfDay;
                SetProperty(ref _expiredAt, value);
            }
        }

        private string _description;
        public string Description
        {
            get { return _description; }
            set { SetProperty(ref _description, value); }
        }

        private string _password;
        public string Password
        {
            get { return _password; }
            set { SetProperty(ref _password, value); }
        }

        public ObservableCollection<Permission> Permissions { get; } = new ObservableCollection<Permission>();
        private readonly HashSet<string> _currentPermission = new HashSet<string>();

        private void OnRoleChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(Role))
            {
                foreach (var permission in Permissions)
                {
                    permission.PropertyChanged -= OnPermissionSelectedChanged;
                    permission.IsSelected = Role == AdminRole || _currentPermission.Contains(permission.Category.Id);
                    permission.PropertyChanged += OnPermissionSelectedChanged;
                }
                IsPermittedEdit = Role == AdminRole || (_profile.IsPermittedEdit ?? false);
            }
        }

        private void OnPermissionSelectedChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == nameof(Permission.IsSelected))
            {
                var permission = sender as Permission;
                if (permission.IsSelected)
                    _currentPermission.Add(permission.Category.Id);
                else
                    _currentPermission.Remove(permission.Category.Id);
            }
        }

        public async Task LoadCategoriesAsync()
        {
            var categories = await GetCategoryAsync();
            foreach (var permission in categories.Select(category => new Permission(category)))
            {
                permission.PropertyChanged += OnPermissionSelectedChanged;
                Permissions.Add(permission);
            }
            PropertyChanged += OnRoleChanged;
        }

        private static async Task<IEnumerable<Category>> GetCategoryAsync()
        {
            using (HttpClient client = HttpClientFactory.CreateClient(ClientNames.ApiClient))
            {
                return await client.GetCategoriesAsync();
            }
        }

        public async Task LoadUserAsync(string userId)
        {
            using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient))
            {
                try
                {
                    var response = await httpClient.GetAsync(Api.UserById.FormatRoute(userId));
                    if (response.IsSuccessStatusCode)
                    {
                        var apiResponse = await response.DeserializeObjectAsync<DataResponse<UserProfile>>();
                        _profile = apiResponse.Data;

                        foreach (var permissionId in _profile.Permissions)
                            _currentPermission.Add(permissionId);

                        AccountName = _profile.AccountName;
                        Username = _profile.UserName;
                        Phone = _profile.Phone;
                        IsActive = _profile.IsActive;
                        Role = _profile.Role;
                        IsPermittedEdit = _profile.Role == AdminRole || (_profile.IsPermittedEdit ?? false);
                        ExpiredAt = _profile.ExpiredAt?.ToLocalTime();
                        Description = _profile.Description;
                    }
                }
                catch (Exception ex) when (!(ex is AppOutdatedException))
                {
                }
            }
        }

        private async Task UpsertUserAsync()
        {
            var success = false;
            if (await ValidateUserAsync())
            {
                var user = new UpsertUser
                {
                    AccountName = AccountName,
                    Username = Username,
                    Phone = Phone,
                    Role = Role,
                    IsActive = IsActive,
                    IsPermittedEdit = IsPermittedEdit,
                    ExpiredAt = ExpiredAt?.ToUniversalTime().ToString("o"),
                    Password = !string.IsNullOrEmpty(Password) ? Password : null,
                    Permissions = Permissions.Where(c => c.IsSelected).Select(c => c.Category.Id).ToList(),
                    Description = Description,
                };
                success = await UpsertUserAsync(_addMode ? Api.Users : Api.UserById.FormatRoute(_profile?.Id), user);
            }
            Processed?.Invoke(this, new UpsertProcessedEventArgs(_addMode ? "add" : "edit", success, _profile?.Id));
        }

        private async Task<bool> ValidateUserAsync()
        {
            if (string.IsNullOrEmpty(Username))
            {
                await ShowDialogAsync("Cần nhập username!");
                return false;
            }

            if (_addMode && string.IsNullOrEmpty(Password))
            {
                await ShowDialogAsync("Cần nhập password!");
                return false;
            }

            if (string.IsNullOrEmpty(Role))
            {
                await ShowDialogAsync("Cần chọn role!");
                return false;
            }

            var selectedCategories = Permissions.Where(c => c.IsSelected);
            if (Role == UserRole && !selectedCategories.Any())
            {
                await ShowDialogAsync("Chọn ít nhất 1 danh mục cho user!");
                return false;
            }

            return true;
        }

        private async Task<bool> UpsertUserAsync(string path, object user)
        {
            using (var httpClient = HttpClientFactory.CreateClient(ClientNames.ApiClient))
            {
                try
                {
                    var response = await (_addMode ? httpClient.PostAsJsonAsync(path, user) : httpClient.PutAsJsonAsync(path, user, ignoreNull: true));
                    if (response.IsSuccessStatusCode)
                    {
                        await ShowDialogAsync($"Account {(_addMode ? "created" : "updated")} successfully!", $"{(_addMode ? "Add" : "Edit")} successful!");
                    }
                    else
                    {
                        var errorMessage = await response.DeserializeObjectAsync<MessageResponse>(ensureSuccess: false);
                        await ShowDialogAsync(errorMessage.ErrorMessage);
                        return false;
                    }
                }
                catch (Exception ex) when (!(ex is AppOutdatedException))
                {
                    await ShowDialogAsync(ex.Message);
                    return false;
                }
            }
            return true;
        }

        private IAsyncOperation<ContentDialogResult> ShowDialogAsync(string message, string title = null)
        {
            var errorDialog = new ContentDialog()
            {
                Title = title,
                Content = message,
                PrimaryButtonText = "OK"
            };
            errorDialog.PrimaryButtonStyle = Application.Current.Resources["PrimaryDialogButton"] as Style;
            return errorDialog.ShowAsync();
        }
    }

    public delegate void UpsertProcessedEventHandler(object sender, UpsertProcessedEventArgs args);
    public class UpsertProcessedEventArgs : EventArgs
    {
        public string Mode { get; }
        public bool Successed { get; }
        public string Id { get; }

        public UpsertProcessedEventArgs(string mode, bool successed, string id = null)
        {
            Mode = mode;
            Successed = successed;
            Id = id;
        }
    }

    public class Permission : NotificationBase
    {
        public readonly Category Category;

        public string Name { get => Category.Name; }

        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set { SetProperty(ref _isSelected, value); }
        }

        public Permission(Category category, bool isSelected = false)
        {
            Category = category;
            _isSelected = isSelected;
        }
    }
}
