using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using ElectroShop.DAL;
using ElectroShop.DAL.Entities;
using ElectroShop.Models;
using ElectroShop.ViewModels;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace ElectroShop.Services
{
    public class UserService
    {
        private readonly UserManager<ApplicationUser> userManager;
        private readonly ElectroShopDBContext context;

        public UserService(UserManager<ApplicationUser> userManager, ElectroShopDBContext context)
        {
            this.userManager = userManager;
            this.context = context;
        }

        //vrati uzivatele podle zadaneho jmena;
        public ApplicationUser GetUserByUsername(string userName)
        {
            return userManager.FindByNameAsync(userName).Result;
        }

        //vrati role podle zadaneho jmena;
        public string GetRoleByUsername(string userName)
        {
            var user = userManager.FindByNameAsync(userName).Result;
            return userManager.GetRolesAsync(user).Result.FirstOrDefault();
        }

        //vrati objekt tridy ClaimsIdentity. Jako parametry prijima objekt uzivatele, jeho role a heslo.
        //Pokud objekt user existuje, zavola se metoda CheckPasswordAsync tridy userManager a provede kontrolu validnosti hesla,
        // heslo validni ? true : false a zapise to do promeny result. result == true ? CreateIdentity() : nic.
        //Pokud objekt user neexistuje, vrati false;
        public ClaimsIdentity SignIn(ApplicationUser user, string role, string password)
        {
            if (user != null)
            {
                var result = userManager.CheckPasswordAsync(user, password).Result;
                if (result)
                {
                    return CreateIdentity(user, role);
                }
            }
            return null;
        }
        //Metodat na vytvareni noveho usera, prijima jako parametry username, jmeno, heslo, a nazev role.
        public IdentityResult CreateUser(string userName, string password, string userType, string name, string surname)
        {
            var result = userManager.CreateAsync(new ApplicationUser
            {
                UserName = userName,
                Email = userName, //zapisuje jen jmeno. Odesilani mailu a relizace smtp protokolu nejsou soucasti zadani.
                EmailConfirmed = true,
                Name = name,
                Surname = surname
            }, password).Result;
            if (!result.Succeeded)
            {
                return result;
            }
            var userAdmin = userManager.FindByNameAsync(userName).Result;
            var result2 = userManager.AddToRoleAsync(userAdmin, userType).Result;
            if (!result2.Succeeded)
            {
                return result2;
            }

            return result;
        }

        //vytvori objekt tridy Claim (identitu), jako parametr prijima objekt tridy ApplicationUser a jeho roli;
        //V konstruktoru tridy se vytvori doplnujici informace o objektu (name, id, role), a jako typ autentikaci bude zvoleno "Cookie".
        private ClaimsIdentity CreateIdentity(ApplicationUser user, string role)
        {
            var identity = new ClaimsIdentity(
                new[]
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.NameIdentifier, user.Id),
                    new Claim(ClaimTypes.Role, role),
                }, "Cookie");
            return identity;
        }
        //Vrati seznam vsech uzivatelu z databazi pres EF kontext.
        public List<UserListModel> GetUsers()
        {
            List<UserListModel> toReturn = new List<UserListModel>() { };
            var identityUsers = context.Users.ToList();
            foreach (var user in identityUsers)
            {
                UserListModel tmpUser = new UserListModel();
                tmpUser.Username = user.UserName;
                tmpUser.Name = user.Name;
                tmpUser.Surname = user.Surname;
                var roles = userManager.GetRolesAsync(user).Result;
                tmpUser.Role = roles.FirstOrDefault();
                toReturn.Add(tmpUser);
            }
            return toReturn;
        }
        //Vrati objekt tridy UserModel s parametry. Jako vstup prijima username uzivatele.
        public UserModel GetUserModelByName(string userName)
        {
            var _user = userManager.FindByNameAsync(userName).Result;
            var role = userManager.GetRolesAsync(_user).Result.FirstOrDefault();
            return new UserModel() { Username = _user.UserName, Role = role, Name = _user.Name, Surname = _user.Surname };
        }
        //metoda editovani usera
        public IdentityResult EditUser(UserModel userVM)
        {
            var user = userManager.FindByNameAsync(userVM.Username).Result;
            user.Name = userVM.Name;
            user.Surname = userVM.Surname;
            var roles = userManager.GetRolesAsync(user);
            var previousRole = roles.Result.FirstOrDefault();
            var result = userManager.RemoveFromRoleAsync(user, previousRole).Result;
            if (!result.Succeeded)
            {
                return result;
            }
            var result2 = userManager.AddToRoleAsync(user, userVM.Role).Result;
            if (!result2.Succeeded)
            {
                return result2;
            }
            var result3 = userManager.UpdateAsync(user).Result;
            if (!result3.Succeeded)
            {
                return result3;
            }

            return result;
        }
        //Maze uzivatele z databaze pres tridu UserManager
        public IdentityResult DeleteUser(UserModel userVM)
        {
            var user = userManager.FindByNameAsync(userVM.Username).Result;
            var result = userManager.DeleteAsync(user).Result;
            return result;
        }
    }
}
