package com.atlassian.stash.internal.web.soy;

import com.atlassian.bitbucket.auth.AuthenticationContext;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.timezone.UserTimeZoneSupplier;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.soy.springmvc.InjectedDataFactory;
import com.atlassian.stash.internal.ApplicationConstants;
import com.atlassian.stash.internal.locale.InternalLocaleService;
import com.atlassian.stash.internal.web.timezone.TimeZoneOffsetHelper;
import com.atlassian.stash.internal.web.util.xsrf.XsrfTokenInterceptor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Map;
import java.util.function.Supplier;

@Component
public class DefaultInjectedDataFactory implements InjectedDataFactory {

    private final ApplicationPropertiesService propertiesService;
    private final AuthenticationContext authenticationContext;
    private final InternalLocaleService localeService;
    private final UserTimeZoneSupplier userTimeZoneSupplier;
    private final Supplier<Instant> nowSupplier;

    @Autowired
    public DefaultInjectedDataFactory(ApplicationPropertiesService propertiesService,
                                      AuthenticationContext authenticationContext,
                                      InternalLocaleService localeService,
                                      UserTimeZoneSupplier userTimeZoneSupplier) {
        this(propertiesService, authenticationContext, localeService, userTimeZoneSupplier, Instant::now);
    }

    @VisibleForTesting
    DefaultInjectedDataFactory(ApplicationPropertiesService propertiesService,
                               AuthenticationContext authenticationContext,
                               InternalLocaleService localeService,
                               UserTimeZoneSupplier userTimeZoneSupplier,
                               Supplier<Instant> nowSupplier) {
        this.authenticationContext = authenticationContext;
        this.localeService = localeService;
        this.propertiesService = propertiesService;
        this.userTimeZoneSupplier = userTimeZoneSupplier;
        this.nowSupplier = nowSupplier;
    }

    @Override
    public Map<String, Object> createInjectedData(HttpServletRequest request) {
        final Map<String, Object> injectedData = Maps.newHashMap();

        ApplicationUser currentUser = authenticationContext.getCurrentUser();
        injectedData.put("principal", currentUser);

        ZoneId userTz = userTimeZoneSupplier.getTimeZone();
        long offset = TimeZoneOffsetHelper.toGmtDifferenceInMinutes(userTz, nowSupplier.get());

        injectedData.put("timezone", offset);

        // properties shown in the title and footer of every page. It can be null during mirror setup.
        String displayName = propertiesService.getDisplayName();
        if (displayName == null) {
            displayName = ApplicationConstants.PRODUCT_NAME;
        }
        injectedData.put("instanceName", displayName);

        // used in the user level lozenge
        injectedData.put("productName", ApplicationConstants.PRODUCT_NAME);

        // this needs to seeded from somewhere for i18n
        injectedData.put("language", localeService.getLocale().getLanguage());

        injectedData.put(XsrfTokenInterceptor.XSRF_TOKEN_NAME, request.getAttribute(XsrfTokenInterceptor.XSRF_TOKEN_NAME));
        injectedData.put(XsrfTokenInterceptor.XSRF_TOKEN_VALUE, request.getAttribute(XsrfTokenInterceptor.XSRF_TOKEN_VALUE));

        // Inject the connect host base URL if set on the request.
        Object connectHostUrl = request.getAttribute("connectHostUrl");
        if (connectHostUrl instanceof String) {
            injectedData.put("connectHostUrl", connectHostUrl);
        }
        return injectedData;
    }
}
