using Backend.Common;
using Backend.Contracts;
using Backend.Data;
using Backend.Domain;
using Backend.Security;
using Backend.Services;
using Microsoft.EntityFrameworkCore;

namespace Backend.Endpoints;

public static partial class AuthEndpoints
{
    private const string TeamSessionPrefix = "team:";
    private const int MinTeamPasswordLength = 10;

    private static async Task<IResult> TeamLogin(
        HttpContext context,
        TeamLoginRequest request,
        AwardsDbContext db,
        IUserSessionService userSessionService)
    {
        var login = NormalizeTeamLogin(request.Login);
        var password = request.Password ?? string.Empty;

        if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
        {
            return Results.Unauthorized();
        }

        var member = await db.TeamMembers.FirstOrDefaultAsync(item => item.Login == login, context.RequestAborted);
        if (member is null
            || !member.IsActive
            || !DemoCredentialHasher.VerifyPassword(password, member.PasswordHash, member.PasswordSalt))
        {
            return Results.Unauthorized();
        }

        member.LastLoginAt = DateTimeOffset.UtcNow;
        var session = await userSessionService.CreateSessionAsync(
            BuildTeamSessionId(member.Login),
            member.DisplayName,
            member.Role,
            RequestMetadataReader.Read(context),
            context.RequestAborted);

        await db.SaveChangesAsync(context.RequestAborted);
        return Results.Ok(await ToAuthSessionDtoAsync(db, userSessionService, session, member.MustChangePassword, context.RequestAborted));
    }

    private static async Task<IResult> ChangePassword(
        HttpContext context,
        ChangePasswordRequest request,
        AwardsDbContext db,
        IUserSessionService userSessionService)
    {
        var session = await userSessionService.ResolveSessionAsync(context, context.RequestAborted);
        if (session is null)
        {
            return Results.Unauthorized();
        }

        var member = await FindTeamMemberForSessionAsync(db, session, context.RequestAborted);
        if (member is null || !member.IsActive)
        {
            return Results.BadRequest(new { message = "Dieser Account nutzt keinen aktiven Team-Login." });
        }

        var currentPassword = request.CurrentPassword ?? string.Empty;
        var newPassword = request.NewPassword?.Trim() ?? string.Empty;
        if (!DemoCredentialHasher.VerifyPassword(currentPassword, member.PasswordHash, member.PasswordSalt))
        {
            return Results.BadRequest(new { message = "Das aktuelle Passwort stimmt nicht." });
        }

        if (newPassword.Length < MinTeamPasswordLength)
        {
            return Results.BadRequest(new { message = $"Das neue Passwort muss mindestens {MinTeamPasswordLength} Zeichen lang sein." });
        }

        if (DemoCredentialHasher.FixedTimePlainTextEquals(currentPassword, newPassword))
        {
            return Results.BadRequest(new { message = "Das neue Passwort muss sich vom aktuellen Passwort unterscheiden." });
        }

        var credentials = DemoCredentialHasher.HashPassword(newPassword);
        member.PasswordHash = credentials.Hash;
        member.PasswordSalt = credentials.Salt;
        member.MustChangePassword = false;
        member.UpdatedAt = DateTimeOffset.UtcNow;
        member.UpdatedByTwitchId = session.TwitchUserId;
        session.DisplayName = member.DisplayName;
        session.Role = AdminRoles.Normalize(member.Role);

        await db.SaveChangesAsync(context.RequestAborted);
        return Results.Ok(await ToAuthSessionDtoAsync(db, userSessionService, session, false, context.RequestAborted));
    }

    private static string BuildTeamSessionId(string login) =>
        $"{TeamSessionPrefix}{login}";

    internal static string ReadTeamLoginFromSession(string twitchUserId) =>
        twitchUserId.StartsWith(TeamSessionPrefix, StringComparison.OrdinalIgnoreCase)
            ? NormalizeTeamLogin(twitchUserId[TeamSessionPrefix.Length..])
            : string.Empty;

    internal static async Task<TeamMember?> FindTeamMemberForSessionAsync(
        AwardsDbContext db,
        UserSession session,
        CancellationToken cancellationToken = default)
    {
        var login = ReadTeamLoginFromSession(session.TwitchUserId);
        if (!string.IsNullOrWhiteSpace(login))
        {
            return await db.TeamMembers.FirstOrDefaultAsync(item => item.Login == login, cancellationToken);
        }

        var twitchUserId = NormalizeTwitchUserId(session.TwitchUserId);
        return string.IsNullOrWhiteSpace(twitchUserId)
            ? null
            : await db.TeamMembers.FirstOrDefaultAsync(item => item.BoundTwitchUserId == twitchUserId, cancellationToken);
    }

    private static string NormalizeTeamLogin(string value) =>
        value.Trim().TrimStart('@').ToLowerInvariant();

    private static string NormalizeTwitchUserId(string? value) =>
        (value ?? string.Empty).Trim().TrimStart('@').ToLowerInvariant();
}
