using System.Net;
using Microsoft.Extensions.DependencyInjection;
using ScadaLink.CentralUI.Auth;
using ScadaLink.Security;

namespace ScadaLink.IntegrationTests;

/// <summary>
/// WP-22: Auth flow integration tests.
/// Tests that require a running LDAP server are marked with Integration trait.
/// </summary>
public class AuthFlowTests : IClassFixture<ScadaLinkWebApplicationFactory>
{
    private readonly ScadaLinkWebApplicationFactory _factory;

    public AuthFlowTests(ScadaLinkWebApplicationFactory factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task LoginEndpoint_WithEmptyCredentials_RedirectsToLoginWithError()
    {
        var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("username", ""),
            new KeyValuePair<string, string>("password", "")
        });

        var response = await client.PostAsync("/auth/login", content);

        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        var location = response.Headers.Location?.ToString() ?? "";
        Assert.Contains("/login", location);
        Assert.Contains("error", location, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task LogoutEndpoint_ClearsCookieAndRedirects()
    {
        var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

        var response = await client.PostAsync("/auth/logout", null);

        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        var location = response.Headers.Location?.ToString() ?? "";
        Assert.Contains("/login", location);
    }

    [Fact]
    public void JwtTokenService_GenerateAndValidate_RoundTrips()
    {
        using var scope = _factory.Services.CreateScope();
        var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();

        var token = jwtService.GenerateToken(
            displayName: "Test User",
            username: "testuser",
            roles: new[] { "Admin", "Design" },
            permittedSiteIds: null);

        Assert.NotNull(token);

        var principal = jwtService.ValidateToken(token);
        Assert.NotNull(principal);

        var displayName = principal!.FindFirst(JwtTokenService.DisplayNameClaimType)?.Value;
        var username = principal.FindFirst(JwtTokenService.UsernameClaimType)?.Value;
        var roles = principal.FindAll(JwtTokenService.RoleClaimType).Select(c => c.Value).ToList();

        Assert.Equal("Test User", displayName);
        Assert.Equal("testuser", username);
        Assert.Contains("Admin", roles);
        Assert.Contains("Design", roles);
    }

    [Fact]
    public void JwtTokenService_WithSiteScopes_IncludesSiteIdClaims()
    {
        using var scope = _factory.Services.CreateScope();
        var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();

        var token = jwtService.GenerateToken(
            displayName: "Deployer",
            username: "deployer1",
            roles: new[] { "Deployment" },
            permittedSiteIds: new[] { "1", "3" });

        var principal = jwtService.ValidateToken(token);
        Assert.NotNull(principal);

        var siteIds = principal!.FindAll(JwtTokenService.SiteIdClaimType).Select(c => c.Value).ToList();
        Assert.Contains("1", siteIds);
        Assert.Contains("3", siteIds);
    }

    [Trait("Category", "Integration")]
    [Fact]
    public async Task LoginEndpoint_WithValidLdapCredentials_SetsCookieAndRedirects()
    {
        // Requires GLAuth test LDAP server: docker compose -f infra/docker-compose.yml up -d glauth
        // GLAuth runs on localhost:3893, baseDN dc=scadalink,dc=local, all passwords "password"
        if (!await IsLdapAvailableAsync())
        {
            // Skip gracefully if GLAuth not running — not a test failure
            return;
        }

        var client = _factory.CreateClient(new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("username", "admin"),
            new KeyValuePair<string, string>("password", "password")
        });

        var response = await client.PostAsync("/auth/login", content);

        Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
        var location = response.Headers.Location?.ToString() ?? "";
        Assert.Equal("/", location);

        // Verify auth cookie was set
        var setCookieHeader = response.Headers.GetValues("Set-Cookie").FirstOrDefault();
        Assert.NotNull(setCookieHeader);
        Assert.Contains("ScadaLink.Auth", setCookieHeader);
    }

    private static async Task<bool> IsLdapAvailableAsync()
    {
        try
        {
            using var tcp = new System.Net.Sockets.TcpClient();
            await tcp.ConnectAsync("localhost", 3893).WaitAsync(TimeSpan.FromSeconds(2));
            return true;
        }
        catch
        {
            return false;
        }
    }
}
