/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.technicalconnector.tests.junit.rule;

import java.util.Properties;

import org.apache.commons.lang.Validate;
import org.junit.rules.ExternalResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.technicalconnector.config.ConfigFactory;
import be.ehealth.technicalconnector.config.ConfigurationModule;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.fgov.ehealth.technicalconnector.tests.session.SessionDestroyer;
import be.fgov.ehealth.technicalconnector.tests.session.SessionInitializer;
import be.fgov.ehealth.technicalconnector.tests.utils.LoggingUtils;
import be.fgov.ehealth.technicalconnector.tests.utils.SupportedLanguages;

/**
 * @author EHP
 */
public class SessionRule extends ExternalResource {
    private static final Logger LOG = LoggerFactory.getLogger(SessionRule.class);

    public static EnvironmentSelectorStep withActiveSession() {
        return new InnerStep(true);
    }

    public static EnvironmentSelectorStep withInactiveSession() {
        return new InnerStep(false);
    }

    public interface EnvironmentSelectorStep extends ConfigurationModuleStep {

        ProfessionSelectorStep forEnvironment(String env);

        BuilderStep baseOn(String location);
    }

    public interface ProfessionSelectorStep {

        UserSelectorStep forProfession(String prof);
    }

    public interface UserSelectorStep {

        ProgramLangOrOverridingPropertiesStep forUser(String env);
    }

    public interface OverridingTestPropertiesStep {

    }

    public interface ProgramLangOrOverridingPropertiesStep extends ConfigurationModuleStep {

        ConfigurationModuleStep withOverridingTestProperties(Properties properties);

        ConfigurationModuleStep forProgrammingLanguage(SupportedLanguages lang);

    }

    public interface ConfigurationModuleStep extends BuilderStep {

        BuilderStep with(Class<? extends ConfigurationModule>... modules);

    }

    public interface BuilderStep {

        SessionRule build();
    }


    private static class InnerStep implements EnvironmentSelectorStep, ProfessionSelectorStep, UserSelectorStep, ProgramLangOrOverridingPropertiesStep {

        private boolean activate;

        private SupportedLanguages lang = SupportedLanguages.JAVA;

        private Properties props = new Properties();

        private Class<? extends ConfigurationModule>[] modules = new Class[]{};

        private Properties overridingTestProps;

        private InnerStep(boolean activeSession) {
            this.activate = activeSession;
        }

        @Override
        public BuilderStep baseOn(String location) {
            Validate.notEmpty(location);
            try {
                props.load(ConnectorIOUtils.getResourceAsStream(location));
            } catch (Exception e) {
                throw new IllegalArgumentException(e);
            }
            return this;
        }

        @Override
        public ConfigurationModuleStep withOverridingTestProperties(Properties properties) {
            overridingTestProps = properties;
            return this;
        }

        @Override
        public ConfigurationModuleStep forProgrammingLanguage(SupportedLanguages lang) {
            this.lang = lang;
            return this;
        }

        @Override
        public ProgramLangOrOverridingPropertiesStep forUser(String user) {
            Validate.notEmpty(user);
            props.setProperty(SessionInitializer.SESSION_USERNAME, user);
            return this;
        }

        @Override
        public UserSelectorStep forProfession(String prof) {
            Validate.notEmpty(prof);
            props.setProperty(SessionInitializer.SESSION_PROFESSION_TYPE, prof);
            return this;
        }

        @Override
        public ProfessionSelectorStep forEnvironment(String env) {
            Validate.notEmpty(env);
            props.setProperty(SessionInitializer.SESSION_ENVIRONMENT, env);
            return this;
        }

        @Override
        public BuilderStep with(Class<? extends ConfigurationModule>... modules) {
            Validate.noNullElements(modules);
            this.modules = modules;
            return this;
        }

        @Override
        public SessionRule build() {
            LoggingUtils.bootstrap();
            return new SessionRule(props, activate, lang, overridingTestProps, modules);
        }

    }

    private Properties props;

    private boolean activate;

    private SupportedLanguages devLang;

    private Properties overridingTestProps;

    private Class<? extends ConfigurationModule>[] modules;

    private SessionRule(Properties props, boolean activate, SupportedLanguages devLang, Properties overridingTestProps, Class<? extends ConfigurationModule>... modules) {
        this.props = props;
        this.activate = activate;
        this.devLang = devLang;
        this.modules = modules;
        this.overridingTestProps = overridingTestProps;
    }


    @Override
    protected void before() throws Throwable {
        ConfigFactory.setConfiguration(null);
        try {
            ConfigFactory.setConfigLocation(null);
        } catch (Exception e) {
            LOG.debug("Unable to reset configlocation", e);
        }
        SessionInitializer.init(props, activate, devLang.getAbbreviation(), overridingTestProps);
        for (Class module : modules) {
            ((ConfigurationModule) module.newInstance()).init(null);
        }
    }


    @Override
    protected void after() {
        try {
            SessionDestroyer.destroy();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public String getSessionProperty(String key) {
        return SessionInitializer.getSessionProps()
                .getProperty(key);
    }

    public String getTestProperty(String key) {
        return props.getProperty(key);
    }
}
