/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2024  Delft University of Technology
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package e2e;

import java.io.File;
import java.util.*;

import org.assertj.core.api.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;

import com.microsoft.playwright.*;
import com.microsoft.playwright.assertions.LocatorAssertions;
import com.microsoft.playwright.assertions.PlaywrightAssertions;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class EndToEndTest implements E2E {

	@Container
	public static DockerComposeContainer<?> compose = new DockerComposeContainer<>(new File("compose.yml"))
			.withExposedService("database", 3306, Wait.forHealthcheck())
			.withExposedService("labracore", 8082, Wait.forHealthcheck())
			.withExposedService("queue", 8081, Wait.forListeningPort());
	static {
		compose.start();
	}

	protected Playwright playwright;
	protected Browser browser;

	protected BrowserContext context;
	protected Page page;

	protected String basePath;

	public EndToEndTest() {
		this.playwright = Playwright.create();

		String browser = System.getenv("BROWSER");
		if (browser == null) {
			browser = "chrome";
		}
		boolean headless = !"false".equalsIgnoreCase(System.getenv("HEADLESS"));
		BrowserType.LaunchOptions options = new BrowserType.LaunchOptions().setHeadless(headless);
		this.browser = switch (browser.toLowerCase()) {
			case "safari" -> playwright.webkit().launch(options);
			case "firefox" -> playwright.firefox().launch(options);
			default -> playwright.chromium().launch(options);
		};

		String host = System.getenv("HOST");
		if (host == null) {
			host = "http://localhost";
		}
		this.basePath = host + ":8081";
	}

	@BeforeEach
	void createContext() {
		boolean mobile = "true".equalsIgnoreCase(System.getenv("MOBILE"));
		Browser.NewContextOptions options = new Browser.NewContextOptions()
				.setPermissions(List.of("notifications"));
		if (mobile) {
			options.setViewportSize(1170, 2532).setDeviceScaleFactor(2).setIsMobile(true).setHasTouch(true);
		}
		context = browser.newContext(options);
		context.grantPermissions(List.of("notifications"),
				new BrowserContext.GrantPermissionsOptions().setOrigin("http://localhost:8081"));
		page = browser.newPage();
		if (mobile) {
			page.setViewportSize(1170, 2532);
		}
	}

	@AfterAll
	void close() {
		playwright.close();
	}

	@Override
	public String withPath(String path) {
		return basePath + path;
	}

	private static int nextName = 1;

	@Override
	public String uniqueName(String type) {
		return type + "-" + (nextName++);
	}

	@Override
	public Page page() {
		return page;
	}

	public LocatorAssertions assertThat(Locator locator) {
		return PlaywrightAssertions.assertThat(locator);
	}

	public <T> ObjectAssert<T> assertThat(T value) {
		return Assertions.assertThat(value);
	}

	public <T> ListAssert<T> assertThat(List<T> value) {
		return Assertions.assertThat(value);
	}

	public AbstractStringAssert<?> assertThat(String value) {
		return Assertions.assertThat(value);
	}
}
