using Blahblah.FlowerStory.Server.Data;
using Blahblah.FlowerStory.Server.Data.Model;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography;

namespace Blahblah.FlowerStory.Server.Controller;

/// <summary>
/// 用户会话相关 API 服务
/// </summary>
[ApiController]
[Produces("application/json")]
[Route("api/user")]
public partial class UserApiController : BaseController
{
    /// <inheritdoc/>
    public UserApiController(FlowerDatabase db, ILogger<UserApiController> logger) : base(db, logger)
    {
    }

    /// <summary>
    /// 用户登录
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/user/auth
    ///     {
    ///         "id": "blahblah",
    ///         "password": "pwd123"
    ///     }
    /// 
    /// </remarks>
    /// <param name="login">登录参数</param>
    /// <returns>成功登录则返回自定义认证头</returns>
    /// <response code="200">返回用户对象，返回头中包含认证信息</response>
    /// <response code="401">认证失败</response>
    /// <response code="404">未找到用户</response>
    /// <response code="500">服务器错误</response>
    [Route("auth", Name = "authenticate")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("application/json")]
    public ActionResult<UserItem> Authenticate([FromBody] LoginParamter login)
    {
#if DEBUG
        logger?.LogInformation("user \"{user}\" try to login with password \"{password}\"", login.Id, login.Password);
#endif
        //var user = database.Users.SingleOrDefault(u => u.UserId == login.Id);
        var user = QueryUserItemForAuthentication(login.Id);
        if (user == null)
        {
            logger?.LogWarning("user \"{user}\" not found", login.Id);
            return NotFound();
        }

        // comput password hash with salt
        string hash = HashPassword(login.Password, login.Id);
        if (hash != user.Password)
        {
            logger?.LogWarning("hash result {hash}, unauthorized with hash {password}", hash, user.Password);
            return Unauthorized();
        }

        // record the session
        // TODO: singleton token
        string clientApp;
        int expires;
        if (Request.Headers.TryGetValue("X-ClientAgent", out var clientAgent))
        {
            clientApp = "app";
            expires = 30 * 24 * 60 * 60;    // 30 days
        }
        else
        {
            clientApp = "browser";
            expires = 20 * 60;  // 20 mins
        }
        var now = DateTimeOffset.UtcNow;
        var token = new TokenItem
        {
            Id = Convert.ToBase64String(SHA256.HashData(Guid.NewGuid().ToByteArray())),
            UserId = user.Id,
            LogonDateUnixTime = now.ToUnixTimeMilliseconds(),
            ActiveDateUnixTime = now.ToUnixTimeMilliseconds(),
            ExpireDateUnixTime = now.AddSeconds(expires).ToUnixTimeMilliseconds(),
            ExpireSeconds = expires,
            ClientApp = clientApp,
            ClientAgent = clientAgent.Count > 0 ? clientAgent : Request.Headers.UserAgent,
            DeviceId = Request.Headers.TryGetValue("X-DeviceId", out var deviceId) ? deviceId.ToString() : null
        };
        database.Tokens.Add(token);

        user.ActiveDateUnixTime = token.ActiveDateUnixTime;
        SaveDatabase();

        Response.Headers.Add(AuthHeader, token.Id);
        return Ok(user);
    }

    /// <summary>
    /// 判断当前会话是否有效
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/user/validation
    ///     Authorization: authorization id
    /// 
    /// </remarks>
    /// <returns>会话有效则返回会话对象</returns>
    /// <response code="200">返回会话对象</response>
    /// <response code="401">认证失败</response>
    [Route("validation", Name = "validation")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<TokenItem> Validation()
    {
        var (result, token) = CheckToken();
        if (result != null)
        {
            return result;
        }
        if (token == null)
        {
            return Unauthorized();
        }

        // update last active time
        SaveDatabase();

        return Ok(token);
    }

    /// <summary>
    /// 注销当前登录会话
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/user/logout
    ///     Authorization: authorization id
    /// 
    /// </remarks>
    /// <returns>注销失败则返回错误内容</returns>
    /// <response code="204">注销成功</response>
    /// <response code="401">认证失败</response>
    [Route("logout", Name = "logout")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    public ActionResult Logout()
    {
        var (result, token) = CheckToken();
        if (result != null)
        {
            return result;
        }
        if (token == null)
        {
            return Unauthorized();
        }

        database.Tokens.Remove(token);
        SaveDatabase();

        return NoContent();
    }

    /// <summary>
    /// 注册用户
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/user/register
    ///     {
    ///         "id": "blahblah",
    ///         "password": "pwd123",
    ///         "userName": "Blah blah",
    ///         "email": "blah@example.com",
    ///         "mobile": "18012345678"
    ///     }
    /// 
    /// </remarks>
    /// <param name="user">注册参数</param>
    /// <returns>成功注册则返回已注册的用户对象</returns>
    /// <response code="200">返回已注册的用户对象</response>
    /// <response code="500">用户重复或其他服务器错误</response>
    [Route("register", Name = "register")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("application/json")]
    public ActionResult<UserItem> Register([FromBody] UserParameter user)
    {
#if DEBUG
        logger?.LogInformation("user register, {user}", user);
#endif
        if (database.Users.Any(u => u.UserId == user.Id))
        {
            logger?.LogWarning("duplicate user \"{id}\"", user.Id);
            return Problem("duplicateUser", "api/user/register");
        }

        var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var item = new UserItem
        {
            UserId = user.Id,
            Password = HashPassword(user.Password, user.Id),
            Level = UserCommon,
            RegisterDateUnixTime = now,
            ActiveDateUnixTime = now,
            Name = user.UserName,
            Email = user.Email,
            Mobile = user.Mobile
        };
        database.Users.Add(item);
        SaveDatabase();

        return Ok(item);
    }

    /// <summary>
    /// 查询当前会话关联的用户
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/user/profile
    ///     Authorization: authorization id
    /// 
    /// </remarks>
    /// <returns>会话有效则返回关联的用户对象</returns>
    /// <response code="200">返回关联的用户对象</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("profile", Name = "getProfile")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<UserItem> Profile()
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        // update last active time
        SaveDatabase();

        return Ok(user);
    }

    /// <summary>
    /// 修改用户
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     PUT /api/user/update
    ///     Authorization: authorization id
    ///     {
    ///         "userName": "Blah blah",
    ///         "email": "blah@example.com",
    ///         "mobile": "18012345678"
    ///     }
    /// 
    /// </remarks>
    /// <param name="update">修改参数</param>
    /// <returns>修改成功则返回已修改的用户对象</returns>
    /// <response code="200">返回已修改的用户对象</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    /// <response code="413">提交正文过大</response>
    [Route("update", Name = "updateProfile")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPut]
    [Consumes("application/json")]
    public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
    {
#if DEBUG
        logger?.LogInformation("user update, {user}", update);
#endif
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        user.Name = update.UserName;
        user.Email = update.Email;
        user.Mobile = update.Mobile;
        SaveDatabase();

        return Ok(user);
    }

    /// <summary>
    /// 修改用户头像
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     PUT /api/user/update_avatar
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     avatar: IFormFile
    /// 
    /// </remarks>
    /// <param name="avatar">用户头像</param>
    /// <returns>修改成功则返回 HTTP 204</returns>
    /// <response code="204">修改成功</response>
    /// <response code="400">头像内容格式非法</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    /// <response code="413">提交正文过大</response>
    [Route("update_avatar", Name = "updateAvatar")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPut]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public ActionResult<UserItem> UploadAvatar([Required] IFormFile avatar)
    {
#if DEBUG
        logger?.LogInformation("user update avatar, {avatar}", avatar);
#endif
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        if (avatar.Length > 0)
        {
            var file = WrapFormFile(avatar);
            if (file == null)
            {
                return BadRequest();
            }
            user.Avatar = file.Content;
        }
        SaveDatabase();

        return NoContent();
    }

    /// <summary>
    /// 移除用户头像
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     DELETE /api/user/remove_avatar
    ///     Authorization: authorization id
    /// 
    /// </remarks>
    /// <returns>移除成功则返回修改的数据库行数</returns>
    /// <response code="200">修改的数据库行数</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("remove_avatar", Name = "removeAvatar")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpDelete]
    public ActionResult RemoveAvatar()
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var count = RemoveUserAvatar(user.Id);
        SaveDatabase();

        return Ok(count);
    }

    //#if DEBUG
    /// <summary>
    /// #DEBUG 获取所有用户
    /// </summary>
    /// <returns></returns>
    [Route("debug_list", Name = "debug_list")]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<UserItem[]> GetUsers()
    {
        return Ok(database.Users.ToArray());
    }

    /// <summary>
    /// #DEBUG 获取所有 token
    /// </summary>
    /// <returns></returns>
    [Route("debug_tokens", Name = "debug_tokens")]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<TokenItem[]> GetTokens()
    {
        return Ok(database.Tokens.ToArray());
    }
    //#endif
}
