using Indotalent.ConfigFrontEnd.Service;
using Indotalent.Data.Entities;
using Indotalent.Features.Account.ForgotPassword.Cqrs;
using Indotalent.Features.Account.Login.Cqrs;
using Indotalent.Features.Account.Register.Cqrs;
using Indotalent.Features.Account.ResetPassword.Cqrs;
using Indotalent.Infrastructure.Authentication.Identity;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using System.Security.Claims;

namespace Indotalent.Features.Account;

public static class AccountEndPoint
{
    public static IEndpointConventionBuilder MapAccountEndpoints(this IEndpointRouteBuilder endpoints)
    {
        var group = endpoints.MapGroup("/account").WithTags("Account");

        group.MapPost("/signin-sso", async ([FromQuery] string email, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IOptions<JwtSettingsModel> jwtOptions, HttpContext context) =>
        {
            if (string.IsNullOrWhiteSpace(email)) return Results.Json(new { status = 401, title = "Email is required" }, statusCode: 401);

            var user = await userManager.FindByEmailAsync(email);

            if (user == null || !user.IsActive)
            {
                return Results.Json(new { status = 401, title = "Account not found or inactive" }, statusCode: 401);
            }

            var jwtSettings = jwtOptions.Value;
            var token = JwtService.GenerateNewJwt(user, jwtSettings);
            var refreshToken = Guid.NewGuid().ToString().Replace("-", "");

            user.RefreshToken = refreshToken;
            user.LastLoginAt = DateTime.Now;
            await userManager.UpdateAsync(user);

            await signInManager.SignInAsync(user, isPersistent: true);

            context.Response.Cookies.Append("X-Auth-Token", token, new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict,
                Expires = DateTimeOffset.UtcNow.AddDays(1)
            });

            context.Response.Cookies.Append("X-Refresh-Token", refreshToken, new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict,
                Expires = DateTimeOffset.UtcNow.AddDays(7)
            });

            return Results.Ok(new { status = 200, title = "SSO Login successful" });
        });

        group.MapGet("/active-user-exists", async ([FromQuery] string email, UserManager<ApplicationUser> userManager) =>
        {
            if (string.IsNullOrWhiteSpace(email))
            {
                return Results.Ok(new { exists = false, isActive = false });
            }

            var user = await userManager.FindByEmailAsync(email);

            if (user == null)
            {
                return Results.Ok(new { exists = false, isActive = false });
            }

            return Results.Ok(new { exists = true, isActive = user.IsActive });
        });

        group.MapPost("/signin", async ([FromBody] LoginRequest request, IMediator mediator, HttpContext context, UserManager<ApplicationUser> userManager) =>
        {
            var command = new LoginCommand(request);
            var response = await mediator.Send(command);

            if (response.Succeeded)
            {
                var user = await userManager.FindByEmailAsync(request.Email);

                if (user != null)
                {
                    context.Response.Cookies.Append("X-Auth-Token", response.Token!, new CookieOptions
                    {
                        HttpOnly = true,
                        Secure = true,
                        SameSite = SameSiteMode.Strict,
                        Expires = DateTimeOffset.UtcNow.AddDays(1)
                    });

                    context.Response.Cookies.Append("X-Refresh-Token", user.RefreshToken!, new CookieOptions
                    {
                        HttpOnly = true,
                        Secure = true,
                        SameSite = SameSiteMode.Strict,
                        Expires = DateTimeOffset.UtcNow.AddDays(7)
                    });
                }

                return Results.Ok(new { status = 200, title = response.Message });
            }
            return Results.Json(new { status = 401, title = response.Message }, statusCode: 401);
        });



        group.MapPost("/refresh-token", async (HttpContext context) =>
        {
            var userManager = context.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
            var jwtSettings = context.RequestServices.GetRequiredService<IOptions<JwtSettingsModel>>().Value;

            var refreshToken = context.Request.Headers["X-Refresh-Token"].ToString();

            if (string.IsNullOrEmpty(refreshToken)) return Results.Unauthorized();

            var user = await userManager.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);

            if (user == null || !user.IsActive) return Results.Unauthorized();

            var newToken = JwtService.GenerateNewJwt(user, jwtSettings);

            context.Response.Cookies.Append("X-Auth-Token", newToken, new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict,
                Expires = DateTimeOffset.UtcNow.AddDays(1)
            });

            return Results.Ok(new LoginResponse { Succeeded = true, Token = newToken });
        });

        group.MapPost("/signup", async ([FromBody] CreateUserRequest request, IMediator mediator) =>
        {
            try
            {
                var command = new CreateUserCommand(request);
                var response = await mediator.Send(command);
                return Results.Ok(response);
            }
            catch (Exception ex)
            {
                return Results.BadRequest(new { errors = new[] { ex.Message } });
            }
        });

        group.MapPost("/signout", async (
            SignInManager<ApplicationUser> signInManager,
            UserManager<ApplicationUser> userManager,
            HttpContext context) =>
        {
            var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);

            if (!string.IsNullOrEmpty(userId))
            {
                var user = await userManager.FindByIdAsync(userId);
                if (user != null)
                {
                    user.RefreshToken = null;
                    await userManager.UpdateAsync(user);
                }
            }

            await signInManager.SignOutAsync();

            context.Response.Cookies.Delete("X-Auth-Token");
            context.Response.Cookies.Delete("X-Refresh-Token");

            return Results.Ok(new { status = 200, title = "Successfully signed out" });

        });

        group.MapPost("/forgot-password", async ([FromBody] ForgotPasswordRequest request, IMediator mediator) =>
        {
            try
            {
                var command = new ForgotPasswordCommand(request);
                var response = await mediator.Send(command);
                return Results.Ok(response);
            }
            catch (Exception ex)
            {
                return Results.BadRequest(new { errors = new[] { ex.Message } });
            }
        });

        group.MapPost("/logout", async (SignInManager<ApplicationUser> signInManager) =>
        {
            await signInManager.SignOutAsync();
            return Results.Ok(new { title = "Success user logout" });
        }).RequireAuthorization();

        group.MapPost("/reset-password", async ([FromBody] ResetPasswordRequest request, IMediator mediator) =>
        {
            try
            {
                var command = new ResetPasswordCommand(request);
                var response = await mediator.Send(command);
                return Results.Ok(response);
            }
            catch (Exception ex)
            {
                return Results.BadRequest(new { errors = new[] { ex.Message } });
            }
        });

        return group;
    }
}