using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Modules.User.Application;
using Modules.User.Application.Commands;
using Modules.User.Application.Queries;
using Modules.User.WebApi.Models;
using System.Net;
using Modules.User.Api.Models;
using Modules.User.Application.Commands.User;
using Modules.User.Application.Commands.User.Access;
using Modules.User.Application.Commands.User.Ban;
using Modules.User.Application.Commands.User.PersonalData;
using Modules.User.Application.Commands.User.Session;
using Modules.User.Application.Queries.User;
using Modules.User.WebApi.Models.User.Access;

namespace Modules.User.WebApi.Controllers;

[ApiController]
[Route("[controller]")]
[ProducesResponseType(400, StatusCode = 400, Type = typeof(ProblemDetails))]
[ProducesResponseType(401, StatusCode = 401, Type = typeof(UnauthorizedResult))]
[Authorize]
public class UserController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<UserController> _logger;


    public UserController(IMediator mediator, ILogger<UserController> logger)
    {
        _mediator = mediator;
        _logger = logger;
    }
    
    [HttpPost("Login")]
    [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(AuthenticationResultModel))]
    [AllowAnonymous]
    public async Task<IActionResult> Login(LoginModel model)
    {
        var loginResult = await _mediator.Send(new LoginCommand
            {
                Email = model.Login,
                Password = model.Password,
                IsAdmin = false,

                Ip = model.Ip,

                Response = Response,
                //CookiePath = "Account/Refresh",
                CookiePath = "/",
            });
        return Ok(new AuthenticationResultModel
        {
            AccessToken = loginResult.AccessToken,
            SessionExpireDate = loginResult.SessionExpireDate.ToString("O"),
        });
    }
    
    [HttpPost("Refresh")]
    [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(AuthenticationResultModel))]
    [AllowAnonymous]
    public async Task<IActionResult> Refresh(RefreshModel model)
    {
        var refreshResult = await _mediator.Send(new RefreshTokensCommand
            {
                Ip = model.Ip,

                Response = Response,
                //CookiePath = "Account/Refresh",
                CookiePath = "/",
        });
        //if (tokens != null)
        //{
        //    return Ok(tokens.Value.AccessToken);
        //}
        //else
        //{
        //    return Unauthorized();
        //}
        return refreshResult == null ? Unauthorized() : Ok(new AuthenticationResultModel
        {
            AccessToken = refreshResult.AccessToken,
            SessionExpireDate = refreshResult.SessionExpireDate.ToString("O"),
        });

        //return tokens == null ? Unauthorized() : Ok(new TokensModel
        //{
        //    AccessToken = tokens.Value.AccessToken,
        //    RefreshToken = tokens.Value.RefreshToken,
        //});
    }


    [HttpPost("Register")]
    [AllowAnonymous]
    public async Task Register(RegisterModel model)
    {
        var token = await _mediator.Send(new CreateUserCommand
            {
                Nickname = model.Nickname,
                Email = model.Login,
                Password = model.Password,
                //IsAdmin = false,
            });
    }

    #region Sessions

    [HttpGet("sessions")]
    [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(List<Session>))]
    public async Task<IActionResult> GetSessions()
    {
        var sessions = await _mediator.Send(new GetAccountSessionsQuery());
        return Ok(sessions.Select(q => new Session
        {
            Id = q.Id,
            IsCurrentSession = q.IsCurrentSession,
            Country = q.ClientInfo.Location.Country,
            Region = q.ClientInfo.Location.Region,
            OnlineStatus = q.OnlineStatus switch
            {
                Application.Models.UserOnlineStatus.Online => UserOnlineStatus.Online,
                Application.Models.UserOnlineStatus.Away => UserOnlineStatus.Away,
                Application.Models.UserOnlineStatus.Offline => UserOnlineStatus.Offline,
                _ => UserOnlineStatus.Unknown,
            },
            LastOnline = q.LastOnline,
            //Latitude = q.ClientInfo.Location.Latitude,
            //Longitude = q.ClientInfo.Location.Longutude,
            UserAgent = q.ClientInfo.UserAgent,
            ExpiredDate = q.ExpiredDate,
        }));
    }

    [HttpDelete("sessions/{id:guid}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.Unauthorized)]
    public async Task<IActionResult> DeleteSession(Guid id)
    {
        try
        {
            await _mediator.Send(new DeleteSessionCommand
            {
                SessionId = id,
            });
            return Ok();
        }
        catch (UnauthorizedAccessException ex)
        {
            //TODO: log
            return Unauthorized();
        }
    }

    [HttpDelete("sessions/current")]
    public async Task<IActionResult> DeleteCurrentSession()
    {
        try
        {
            await _mediator.Send(new DeleteCurrentSessionCommand());
            return Ok();
        }
        catch (UnauthorizedAccessException ex)
        {
            //TODO: log
            return Unauthorized();
        }
    }

    [HttpDelete("sessions/all")]
    public async Task<IActionResult> DeleteAllSessions()
    {
        try
        {
            await _mediator.Send(new DeleteAllSessionsCommand());
            return Ok();
        }
        catch (UnauthorizedAccessException ex)
        {
            //TODO: log
            return Unauthorized();
        }
    }

    #endregion

    #region Access

    [HttpPost("ban")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> BanUser(AccountBanModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new BanAccountCommand
        {
            AccountId = model.AccountId,
            Reason = model.Reason,
            ExpiresAtUtc = model.ExpiresAtUtc,
        }, cancellationToken);
        return Ok();
    }

    [HttpPost("unban")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> UnbanUser(AccountUnbanModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new UnbanAccountCommand()
        {
            AccountId = model.AccountId,
            Reason = model.Reason,
        }, cancellationToken);
        return Ok();
    }


    [HttpPost("{id:guid}/role/assign")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> AssignRoleToUser(Guid id, RoleAssignModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new RoleAssignToUserCommand
        {
            AccountId = id,
            RoleId = model.RoleId,
            AssignReason = model.AssignReason,
        }, cancellationToken);
        return Ok();
    }

    [HttpPost("{id:guid}/role/revoke")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> RevokeRoleFromUser(Guid id, RoleRevokeModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new RoleRevokeFromUserCommand()
        {
            AccountId = id,
            RoleId = model.RoleId,
            RevokeReason = model.RevokeReason,
        }, cancellationToken);
        return Ok();
    }


    [HttpPost("{id:guid}/permission/assign")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> GrantPermissionToUser(Guid id, PermissionGrantModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new PermissionGrantToUserCommand()
        {
            AccountId = id,
            PermissionId = model.PermissionId,
            GrantReason = model.GrantReason,
        }, cancellationToken);
        return Ok();
    }

    [HttpPost("{id:guid}/permission/revoke")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> RevokePermissionFromUser(Guid id, PermissionRevokeModel model, CancellationToken cancellationToken)
    {
        await _mediator.Send(new PermissionRevokeFromUserCommand()
        {
            AccountId = id,
            PermissionId = model.PermissionId,
            RevokeReason = model.RevokeReason,
        }, cancellationToken);
        return Ok();
    }

    #endregion
    
    [HttpGet("list")]
    public async Task<PagedData<UserInfoShort>> GetUsers(bool? hasAvatar, string? nickName,
        DateTimeOffset? lastOnlineFrom = null,  DateTimeOffset? lastOnlineTo = null,
        bool? isBanned = null, int page = 1,  int itemsOnPage = 10)
    {
        var usersPaged = await _mediator.Send(new GetUserListQuery()
        {
            HasAvatar = hasAvatar,
            NickName = nickName,
            LastOnlineFrom = lastOnlineFrom,
            LastOnlineTo = lastOnlineTo,
            IsBanned = isBanned,
            
            ItemsOnPage = itemsOnPage,
            Page = page,
        });
        
        return new PagedData<UserInfoShort>
        {
            PagesCount = usersPaged.PagesCount,
            Page = usersPaged.Page,
            ItemsOnPage = usersPaged.ItemsOnPage,
            ItemsCount = usersPaged.ItemsCount,
            Items = usersPaged.Items.Select(q => new UserInfoShort
            {
                Id = q.Id,
                NickName = q.NickName,
                FirstName = q.FirstName,
                Patronymic = q.Patronymic,
                LastName = q.LastName,
                Email = q.Email,
                HasAvatar = q.HasAvatar,
                BirthDate = q.BirthDate,
                LanguageId = q.LanguageId,
                
                OnlineStatus = q.OnlineStatus switch
                {
                    Application.Models.UserOnlineStatus.Online => UserOnlineStatus.Online,
                    Application.Models.UserOnlineStatus.Away => UserOnlineStatus.Away,
                    Application.Models.UserOnlineStatus.Offline => UserOnlineStatus.Offline,
                    _ => UserOnlineStatus.Unknown,
                },
                LastOnline = q.LastOnline,
                
                BanStatus = new BanStatus(q.BanStatus.IsBanned, q.BanStatus.BannedUntil),
                
                // Roles = q.roles
            })
        };
    }
    
    // [HttpGet("UserAvatar")]
    // [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))]
    // [AllowAnonymous]
    // public async Task<IActionResult> GetUserAvatar(string userId)
    // {
    //     if (!string.IsNullOrEmpty(objectId))
    //     {
    //         var media = await _mediator.Send(new GetUserAvatarQuery
    //         {
    //             ObjectId = objectId,
    //         });
    //         if (media == null)
    //         {
    //             return NotFound();
    //         }
    //         else
    //         {
    //             return new FileStreamResult(media.Data, media.ContentType);
    //         }
    //     }
    //     else
    //     {
    //         return NotFound();
    //     }
    // }

    // [HttpGet]
    // [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(UserInfo))]
    // public async Task<IActionResult> GetUser()
    // {
    //     var user = await _userContext.GetUserInfo();
    //     if (user?.Id == null) return NotFound();
    //     return Ok(new UserInfo
    //     {
    //         Id = user.Id,
    //         AccountId = user.AccountId,
    //         SessionId = user.SessionId,
    //         NickName = user.NickName,
    //         FirstName = user.FirstName,
    //         Patronymic = user.Patronymic,
    //         LastName = user.LastName,
    //         BirthDate = user.BirthDate,
    //         LanguageId = user.LanguageId,
    //         Email = user.Email,
    //         IsAuthenticated = user.IsAuthenticated,
    //         OnlineStatus = UserOnlineStatus.Online,
    //     });
    // }

    [HttpGet("Id")]
    [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(UserInfo))]
    public async Task<IActionResult> GetUserId(CancellationToken cancellationToken)
    {
        return Ok(await _mediator.Send(new GetUserIdQuery(), cancellationToken));
    }

    [AllowAnonymous]
    [HttpGet("Avatar")]
    [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))]
    public async Task<IActionResult> GetAvatar([FromQuery]Guid userId)
    {
        var avatar = await _mediator.Send(new GetUserAvatarQuery { UserId = userId });
        if (avatar == null)
        {
            return NotFound();
        }
        else
        {
            return new FileStreamResult(avatar.Data, avatar.ContentType);
        }
    }

    [HttpPost("SetAvatar")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<IActionResult> SetAvatar(IFormFile file)
    {
        if (file.Length == 0) return BadRequest("File is empty.");
        await using var stream = file.OpenReadStream();
        await _mediator.Send(new SetUserAvatarCommand
        {
            ContentType = file.ContentType,
            Extension = Path.GetExtension(file.FileName),
            Data = stream,
        });
        return Ok();
    }

    [HttpPost("DeleteAvatar")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<IActionResult> DeleteAvatar()
    {
        await _mediator.Send(new DeleteUserAvatarCommand());
        return Ok();
    }

    /*
    public string? NickName { get; set; } = default!;
    public string? FirstName { get; set; }
    public string? Patronymic { get; set; }
    public string? LastName { get; set; }
    public Guid? LanguageId { get; set; }
    */


    [HttpPost("SetUserInformation")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<IActionResult> SetUserInformation(UserInformationEditModel model)
    {
        await _mediator.Send(new SetUserNameCommand
        {
            FirstName = model.FirstName,
            Patronymic = model.Patronymic,
            LastName = model.LastName,
        });
        return Ok();
    }

    [HttpPost("SetBirthDate")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<IActionResult> SetBirthDate(BirthDateEditModel model)
    {
        await _mediator.Send(new SetUserBirthDateCommand
        {
            BirthDate = model.BirthDate,
        });
        return Ok();
    }


    [HttpPost("SetLanguage")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    public async Task<IActionResult> SetLanguage(LanguageEditModel model)
    {
        await _mediator.Send(new SetUserLanguageCommand
        {
            LanguageId = model.LanguageId,
        });
        return Ok();
    }
}
