using Microsoft.Playwright;
using ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Cluster;
using Xunit;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests.Design;

[Collection("Playwright")]
public class SharedScriptCrudTests
{
    private readonly PlaywrightFixture _pw;

    public SharedScriptCrudTests(PlaywrightFixture pw)
    {
        _pw = pw;
    }

    /// <summary>
    /// CLI-create a shared script, then exercise the Central UI list render + UI delete
    /// round-trip. The create/edit form is driven by a Monaco editor (brittle to type),
    /// so authoring happens via the CLI; the UI delete is the behavior under test. A
    /// best-effort CLI delete in finally guarantees no zztest-script-* definition leaks
    /// if the UI path fails mid-way (the happy path already deletes via the UI).
    /// </summary>
    [SkippableFact]
    public async Task CliCreated_Script_DeletesViaCard()
    {
        Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);

        var name = CliRunner.UniqueName("script");
        int id = await CliRunner.CreateSharedScriptAsync(name);

        try
        {
            var page = await _pw.NewAuthenticatedPageAsync();
            await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/design/shared-scripts");
            await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
            await Assertions.Expect(page.Locator("h4:has-text('Shared Scripts')")).ToBeVisibleAsync();

            // Narrow the list so the card locator stays unambiguous even with many scripts.
            await page.Locator("input[placeholder='Filter by name or code…']").FillAsync(name);

            var card = page.Locator("div.card").Filter(new() { HasText = name });
            await Assertions.Expect(card).ToHaveCountAsync(1, new() { Timeout = 10_000 });

            // Scope the kebab + delete item to the card's .dropdown for strict-mode safety.
            var dropdown = card.Locator(".dropdown");
            await dropdown.Locator("button[aria-label^='More actions']").ClickAsync();
            await dropdown.Locator(".dropdown-menu button.dropdown-item.text-danger").ClickAsync();

            await Assertions.Expect(page.Locator(".modal-title:has-text('Delete Shared Script')")).ToBeVisibleAsync();
            await page.Locator(".modal-footer .btn-danger").ClickAsync();

            // Single web-first assertion on the success toast — toasts auto-dismiss, so we
            // do NOT chase the toast body in a second sequential check.
            await Assertions.Expect(page.Locator(".toast", new() { HasText = "deleted" }))
                .ToHaveCountAsync(1, new() { Timeout = 15_000 });

            // The card must be gone.
            await Assertions.Expect(page.Locator("div.card").Filter(new() { HasText = name }))
                .ToHaveCountAsync(0, new() { Timeout = 10_000 });
        }
        finally
        {
            // Best-effort teardown: the happy path deletes via the UI, so this finds
            // nothing; it only fires if the UI path failed mid-way.
            await CliRunner.DeleteSharedScriptAsync(id);
        }
    }

    /// <summary>
    /// The create form renders: heading, the (enabled) Name input, the editor tabs, and
    /// the Monaco editor surface. Asserts render only — does NOT click Save, Check Syntax,
    /// or Test Run (Test Run fires real I/O). Mutates nothing.
    /// </summary>
    [SkippableFact]
    public async Task CreateForm_Renders()
    {
        Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);

        var page = await _pw.NewAuthenticatedPageAsync();

        await page.GotoAsync($"{PlaywrightFixture.BaseUrl}/design/shared-scripts/create");
        await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
        await Assertions.Expect(page.Locator("h4:has-text('New Shared Script')")).ToBeVisibleAsync();

        var nameInput = page.Locator("input[type=text].form-control.form-control-sm");
        await Assertions.Expect(nameInput).ToBeVisibleAsync();
        await Assertions.Expect(nameInput).ToBeEnabledAsync();

        await Assertions.Expect(page.Locator("button.nav-link:has-text('Code')")).ToBeVisibleAsync();
        await Assertions.Expect(page.Locator("button.nav-link:has-text('Parameters')")).ToBeVisibleAsync();
        await Assertions.Expect(page.Locator("button.nav-link:has-text('Return type')")).ToBeVisibleAsync();

        // Monaco mounts asynchronously via JS interop — give it a generous timeout.
        await Assertions.Expect(page.Locator(".monaco-editor")).ToBeVisibleAsync(new() { Timeout = 15_000 });
    }
}
