using Microsoft.Playwright;

namespace ScadaLink.CentralUI.PlaywrightTests;

/// <summary>
/// Shared fixture that manages the Playwright browser connection.
/// Creates a single browser connection per test collection, reused across all tests.
/// Requires the Playwright Docker container running at ws://localhost:3000.
/// </summary>
public class PlaywrightFixture : IAsyncLifetime
{
    /// <summary>
    /// Playwright Server WebSocket endpoint (Docker container on host port 3000).
    /// </summary>
    private const string PlaywrightWsEndpoint = "ws://localhost:3000";

    /// <summary>
    /// Central UI base URL as seen from inside the Docker network.
    /// The browser runs in the Playwright container, so it uses the Docker hostname.
    /// </summary>
    public const string BaseUrl = "http://scadalink-traefik";

    /// <summary>Test LDAP credentials (multi-role user with Admin + Design + Deployment).</summary>
    public const string TestUsername = "multi-role";
    public const string TestPassword = "password";

    public IPlaywright Playwright { get; private set; } = null!;
    public IBrowser Browser { get; private set; } = null!;

    public async Task InitializeAsync()
    {
        Playwright = await Microsoft.Playwright.Playwright.CreateAsync();
        Browser = await Playwright.Chromium.ConnectAsync(PlaywrightWsEndpoint);
    }

    public async Task DisposeAsync()
    {
        await Browser.CloseAsync();
        Playwright.Dispose();
    }

    /// <summary>
    /// Create a new browser context and page. Each test gets an isolated session.
    /// </summary>
    public async Task<IPage> NewPageAsync()
    {
        var context = await Browser.NewContextAsync();
        return await context.NewPageAsync();
    }

    /// <summary>
    /// Create a new page and log in with the default multi-role test user.
    /// </summary>
    public Task<IPage> NewAuthenticatedPageAsync() =>
        NewAuthenticatedPageAsync(TestUsername, TestPassword);

    /// <summary>
    /// Create a new page and log in with specific credentials.
    /// Uses JavaScript fetch() to POST to /auth/login from within the browser,
    /// which sets the auth cookie in the browser context. Then navigates to the dashboard.
    /// </summary>
    public async Task<IPage> NewAuthenticatedPageAsync(string username, string password)
    {
        var page = await NewPageAsync();

        // Navigate to the login page first to establish the origin
        await page.GotoAsync($"{BaseUrl}/login");
        await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);

        // POST to /auth/login via fetch() inside the browser.
        // This sets the auth cookie in the browser context automatically.
        var finalUrl = await page.EvaluateAsync<string>(@"
            async ([u, p]) => {
                const resp = await fetch('/auth/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                    body: 'username=' + encodeURIComponent(u) + '&password=' + encodeURIComponent(p),
                    redirect: 'follow'
                });
                return resp.url;
            }
        ", new object[] { username, password });

        if (finalUrl.Contains("/login"))
        {
            throw new InvalidOperationException($"Login failed for '{username}' — redirected back to login: {finalUrl}");
        }

        // Navigate to the dashboard — cookie authenticates us
        await page.GotoAsync(BaseUrl);
        await page.WaitForLoadStateAsync(LoadState.NetworkIdle);

        return page;
    }

    /// <summary>
    /// Wait for Blazor enhanced navigation to update the URL path.
    /// Blazor Server uses SignalR for client-side navigation (no full page reload),
    /// so standard WaitForURLAsync times out. This polls window.location instead.
    /// </summary>
    public static async Task WaitForPathAsync(IPage page, string path, string? excludePath = null, int timeoutMs = 10000)
    {
        var js = excludePath != null
            ? $"() => window.location.pathname.includes('{path}') && !window.location.pathname.includes('{excludePath}')"
            : $"() => window.location.pathname.includes('{path}')";
        await page.WaitForFunctionAsync(js, null, new() { Timeout = timeoutMs });
    }
}

[CollectionDefinition("Playwright")]
public class PlaywrightCollection : ICollectionFixture<PlaywrightFixture>;
