package com.example.e2e;

import com.example.Application;
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.assertions.PlaywrightAssertions;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

import java.util.Locale;

/**
 * Base class for end-to-end tests that drive the running application in a real
 * browser. The Spring Boot app is started on a random port, Playwright opens a
 * headless Chromium against it, and every test gets a fresh browser context —
 * so each test also gets a fresh Vaadin session.
 *
 * <p>Playwright downloads its browsers into {@code ~/.cache/ms-playwright} on
 * first use. If that is not possible (offline runner, missing system libraries)
 * the tests are <em>skipped</em> rather than failed, so the build stays green
 * on runners that cannot host a browser.
 */
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class PlaywrightTestBase {

    /** Generous: the first request in dev mode has to bootstrap the frontend. */
    private static final double TIMEOUT_MS = 60_000;

    private static Playwright playwright;
    private static Browser browser;

    @LocalServerPort
    private int port;

    private BrowserContext context;
    protected Page page;

    @BeforeAll
    static void launchBrowser() {
        try {
            playwright = Playwright.create();
            browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(true));
        } catch (Exception | UnsatisfiedLinkError e) {
            closeBrowser();
            Assumptions.abort("no Playwright browser available: " + e.getMessage());
        }
        PlaywrightAssertions.setDefaultAssertionTimeout(TIMEOUT_MS);
    }

    @AfterAll
    static void closeBrowser() {
        if (browser != null) {
            browser.close();
            browser = null;
        }
        if (playwright != null) {
            playwright.close();
            playwright = null;
        }
    }

    @BeforeEach
    void openPage() {
        // Pin the locale so the assertions have one fixed set of captions to
        // match. English, not German: the German bundle is the unsuffixed
        // fallback (`translations.properties`), so `de` is not a provided
        // locale and Vaadin serves `translations_en.properties` for it anyway.
        context = browser.newContext(new Browser.NewContextOptions().setLocale(Locale.US.toLanguageTag()));
        context.setDefaultTimeout(TIMEOUT_MS);
        page = context.newPage();
    }

    @AfterEach
    void closePage() {
        if (context != null) {
            context.close();
            context = null;
        }
    }

    /** Opens a Vaadin route, e.g. {@code navigate("formular")}. */
    protected void navigate(String route) {
        page.navigate("http://localhost:" + port + "/" + route);
    }
}
