package org.molgenis.ui.settings;

import org.molgenis.data.MolgenisDataException;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.settings.AppSettings;
import org.molgenis.data.settings.DefaultSettingsEntity;
import org.molgenis.data.settings.DefaultSettingsEntityType;
import org.molgenis.ui.menumanager.MenuManagerServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

import static java.util.Objects.requireNonNull;
import static org.molgenis.data.meta.AttributeType.*;

/**
 * Application settings that are read from a data source and persisted to a data source.
 */
@Component
public class AppDbSettings extends DefaultSettingsEntity implements AppSettings
{
	private static final long serialVersionUID = 1L;

	private static final String ID = "app";

	public AppDbSettings()
	{
		super(ID);
	}

	@Component
	public static class Meta extends DefaultSettingsEntityType
	{
		private static final String TITLE = "title";
		private static final String LOGO_NAVBAR_HREF = "logo_href_navbar";
		private static final String LOGO_TOP_HREF = "logo_href_top";
		private static final String FOOTER = "footer";
		private static final String SIGNUP = "signup";
		private static final String SIGNUP_MODERATION = "signup_moderation";
		private static final String GOOGLE_SIGN_IN = "google_sign_in";
		private static final String GOOGLE_APP_CLIENT_ID = "google_app_client_id";
		public static final String MENU = "molgenis_menu";
		private static final String LANGUAGE_CODE = "language_code";
		private static final String BOOTSTRAP_THEME = "bootstrap_theme";
		private static final String CSS_HREF = "css_href";

		private static final String TRACKING = "tracking";
		private static final String TRACKING_CODE_FOOTER = "tracking_code_footer";
		private static final String GOOGLE_ANALYTICS_IP_ANONYMIZATION = "ga_privacy_friendly";
		private static final String GOOGLE_ANALYTICS_TRACKING_ID = "ga_tracking_id";
		private static final String GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS = "ga_acc_privacy_friendly";
		private static final String GOOGLE_ANALYTICS_TRACKING_ID_MOLGENIS = "ga_tracking_id_mgs";
		private static final String GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS_MOLGENIS = "ga_acc_privacy_friendly_mgs";
		private static final String AGGREGATE_THRESHOLD = "aggregate_threshold";

		private static final String DEFAULT_TITLE = "MOLGENIS";
		private static final String DEFAULT_LOGO_NAVBAR_HREF = "/img/logo_molgenis_small.png";
		private static final boolean DEFAULT_SIGNUP = false;
		private static final boolean DEFAULT_SIGNUP_MODERATION = true;
		private static final boolean DEFAULT_GOOGLE_SIGN_IN = true;
		private static final String DEFAULT_GOOGLE_APP_CLIENT_ID = "130634143611-e2518d1uqu0qtec89pjgn50gbg95jin4.apps.googleusercontent.com";
		private static final String DEFAULT_LANGUAGE_CODE = "en";
		private static final String DEFAULT_BOOTSTRAP_THEME = "bootstrap-molgenis.min.css";
		private static final boolean DEFAULT_GOOGLE_ANALYTICS_IP_ANONYMIZATION = true;
		private static final boolean DEFAULT_GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS = false;
		private static final boolean DEFAULT_GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS_MOLGENIS = true;

		private static final String CUSTOM_JAVASCRIPT = "custom_javascript";

		private final MenuManagerServiceImpl menuManagerServiceImpl;

		@Autowired
		public Meta(@SuppressWarnings("SpringJavaAutowiringInspection") MenuManagerServiceImpl menuManagerServiceImpl)
		{
			super(ID);
			this.menuManagerServiceImpl = requireNonNull(menuManagerServiceImpl);
		}

		@Override
		public void init()
		{
			super.init();
			setLabel("Application settings");
			setDescription("General application settings.");

			addAttribute(TITLE).setDataType(STRING).setNillable(false).setDefaultValue(DEFAULT_TITLE)
					.setLabel("Application title").setDescription("Displayed in browser toolbar.");
			addAttribute(SIGNUP).setDataType(BOOL).setNillable(false).setDefaultValue(String.valueOf(DEFAULT_SIGNUP))
					.setLabel("Allow users to sign up");
			addAttribute(SIGNUP_MODERATION).setDataType(BOOL).setNillable(false)
					.setDefaultValue(String.valueOf(DEFAULT_SIGNUP_MODERATION)).setLabel("Sign up moderation")
					.setDescription("Admins must accept sign up requests before account activation")
					.setVisibleExpression("$('" + SIGNUP + "').eq(true).value()");
			addAttribute(GOOGLE_SIGN_IN).setDataType(BOOL).setNillable(false)
					.setDefaultValue(String.valueOf(DEFAULT_GOOGLE_SIGN_IN)).setLabel("Enable Google Sign-In")
					.setDescription("Enable users to sign in with their existing Google account").setVisibleExpression(
					"$('" + SIGNUP + "').eq(true).value() && $('" + SIGNUP_MODERATION + "').eq(false).value()");
			addAttribute(GOOGLE_APP_CLIENT_ID).setDataType(STRING).setNillable(false)
					.setDefaultValue(DEFAULT_GOOGLE_APP_CLIENT_ID).setLabel("Google app client ID")
					.setDescription("Google app client ID used during Google Sign-In")
					.setVisibleExpression("$('" + GOOGLE_SIGN_IN + "').eq(true).value()");
			addAttribute(LOGO_NAVBAR_HREF).setDataType(STRING).setNillable(true).setLabel("Logo in navigation bar")
					.setDefaultValue(DEFAULT_LOGO_NAVBAR_HREF)
					.setDescription("HREF to logo image used instead of home plugin label");
			addAttribute(LOGO_TOP_HREF).setDataType(STRING).setNillable(true).setLabel("Logo above navigation bar")
					.setDescription("HREF to logo image");
			addAttribute(FOOTER).setDataType(TEXT).setNillable(true).setLabel("Footer text");
			addAttribute(MENU).setDataType(TEXT).setNillable(true).setDefaultValue(getDefaultMenuValue())
					.setLabel("Menu").setDescription("JSON object that describes menu content.");
			addAttribute(LANGUAGE_CODE).setDataType(STRING).setNillable(false).setDefaultValue(DEFAULT_LANGUAGE_CODE)
					.setLabel("Language code").setDescription("ISO 639 alpha-2 or alpha-3 language code.");
			addAttribute(BOOTSTRAP_THEME).setDataType(STRING).setNillable(false)
					.setDefaultValue(DEFAULT_BOOTSTRAP_THEME).setLabel("Bootstrap theme")
					.setDescription("CSS file name of theme (see molgenis-core-ui/src/main/resources/css/themes).");
			addAttribute(CSS_HREF).setDataType(STRING).setNillable(true).setLabel("CSS href")
					.setDescription("CSS file name to add custom CSS (see molgenis-core-ui/src/main/resources/css).");

			addAttribute(AGGREGATE_THRESHOLD).setDataType(INT).setNillable(true).setLabel("Aggregate threshold")
					.setDescription(
							"Aggregate value counts below this threshold are reported as the threshold. (e.g. a count of 100 is reported as <= 10)");

			addAttribute(CUSTOM_JAVASCRIPT).setDataType(TEXT).setNillable(true).setLabel("Custom javascript headers")
					.setDescription(
							"Custom javascript headers, specified as comma separated list. These headers will be included in the molgenis header before the applications own javascript headers.");

			// tracking settings
			Attribute trackingAttr = addAttribute(TRACKING).setDataType(COMPOUND).setLabel("Tracking");

			addAttribute(GOOGLE_ANALYTICS_IP_ANONYMIZATION).setParent(trackingAttr).setDataType(BOOL).setNillable(false)
					.setDefaultValue(String.valueOf(DEFAULT_GOOGLE_ANALYTICS_IP_ANONYMIZATION))
					.setLabel("IP anonymization").setDescription(
					"Disables the cookie wall by using privacy friendly tracking (only works if google analytics accounts are configured correctly, see below)");
			addAttribute(GOOGLE_ANALYTICS_TRACKING_ID).setParent(trackingAttr).setDataType(STRING).setNillable(true)
					.setLabel("Google analytics tracking ID")
					.setDescription("Google analytics tracking ID (e.g. UA-XXXX-Y)");
			addAttribute(GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS).setParent(trackingAttr).setDataType(BOOL)
					.setNillable(false)
					.setDefaultValue(String.valueOf(DEFAULT_GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS))
					.setLabel("Google analytics account privacy friendly").setDescription(
					"Confirm that you have configured your Google Analytics account as described here: https://cbpweb.nl/sites/default/files/atoms/files/handleiding_privacyvriendelijk_instellen_google_analytics_0.pdf");
			addAttribute(GOOGLE_ANALYTICS_TRACKING_ID_MOLGENIS).setParent(trackingAttr).setDataType(STRING).setNillable(true)
					.setLabel("Google analytics tracking ID (MOLGENIS)")
					.setDescription("Google analytics tracking ID used by MOLGENIS");
			addAttribute(GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS_MOLGENIS).setParent(trackingAttr).setDataType(BOOL)
					.setNillable(false).setDefaultValue(
					String.valueOf(DEFAULT_GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS_MOLGENIS))
					.setReadOnly(true).setLabel("Google analytics account privacy friendly (MOLGENIS)").setDescription(
					"Confirm that the MOLGENIS Google Analytics account is configured as described here: https://cbpweb.nl/sites/default/files/atoms/files/handleiding_privacyvriendelijk_instellen_google_analytics_0.pdf");
			addAttribute(TRACKING_CODE_FOOTER).setParent(trackingAttr).setDataType(SCRIPT).setNillable(true)
					.setLabel("Tracking code footer").setDescription(
					"JS tracking code that is placed in the footer HTML (e.g. PiWik). This enables the cookie wall.");
		}

		private String getDefaultMenuValue()
		{
			return menuManagerServiceImpl.getDefaultMenuValue();
		}
	}

	@Override
	public String getTitle()
	{
		return getString(Meta.TITLE);
	}

	@Override
	public void setTitle(String title)
	{
		set(Meta.TITLE, title);
	}

	@Override
	public String getLogoTopHref()
	{
		return getString(Meta.LOGO_TOP_HREF);
	}

	@Override
	public void setLogoTopHref(String logoHref)
	{
		set(Meta.LOGO_TOP_HREF, logoHref);
	}

	@Override
	public String getLogoNavBarHref()
	{
		return getString(Meta.LOGO_NAVBAR_HREF);
	}

	@Override
	public void setLogoNavBarHref(String logoHref)
	{
		set(Meta.LOGO_NAVBAR_HREF, logoHref);
	}

	@Override
	public String getFooter()
	{
		return getString(Meta.FOOTER);
	}

	@Override
	public void setFooter(String footerText)
	{
		set(Meta.FOOTER, footerText);
	}

	@Override
	public boolean getSignUp()
	{
		Boolean value = getBoolean(Meta.SIGNUP);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setSignUp(boolean signUp)
	{
		set(Meta.SIGNUP, signUp);
	}

	@Override
	public boolean getSignUpModeration()
	{
		Boolean value = getBoolean(Meta.SIGNUP_MODERATION);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setSignUpModeration(boolean signUpModeration)
	{
		set(Meta.SIGNUP_MODERATION, signUpModeration);
	}

	@Override
	public String getLanguageCode()
	{
		return getString(Meta.LANGUAGE_CODE);
	}

	@Override
	public void setLanguageCode(String languageCode)
	{
		set(Meta.LANGUAGE_CODE, languageCode);
	}

	@Override
	public String getBootstrapTheme()
	{
		return getString(Meta.BOOTSTRAP_THEME);
	}

	@Override
	public void setBootstrapTheme(String bootstrapTheme)
	{
		// verify that css file exists
		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		Resource resource = resolver.getResource("/css/" + bootstrapTheme);
		if (!resource.exists())
		{
			throw new MolgenisDataException("Bootstrap theme does not exist [/css/" + bootstrapTheme + "]");
		}

		set(Meta.BOOTSTRAP_THEME, bootstrapTheme);
	}

	@Override
	public String getCssHref()
	{
		return getString(Meta.CSS_HREF);
	}

	@Override
	public void setCssHref(String cssHref)
	{
		set(Meta.CSS_HREF, cssHref);
	}

	@Override
	public String getMenu()
	{
		return getString(Meta.MENU);
	}

	@Override
	public void setMenu(String menuJson)
	{
		set(Meta.MENU, menuJson);
	}

	@Override
	public String getTrackingCodeFooter()
	{
		return getString(Meta.TRACKING_CODE_FOOTER);
	}

	@Override
	public void setTrackingCodeFooter(String trackingCodeHeader)
	{
		set(Meta.TRACKING_CODE_FOOTER, trackingCodeHeader);
	}

	@Override
	public String getGoogleAnalyticsTrackingId()
	{
		return getString(Meta.GOOGLE_ANALYTICS_TRACKING_ID);
	}

	@Override
	public void setGoogleAnalyticsTrackingId(String trackingCodeFooter)
	{
		set(Meta.GOOGLE_ANALYTICS_TRACKING_ID, trackingCodeFooter);
	}

	@Override
	public String getGoogleAnalyticsTrackingIdMolgenis()
	{
		return getString(Meta.GOOGLE_ANALYTICS_TRACKING_ID_MOLGENIS);
	}

	@Override
	public void setGoogleAnalyticsTrackingIdMolgenis(String googleAnalyticsTrackingIdMolgenis)
	{
		set(Meta.GOOGLE_ANALYTICS_TRACKING_ID_MOLGENIS, googleAnalyticsTrackingIdMolgenis);
	}

	@Override
	public Integer getAggregateThreshold()
	{
		return getInt(Meta.AGGREGATE_THRESHOLD);
	}

	@Override
	public void setAggregateThreshold(Integer threshold)
	{
		set(Meta.AGGREGATE_THRESHOLD, threshold);
	}

	@Override
	public boolean getGoogleAnalyticsIpAnonymization()
	{
		Boolean value = getBoolean(Meta.GOOGLE_ANALYTICS_IP_ANONYMIZATION);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setGoogleAnalyticsIpAnonymization(boolean googleAnalyticsIpAnonymization)
	{
		set(Meta.GOOGLE_ANALYTICS_IP_ANONYMIZATION, googleAnalyticsIpAnonymization);
	}

	@Override
	public boolean getGoogleAnalyticsAccountPrivacyFriendly()
	{
		Boolean value = getBoolean(Meta.GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setGoogleAnalyticsAccountPrivacyFriendly(boolean googleAnalyticsAccountPrivacyFriendly)
	{
		set(Meta.GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS, googleAnalyticsAccountPrivacyFriendly);
	}

	@Override
	public boolean getGoogleAnalyticsAccountPrivacyFriendlyMolgenis()
	{
		Boolean value = getBoolean(Meta.GOOGLE_ANALYTICS_ACCOUNT_PRIVACY_FRIENDLY_SETTINGS_MOLGENIS);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setGoogleSignIn(boolean googleSignIn)
	{
		set(Meta.GOOGLE_SIGN_IN, googleSignIn);
	}

	@Override
	public boolean getGoogleSignIn()
	{
		Boolean value = getBoolean(Meta.GOOGLE_SIGN_IN);
		return value != null ? value.booleanValue() : false;
	}

	@Override
	public void setGoogleAppClientId(String googleAppClientId)
	{
		set(Meta.GOOGLE_APP_CLIENT_ID, googleAppClientId);
	}

	@Override
	public String getGoogleAppClientId()
	{
		return getString(Meta.GOOGLE_APP_CLIENT_ID);
	}

	@Override
	public void setCustomJavascript(String customJavascript)
	{
		set(Meta.CUSTOM_JAVASCRIPT, customJavascript);
	}

	@Override
	public String getCustomJavascript()
	{
		return getString(Meta.CUSTOM_JAVASCRIPT);
	}
}
