package com.example.support;

import com.clanstop.healing.config.FrameworkConfig;
import io.cucumber.java.After;
import io.cucumber.java.AfterStep;
import io.cucumber.java.Before;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeDriverService;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.GeckoDriverService;

import java.io.File;
import java.time.Duration;

/**
 * Plain (no self-healing) driver lifecycle, shared by every generated plain test. Builds a
 * browser per scenario from healing.properties (browser / headless / driver path / implicit
 * wait) and shares it via {@link Driver}. No editing, no hardcoded paths - Selenium Manager
 * downloads the driver automatically when none is configured.
 */
public class PlainHooks {

    // "not @api": API-only tests are tagged @api and need no browser, so skip the driver lifecycle
    // for them (nothing to launch, nothing to quit). UI and mixed UI+API tests are unaffected.
    @Before("not @api")
    public void setUp() {
        FrameworkConfig cfg = FrameworkConfig.get();
        WebDriver d = build(cfg);
        long implicitWaitMs = cfg.implicitWaitMs();
        if (implicitWaitMs > 0) {
            d.manage().timeouts().implicitlyWait(Duration.ofMillis(implicitWaitMs));
        }
        Driver.set(d);
    }

    // Slow-mo: pause after every UI step so a run is easy to watch (mirrors the recorder's setting).
    // Driven by healing.step.pause.ms / -DHEALING_STEP_PAUSE_MS; 0 = off. Skips @api (no browser).
    @AfterStep("not @api")
    public void slowMo() {
        long pause = FrameworkConfig.get().stepPauseMs();
        if (pause > 0) {
            try {
                Thread.sleep(pause);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    @After("not @api")
    public void tearDown() {
        if (Driver.get() != null) {
            Driver.get().quit();
            Driver.remove();
        }
    }

    private static WebDriver remote(String url, org.openqa.selenium.MutableCapabilities options) {
        try {
            return new org.openqa.selenium.remote.RemoteWebDriver(new java.net.URL(url), options);
        } catch (java.net.MalformedURLException e) {
            throw new RuntimeException("Invalid grid / remote hub URL: " + url, e);
        }
    }

    private static WebDriver build(FrameworkConfig cfg) {
        boolean headless = cfg.headless();
        String driverPath = cfg.driverPath();
        String bin = cfg.browserBinary();

        // Cross-browser cloud grid: when a hub is configured, run there (browser chosen by grid.browser)
        // instead of a local browser. Covers plain Selenium Grid and BrowserStack/Sauce Labs/LambdaTest.
        String hub = com.clanstop.healing.driver.GridCapabilities.hubUrl(cfg);
        if (hub != null) {
            return remote(hub, com.clanstop.healing.driver.GridCapabilities.build(cfg));
        }

        switch (cfg.browser()) {
            case "firefox": {
                FirefoxOptions o = new FirefoxOptions();
                if (headless) { o.addArguments("-headless"); }
                if (bin != null) { o.setBinary(bin); }
                if (driverPath != null) {
                    return new FirefoxDriver(new GeckoDriverService.Builder()
                            .usingDriverExecutable(new File(driverPath)).build(), o);
                }
                return new FirefoxDriver(o);
            }
            case "edge": {
                EdgeOptions o = new EdgeOptions();
                if (headless) { o.addArguments("--headless=new"); }
                o.addArguments("--no-sandbox", "--disable-dev-shm-usage", "--window-size=1280,900");
                if (bin != null) { o.setBinary(bin); }
                if (driverPath != null) {
                    return new EdgeDriver(new EdgeDriverService.Builder()
                            .usingDriverExecutable(new File(driverPath)).build(), o);
                }
                return new EdgeDriver(o);
            }
            default: {
                ChromeOptions o = new ChromeOptions();
                if (headless) { o.addArguments("--headless=new"); }
                o.addArguments("--no-sandbox", "--disable-dev-shm-usage", "--window-size=1280,900");
                if (bin != null) { o.setBinary(bin); }
                if (driverPath != null) {
                    return new ChromeDriver(new ChromeDriverService.Builder()
                            .usingDriverExecutable(new File(driverPath)).build(), o);
                }
                return new ChromeDriver(o);
            }
        }
    }
}
