using NUnit.Framework;
using System.Text.RegularExpressions;
using Microsoft.Playwright;
using Microsoft.Playwright.NUnit;
using NUnit.Framework.Internal;

namespace PlaywrightTests;

[Parallelizable(ParallelScope.Self)]
[TestFixture]
public partial class GumbyPageTests : PageTest
{
    private const string ScreenshotRoot = "../../../screenshots/";
    private const string HomeUrl = "http://localhost:5296";
    private const int AppInitDelay = 5000;
    private IPage? _page;
    private IBrowser? _browser;
    
    [GeneratedRegex("Home page")]
    private static partial Regex HomePageRegex();
    
    [GeneratedRegex(".*Gumby")]
    private static partial Regex GumbyUrlRegex();

    [SetUp]
    public async Task Init()
    {
        _browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions() { Headless = true, });
        _page = await _browser.NewPageAsync();
        await WaitForApp(_page);
    }

    [Test]
    public async Task GumbyOptionShouldBePresentInMenu()
    {
        // Confirm we've found the Gumby link in the header
        var gumbyPageLink = _page!.GetByRole(AriaRole.Link, new PageGetByRoleOptions { Name = "Gumby" });
        await Expect(gumbyPageLink).ToHaveAttributeAsync("href", "/Gumby");
    }

    [Test]
    public async Task NavigationToGumbyPageShouldSucceed()
    {
        await GotoGumby(_page!);
        await PageSnap(_page!, "gumby.png");
        await Expect(_page!).ToHaveURLAsync(GumbyUrlRegex());
    }
    
    [Test]
    public async Task GumbyImageShouldBePresentOnBrowser()
    {
        await GotoGumby(_page!);
        Expect(_page!.GetByRole(AriaRole.Img, new PageGetByRoleOptions { Name = "Gumby" }));
        
    }
    
    [Test]
    public async Task ResponseFromButtonShouldNotTakeTooLong()
    {
        /*
         * This test will sometimes pass, and sometimes fail, if the randomizer on MyWebApp is working correctly.
         * That is BY DESIGN. It's meant to show (at least in a rudimentary way) how a Playwright test can be used
         * to do synthetic monitoring. 
         */
        await GotoGumby(_page!);
        
        const int delay = 10; //MyWebApp is set to 12 seconds, so it should fail sometimes
        const int delayMillis = delay * 1000; // A thousand milliseconds in a second
        
        await _page!.GetByText("Clear").ClickAsync(); //clear the label first.
        await PageSnap(_page!, "gumby-before-button-press.png");
        
        await _page!.GetByText("Press Me!").ClickAsync();
        await PageSnap(_page!, "/gumby-after-button-press-during-wait.png");
        
        await Expect(_page!.Locator("label[id=result]"))
            .ToContainTextAsync("You Pressed The Button", new LocatorAssertionsToContainTextOptions {Timeout = delayMillis});
       
        await PageSnap(_page!, "gumby-after-test-completion.png");

    }

    /*** HELPERS ***/
    
    private static async Task PageSnap(IPage page, string filename)
    {
        await page.ScreenshotAsync(new PageScreenshotOptions { FullPage = true, Path = ScreenshotRoot+"/"+filename});
    }

    private static async Task ElementSnap(ILocator locator, string filename)
    {
        await locator.ScreenshotAsync(new LocatorScreenshotOptions { Path = ScreenshotRoot+"/"+filename });
    }
    
    private static async Task GotoGumby(IPage page)
    {
        await page.GetByRole(AriaRole.Link, new PageGetByRoleOptions { Name = "Gumby" }).ClickAsync();
    }
    
    private static async Task WaitForApp(IPage page)
    {
        var pageIsLoaded = false;
        while (!pageIsLoaded)
        {
            try
            {
                await page.GotoAsync(HomeUrl, new PageGotoOptions() { Timeout = AppInitDelay }); 
                pageIsLoaded = true;
            }
            catch
            {
                await Task.Delay(AppInitDelay);
            }
        }

        var appIsLoaded = false;
        while (!appIsLoaded)
        {
            try
            {
                var titleAsync = await page.TitleAsync();
                if (HomePageRegex().Match(titleAsync).Success)
                {
                    appIsLoaded = true;
                    continue;
                };
            }
            catch
            {
                // _logger.Information("Error loading app");
            }

            // _logger.Information("App not loaded, delaying before retrying...");
            await Task.Delay(AppInitDelay);
        }
        // _logger.Information($"{nameof(WaitForApp)} completed");
    }
}