using System.Diagnostics;
using FastBlog.Core.Models;
using FastBlog.Core.Models.Users;
using FastBlog.Core.Services;
using FastBlog.Web.Middlewares;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;

namespace FastBlog.Web.Controllers;

[EnableRateLimiting("fixed")]
public sealed class UsersController(UserService userService, SessionService sessionService) : Controller
{
    public UserService UserService { get; } = userService;

    public sealed record LoginRequest(string? Login, string? Password);

    public sealed record CreateRequest(string Login, string Password);

    public sealed record LoginModel(string? Login, string? Password, List<string> Errors);

    [HttpGet("/users/login")]
    public IActionResult LogIn()
    {
        if (HttpContext.IsUser())
            return Redirect("/");

        return View(new LoginModel(null, null, []));
    }

    [Htmx]
    [EnableRateLimiting("fixed")]
    [HttpPost("/users/login")]
    public async Task<IActionResult> LogIn(LoginRequest request)
    {
        if (HttpContext.IsUser())
            return Redirect("/");

        List<string> errors = [];
        // validation
        if (string.IsNullOrWhiteSpace(request.Login)
            || request.Login.Length < 3 || request.Login.Length > 24)
        {
            errors.Add("Login is required and should be 3 to 24 symbols");
        }

        if (string.IsNullOrWhiteSpace(request.Password)
            || request.Password.Length < 3 || request.Password.Length > 64)
        {
            errors.Add("Password is required and should be 3 to 64 symbols");
        }

        if (errors.Count > 0)
        {
            return PartialView(new LoginModel(request.Login, request.Password, errors));
        }

        Debug.Assert(request.Login is not null);
        Debug.Assert(request.Password is not null);

        // execute
        var session = await sessionService.Authenticate(request.Login, request.Password);

        if (session is null)
        {
            errors.Add("Unknown user or incorrect password");
            return PartialView(new LoginModel(request.Login, request.Password, errors));
        }

        UpdateCookie(HttpContext, "session-t", session.Token);

        HttpContext.Response.Headers["HX-Redirect"] = "/";
        return Ok();
    }

    [SimpleAuth]
    [Htmx]
    [HttpDelete("/users/logout")]
    public async ValueTask<IActionResult> LogOut()
    {
        if (!HttpContext.Request.Cookies.TryGetValue("session-t", out var sessionT))
            return Redirect("/");

        await sessionService.Deactivate(sessionT);
        HttpContext.Response.Cookies.Delete("session-t");
        HttpContext.Response.Headers["HX-Redirect"] = "/";
        return Ok();
    }


    [HttpGet("/users")]
    [SimpleAuth]
    public async Task<IActionResult> Page([FromQuery] int page = 0)
    {
        var users = await userService.GetUsers(new PagedRequest(25, page));
        return View("Index", users);
    }
    
    [HttpDelete("/users/{id:int}")]
    [SimpleAuth]
    public async Task<IActionResult> Delete(int id)
    {
        _ = await userService.DeleteUser(id);
        HttpContext.Response.Headers["HX-Redirect"] = "/users";
        return Ok();
    }

    public sealed record UserViewModel(User User, bool Success = true, string? Message = null);

    [SimpleAuth]
    [HttpGet("/users/me")]
    public IActionResult GetCurrent()
    {
        if (!HttpContext.TryGetUser(out var user))
        {
            return Redirect("/users/login");
        }

        return View("Profile", new UserViewModel(user));
    }

    [SimpleAuth]
    [HttpPost("/users")]
    public async Task<IActionResult> Create(CreateRequest createRequest)
    {
        if (await userService.GetByName(createRequest.Login) is not null)
        {
            var u = await userService.GetUsers(new PagedRequest(25, 0));
            var v = View("Index", u);
            v.ViewData.Add("error", "User already exists");
            return v;
        }
        var user = await userService.CreateUser(new NewUser
        {
            Password = createRequest.Password,
            Username = createRequest.Login
        });
        var users = await userService.GetUsers(new PagedRequest(25, 0));
        return View("Index", users);
    }

    [SimpleAuth]
    [HttpGet("/users/{id:int}")]
    public async Task<IActionResult> Get(int id)
    {
        var user = await userService.Get(id);
        if (user is null)
            return Redirect("~/");
        return View("Profile", new UserViewModel(user));
    }


    public sealed record ChangePasswordModel(string OldPassword, string NewPassword);

    [SimpleAuth]
    [HttpPost("/users/{id:int}/change-password")]
    public async Task<IActionResult> ChangePassword(int id, ChangePasswordModel model)
    {
        var user = await userService.Get(id);
        if (user is null)
            return Redirect("~/");
        var result = await userService.ChangePassword(id, model.OldPassword, model.NewPassword);
        return View("Profile", new UserViewModel(user, result.Success, result.Message));
    }

    [SimpleAuth]
    [HttpPost("/users/{id:int}/set-password")]
    public async Task<IActionResult> SetPassword(int id, string newPassword)
    {
        if (!HttpContext.TryGetUser(out var currentUser))
        {
            return Redirect("~/");
        }

        if (currentUser.Id == id)
        {
            return View("Profile", new UserViewModel(currentUser, false, "Provide the old password too"));
        }

        var user = await userService.Get(id);
        if (user is null)
            return Redirect("~/");
        var result = await userService.SetPassword(id, newPassword);
        return View("Profile", new UserViewModel(user, result.Success, result.Message));
    }

    private static void UpdateCookie(HttpContext context, string key, string value)
    {
        context.Response.Cookies.Delete(key);
        context.Response.Cookies.Append(key, value, new CookieOptions()
        {
            HttpOnly = true,
            SameSite = SameSiteMode.Strict,
            Secure = true,
            IsEssential = true
        });
    }
}