using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using Timeline.Entities;
using Timeline.Models.Validation;
using Timeline.Services.Exceptions;
namespace Timeline.Services
{
    /// 
    /// This service provide some basic user features, which should be used internally for other services.
    /// 
    public interface IBasicUserService
    {
        /// 
        /// Check if a user exists.
        /// 
        /// The id of the user.
        /// True if exists. Otherwise false.
        Task CheckUserExistence(long id);
        /// 
        /// Get the user id of given username.
        /// 
        /// Username of the user.
        /// The id of the user.
        /// Thrown when  is null.
        /// Thrown when  is of bad format.
        /// Thrown when the user with given username does not exist.
        Task GetUserIdByUsername(string username);
        /// 
        /// Get the username modified time of a user.
        /// 
        /// User id.
        /// The time.
        /// Thrown when user does not exist.
        Task GetUsernameLastModifiedTime(long userId);
    }
    public class BasicUserService : IBasicUserService
    {
        private readonly DatabaseContext _database;
        private readonly UsernameValidator _usernameValidator = new UsernameValidator();
        public BasicUserService(DatabaseContext database)
        {
            _database = database;
        }
        public async Task CheckUserExistence(long id)
        {
            return await _database.Users.AnyAsync(u => u.Id == id);
        }
        public async Task GetUserIdByUsername(string username)
        {
            if (username == null)
                throw new ArgumentNullException(nameof(username));
            if (!_usernameValidator.Validate(username, out var message))
                throw new ArgumentException(message);
            var entity = await _database.Users.Where(user => user.Username == username).Select(u => new { u.Id }).SingleOrDefaultAsync();
            if (entity == null)
                throw new UserNotExistException(username);
            return entity.Id;
        }
        public async Task GetUsernameLastModifiedTime(long userId)
        {
            var entity = await _database.Users.Where(u => u.Id == userId).Select(u => new { u.UsernameChangeTime }).SingleOrDefaultAsync();
            if (entity is null)
                throw new UserNotExistException(userId);
            return entity.UsernameChangeTime;
        }
    }
    public static class BasicUserServiceExtensions
    {
        public static async Task ThrowIfUserNotExist(this IBasicUserService service, long userId)
        {
            if (!await service.CheckUserExistence(userId))
            {
                throw new UserNotExistException(userId);
            }
        }
    }
}