package it.com.atlassian.stash.rules;

import com.atlassian.fugue.Suppliers;
import com.atlassian.webdriver.browsers.WebDriverBrowserAutoInstall;
import com.atlassian.webdriver.utils.WebDriverUtil;
import com.google.common.base.Supplier;
import org.junit.rules.ExternalResource;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.html5.WebStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.Collection;
import java.util.EnumSet;

/**
 * Cleans up any type of {@link StorageType web storage} before and after test.
 *
 * @since 3.5
 */
public class WebStorageCleanUpRule extends ExternalResource {

    private static final Logger log = LoggerFactory.getLogger(WebStorageCleanUpRule.class);

    private final Supplier<? extends WebDriver> webDriverSupplier;
    private final Iterable<StorageType> storageTypes;

    public WebStorageCleanUpRule() {
        this(StorageType.ALL);
    }

    public WebStorageCleanUpRule(@Nonnull Collection<StorageType> storageTypes) {
        this.webDriverSupplier = WebDriverBrowserAutoInstall.driverSupplier();
        this.storageTypes = EnumSet.copyOf(storageTypes);
    }

    @Inject
    public WebStorageCleanUpRule(@Nonnull WebDriver webDriver) {
        this.webDriverSupplier = Suppliers.ofInstance(webDriver);
        this.storageTypes = StorageType.ALL;
    }

    @Override
    protected void before() throws Throwable {
        cleanUp();
    }

    @Override
    protected void after() {
        cleanUp();
    }

    private void cleanUp() {
        final WebDriver driver = webDriverSupplier.get();
        if (driver == null) {
            log.error("No WebDriver in the test environment, cannot execute this rule without WebDriver");
            return;
        }

        for (StorageType storageType : storageTypes) {
            cleanUpStorage(webDriverSupplier.get(), storageType);
        }
    }

    private void cleanUpStorage(WebDriver webDriver, StorageType storageType) {
        if (WebDriverUtil.isInstance(webDriver, WebStorage.class)) {
            cleanUpUsingWebStorage(webDriver, storageType);
        } else if (WebDriverUtil.isInstance(webDriver, JavascriptExecutor.class)) {
            cleanUpUsingJs(webDriver, storageType);
        } else {
            log.warn("Unable to clean up sessionStorage for driver: {}", WebDriverUtil.getUnderlyingDriver(webDriver));
        }
    }

    private void cleanUpUsingWebStorage(WebDriver webDriver, StorageType storageType) {
        try {
            storageType.cleanUpUsingWebStorage(WebDriverUtil.as(webDriver, WebStorage.class));
        } catch (Exception e) {
            log.warn("Error when attempting to clean up {} using driver: {}", storageType.getStorageName(), webDriver);
            log.debug("Error details", e);
        }
    }

    private void cleanUpUsingJs(WebDriver webDriver, StorageType storageType) {
        try {
            storageType.cleanUpUsingJs(WebDriverUtil.as(webDriver, JavascriptExecutor.class));
        } catch (Exception e) {
            log.warn("Error when attempting to clean up {} using driver: {}", storageType.getStorageName(), webDriver);
            log.debug("Error details", e);
        }
    }
}
