using System.Text.Json;
using Microsoft.Playwright;

namespace ScadaLink.CentralUI.PlaywrightTests;

[Collection("Playwright")]
public class LoginTests
{
    private readonly PlaywrightFixture _fixture;

    public LoginTests(PlaywrightFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public async Task UnauthenticatedUser_RedirectsToLogin()
    {
        var page = await _fixture.NewPageAsync();

        await page.GotoAsync(PlaywrightFixture.BaseUrl);

        Assert.Contains("/login", page.Url);
        await Expect(page.Locator("h4")).ToHaveTextAsync("ScadaLink");
        await Expect(page.Locator("#username")).ToBeVisibleAsync();
        await Expect(page.Locator("#password")).ToBeVisibleAsync();
        await Expect(page.Locator("button[type='submit']")).ToHaveTextAsync("Sign In");
    }

    [Fact]
    public async Task ValidCredentials_AuthenticatesSuccessfully()
    {
        var page = await _fixture.NewAuthenticatedPageAsync();

        // Should be on the dashboard, not the login page
        Assert.DoesNotContain("/login", page.Url);
    }

    [Fact]
    public async Task InvalidCredentials_ShowsError()
    {
        var page = await _fixture.NewPageAsync();

        await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login");
        await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);

        // POST invalid credentials via fetch
        var status = await page.EvaluateAsync<int>(@"
            async () => {
                const resp = await fetch('/auth/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: 'username=baduser&password=badpass',
                    redirect: 'follow'
                });
                return resp.status;
            }
        ");

        // The login endpoint redirects to /login?error=... on failure.
        // Reload the page to see the error state.
        await page.ReloadAsync();
        Assert.Equal(200, status); // redirect followed to login page
    }

    [Fact]
    public async Task TokenEndpoint_ReturnsJwt()
    {
        var page = await _fixture.NewPageAsync();

        await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login");
        await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);

        var result = await page.EvaluateAsync<JsonElement>(@"
            async () => {
                const resp = await fetch('/auth/token', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: 'username=multi-role&password=password'
                });
                const json = await resp.json();
                return { status: resp.status, hasToken: !!json.access_token, username: json.username || '' };
            }
        ");

        Assert.Equal(200, result.GetProperty("status").GetInt32());
        Assert.True(result.GetProperty("hasToken").GetBoolean());
        Assert.Equal("multi-role", result.GetProperty("username").GetString());
    }

    [Fact]
    public async Task TokenEndpoint_InvalidCredentials_Returns401()
    {
        var page = await _fixture.NewPageAsync();

        await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/login");
        await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);

        var status = await page.EvaluateAsync<int>(@"
            async () => {
                const resp = await fetch('/auth/token', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: 'username=baduser&password=badpass'
                });
                return resp.status;
            }
        ");

        Assert.Equal(401, status);
    }

    private static ILocatorAssertions Expect(ILocator locator) =>
        Assertions.Expect(locator);
}
