using System.Net;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Identity;
using Xunit;
using IamService.API.Application.Commands.Auth;
using IamService.API.Application.Common;
using IamService.Domain.AggregatesModel.UserAggregate;

namespace IamService.FunctionalTests.Controllers;

/// <summary>
/// EN: Functional tests for AuthController endpoints.
/// VI: Functional tests cho các endpoints của AuthController.
/// </summary>
public class AuthControllerTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory _factory;

    public AuthControllerTests(CustomWebApplicationFactory factory)
    {
        _factory = factory;
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false
        });
    }

    [Fact]
    public async Task Register_WithValidData_ShouldReturn201()
    {
        // Arrange
        var request = new RegisterUserCommand(
            $"test_{Guid.NewGuid()}@example.com",
            "Password123!",
            "John",
            "Doe");

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/auth/register", request);

        // Debug: Print response body if not Created
        if (response.StatusCode != HttpStatusCode.Created)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new Exception($"Expected Created, got {response.StatusCode}. Body: {body}");
        }

        // Assert
        Assert.Equal(HttpStatusCode.Created, response.StatusCode);

        var result = await response.Content.ReadFromJsonAsync<ApiResponse<RegisterUserCommandResult>>();
        Assert.NotNull(result);
        Assert.True(result.Success);
        Assert.NotNull(result.Data);
        Assert.Equal(request.Email, result.Data.Email);
    }

    [Fact]
    public async Task Register_WithInvalidEmail_ShouldReturn400()
    {
        // Arrange
        var request = new
        {
            Email = "invalid-email",
            Password = "Password123!",
            FirstName = "John",
            LastName = "Doe"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/auth/register", request);

        // Debug: Print response body if not expected
        if (response.StatusCode != HttpStatusCode.BadRequest)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new Exception($"Expected BadRequest, got {response.StatusCode}. Body: {body}");
        }

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Fact]
    public async Task Register_WithWeakPassword_ShouldReturn400()
    {
        // Arrange
        var request = new
        {
            Email = "test@example.com",
            Password = "weak",
            FirstName = "John",
            LastName = "Doe"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/auth/register", request);

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Fact]
    public async Task Register_WithDuplicateEmail_ShouldReturn409()
    {
        // Arrange
        var email = $"duplicate_{Guid.NewGuid()}@example.com";
        var request = new RegisterUserCommand(email, "Password123!", "John", "Doe");

        // First registration
        await _client.PostAsJsonAsync("/api/v1/auth/register", request);

        // Act - Second registration with same email
        var response = await _client.PostAsJsonAsync("/api/v1/auth/register", request);

        // Debug: Print response body if not expected
        if (response.StatusCode != HttpStatusCode.Conflict)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new Exception($"Expected Conflict, got {response.StatusCode}. Body: {body}");
        }

        // Assert
        Assert.Equal(HttpStatusCode.Conflict, response.StatusCode);
    }

    [Fact]
    public async Task Token_WithValidCredentials_ShouldReturnTokens()
    {
        // Arrange - Register user first
        var email = $"login_{Guid.NewGuid()}@example.com";
        var password = "Password123!";

        await _client.PostAsJsonAsync("/api/v1/auth/register", new
        {
            Email = email,
            Password = password,
            FirstName = "Login",
            LastName = "Test"
        });

        // Activate user (in real scenario, need to verify email first)
        using var scope = _factory.Services.CreateScope();
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
        var user = await userManager.FindByEmailAsync(email);
        if (user != null)
        {
            user.Activate();
            await userManager.UpdateAsync(user);
        }

        // Act - Login using Duende IdentityServer format
        var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "password",
            ["client_id"] = "password-client",
            ["client_secret"] = "password-client-secret",
            ["username"] = email,
            ["password"] = password,
            ["scope"] = "openid profile email api offline_access"
        });

        var response = await _client.PostAsync("/connect/token", tokenRequest);

        // Debug: Print response body if not expected
        if (response.StatusCode != HttpStatusCode.OK)
        {
            var body = await response.Content.ReadAsStringAsync();
            throw new Exception($"Expected OK, got {response.StatusCode}. Body: {body}");
        }

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        var result = await response.Content.ReadFromJsonAsync<TokenResponse>();
        Assert.NotNull(result);
        Assert.NotEmpty(result.access_token);
        Assert.Equal("Bearer", result.token_type);
    }

    [Fact]
    public async Task Token_WithInvalidCredentials_ShouldReturn400()
    {
        // Arrange - using Duende IdentityServer format
        var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["grant_type"] = "password",
            ["client_id"] = "password-client",
            ["client_secret"] = "password-client-secret",
            ["username"] = "nonexistent@example.com",
            ["password"] = "WrongPassword123!",
            ["scope"] = "openid api"
        });

        // Act
        var response = await _client.PostAsync("/connect/token", tokenRequest);

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    private record TokenResponse(
        string access_token,
        string token_type,
        int expires_in,
        string? refresh_token,
        string? scope);
}
