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 static async Task<IResult> DemoLogin(
        HttpContext context,
        IHostEnvironment environment,
        AwardsDbContext db,
        IConfiguration configuration,
        DemoLoginRequest request,
        IUserSessionService userSessionService,
        IRiskFlagService riskFlagService,
        IRiskRuleService riskRuleService)
    {
        var login = request.Login?.Trim() ?? request.Email?.Trim() ?? string.Empty;
        var password = request.Password ?? string.Empty;
        var settings = await db.SiteSettings.AsNoTracking().FirstOrDefaultAsync(item => item.Id == 1, context.RequestAborted);
        var databaseDemoConfigured = settings is not null
            && settings.DemoLoginManagedByDatabase;

        string twitchUserId;
        string displayName;
        bool credentialsMatch;
        var fallbackConfiguredLogin = ReadDemoLoginIdentifier(configuration);
        var fallbackConfiguredEmail = ReadDemoSetting(configuration, "Email", "VTSA_DEMO_ADMIN_EMAIL");
        var fallbackConfiguredPassword = ReadDemoSetting(configuration, "Password", "VTSA_DEMO_ADMIN_PASSWORD");
        var fallbackConfiguredTwitchUserId = ReadDemoSetting(configuration, "TwitchUserId", "VTSA_DEMO_ADMIN_TWITCH_ID");
        var fallbackConfiguredDisplayName = ReadDemoSetting(configuration, "DisplayName", "VTSA_DEMO_ADMIN_DISPLAY_NAME");

        if (databaseDemoConfigured && settings is not null)
        {
            if (!settings.DemoLoginEnabled)
            {
                return Results.NotFound();
            }

            if (!HasDatabaseDemoCredentials(settings)
                || string.IsNullOrWhiteSpace(settings.DemoLoginTwitchUserId)
                || string.IsNullOrWhiteSpace(settings.DemoLoginDisplayName))
            {
                return Results.Json(
                    new { message = "Demo login is not fully configured." },
                    statusCode: StatusCodes.Status503ServiceUnavailable);
            }

            credentialsMatch = LoginMatchesIdentifier(
                    login,
                    settings.DemoLoginEmail,
                    settings.DemoLoginTwitchUserId,
                    settings.DemoLoginDisplayName)
                && DemoCredentialHasher.VerifyPassword(password, settings.DemoLoginPasswordHash, settings.DemoLoginPasswordSalt);
            twitchUserId = settings.DemoLoginTwitchUserId.Trim();
            displayName = settings.DemoLoginDisplayName.Trim();

            if (!credentialsMatch
                && environment.IsDevelopment()
                && IsDemoLoginEnabled(configuration)
                && !string.IsNullOrWhiteSpace(fallbackConfiguredLogin)
                && !string.IsNullOrWhiteSpace(fallbackConfiguredPassword)
                && !string.IsNullOrWhiteSpace(fallbackConfiguredTwitchUserId)
                && !string.IsNullOrWhiteSpace(fallbackConfiguredDisplayName))
            {
                credentialsMatch = LoginMatchesIdentifier(
                        login,
                        fallbackConfiguredLogin,
                        fallbackConfiguredEmail,
                        fallbackConfiguredTwitchUserId,
                        fallbackConfiguredDisplayName)
                    && DemoCredentialHasher.FixedTimePlainTextEquals(password, fallbackConfiguredPassword);
                twitchUserId = fallbackConfiguredTwitchUserId.Trim();
                displayName = fallbackConfiguredDisplayName.Trim();
            }
        }
        else
        {
            if (!IsDemoLoginEnabled(configuration))
            {
                return Results.NotFound();
            }

            if (string.IsNullOrWhiteSpace(fallbackConfiguredLogin)
                || string.IsNullOrWhiteSpace(fallbackConfiguredPassword)
                || string.IsNullOrWhiteSpace(fallbackConfiguredTwitchUserId)
                || string.IsNullOrWhiteSpace(fallbackConfiguredDisplayName))
            {
                return Results.Json(
                    new { message = "Demo login is not fully configured." },
                    statusCode: StatusCodes.Status503ServiceUnavailable);
            }

            credentialsMatch = LoginMatchesIdentifier(
                    login,
                    fallbackConfiguredLogin,
                    fallbackConfiguredEmail,
                    fallbackConfiguredTwitchUserId,
                    fallbackConfiguredDisplayName)
                && DemoCredentialHasher.FixedTimePlainTextEquals(password, fallbackConfiguredPassword);
            twitchUserId = fallbackConfiguredTwitchUserId.Trim();
            displayName = fallbackConfiguredDisplayName.Trim();
        }

        if (!credentialsMatch)
        {
            return Results.Unauthorized();
        }

        var requestMetadata = RequestMetadataReader.Read(context);
        var session = await userSessionService.CreateSessionAsync(
            twitchUserId,
            displayName,
            AdminRoles.Owner,
            requestMetadata,
            context.RequestAborted);

        var rapidDemoLoginRule = await riskRuleService.GetRuleAsync("rapid_demo_login_ip", context.RequestAborted);
        var recentSessionsFromIp = await userSessionService.CountRecentSessionsFromIpAsync(
            requestMetadata.ClientIp,
            DateTimeOffset.UtcNow.AddMinutes(-rapidDemoLoginRule.WindowMinutes),
            context.RequestAborted);

        if (rapidDemoLoginRule.Enabled && recentSessionsFromIp >= rapidDemoLoginRule.Threshold)
        {
            await riskFlagService.AddIfMissingAsync(
                null,
                session.TwitchUserId,
                "login",
                "rapid_demo_login_ip",
                rapidDemoLoginRule.Severity,
                "Mehrere Demo-Admin-Sessions wurden in kurzer Zeit von derselben IP erzeugt.",
                requestMetadata,
                new
                {
                    recentSessionsFromIp,
                    threshold = rapidDemoLoginRule.Threshold,
                    windowMinutes = rapidDemoLoginRule.WindowMinutes,
                    entityLinks = new[]
                    {
                        new
                        {
                            label = "Audit-Log öffnen",
                            entityType = "session",
                            entityId = session.TwitchUserId,
                            to = $"/admin/users-logs?query={Uri.EscapeDataString(session.TwitchUserId)}",
                        },
                    },
                },
                context.RequestAborted);
            await db.SaveChangesAsync(context.RequestAborted);
        }

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

    private static bool IsDemoLoginEnabled(IConfiguration configuration)
    {
        var rawValue = configuration["VTSA_DEMO_LOGIN_ENABLED"]
            ?? configuration["DemoAdmin:Enabled"];

        return bool.TryParse(rawValue, out var enabled) && enabled;
    }

    private static string ReadDemoSetting(IConfiguration configuration, string key, string environmentKey) =>
        configuration[environmentKey] ?? configuration[$"DemoAdmin:{key}"] ?? string.Empty;

    private static string ReadDemoLoginIdentifier(IConfiguration configuration)
    {
        var configuredLogin = ReadDemoSetting(configuration, "Login", "VTSA_DEMO_ADMIN_LOGIN");
        return string.IsNullOrWhiteSpace(configuredLogin)
            ? ReadDemoSetting(configuration, "Email", "VTSA_DEMO_ADMIN_EMAIL")
            : configuredLogin;
    }

    private static bool HasDatabaseDemoCredentials(SiteSettings settings) =>
        !string.IsNullOrWhiteSpace(settings.DemoLoginEmail)
        && !string.IsNullOrWhiteSpace(settings.DemoLoginPasswordHash)
        && !string.IsNullOrWhiteSpace(settings.DemoLoginPasswordSalt);

    private static bool LoginMatchesIdentifier(string login, params string?[] validIdentifiers)
    {
        var normalizedLogin = NormalizeLoginIdentifier(login);
        if (string.IsNullOrWhiteSpace(normalizedLogin))
        {
            return false;
        }

        return validIdentifiers
            .Select(NormalizeLoginIdentifier)
            .Any(identifier => string.Equals(normalizedLogin, identifier, StringComparison.OrdinalIgnoreCase));
    }

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