package com.atlassian.bitbucket.internal.stp;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.license.LicenseService;
import com.atlassian.bitbucket.mail.MailHostConfiguration;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.timezone.TimeZoneService;
import com.atlassian.bitbucket.util.NumberUtils;
import com.atlassian.extras.api.bitbucket.BitbucketServerLicense;
import com.atlassian.support.tools.spi.RootLevelSupportInfoAppender;
import com.atlassian.support.tools.spi.SupportInfoBuilder;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;

import static com.atlassian.bitbucket.internal.stp.StpKeys.*;

/**
 * Adds general information about Bitbucket to STP.
 *
 * @since 2.5
 */
public class BitbucketSupportInfo extends RootLevelSupportInfoAppender {

    private final ApplicationPropertiesService propertiesService;
    private final I18nService i18nService;
    private final LicenseService licenseService;
    private final TimeZoneService timeZoneService;

    public BitbucketSupportInfo(ApplicationPropertiesService propertiesService,
                                I18nService i18nService,
                                LicenseService licenseService,
                                TimeZoneService timeZoneService) {
        this.propertiesService = propertiesService;
        this.i18nService = i18nService;
        this.licenseService = licenseService;
        this.timeZoneService = timeZoneService;
    }

    @Override
    protected void addSupportInfo(SupportInfoBuilder builder) {
        addGeneralInformation(builder.addCategory(GENERAL_INFO));
        addFilesystemInformation(builder.addCategory(FS_INFO));
        addMailInformation(builder.addCategory(MAIL));
        addDatabaseInformation(builder.addCategory(DB));
        addHttpScmHostingInformation(builder.addCategory(HTTP_SCM_HOSTING));
    }

    private void addDatabaseInformation(SupportInfoBuilder builder) {
        builder.addValue(DB_DRIVER_NAME, propertiesService.getJdbcDriver());
        builder.addValue(DB_DRIVER_VERSION, propertiesService.getJdbcDriverVersion());
        builder.addValue(DB_CONNECTION_URL, propertiesService.getJdbcUrl());
    }

    /**
     * @param builder the builder to append home and shared home information to
     * @since 3.10
     */
    private void addFilesystemInformation(SupportInfoBuilder builder) {
        addFilesystemInformation(builder.addCategory(FS_HOME), propertiesService.getHomeDir());
        addFilesystemInformation(builder.addCategory(FS_SHARED_HOME), propertiesService.getSharedHomeDir());
    }

    /**
     * @param builder the builder to append filesystem information about the specified directory to
     * @param dir     the home directory to get details for
     * @since 3.10
     */
    private void addFilesystemInformation(SupportInfoBuilder builder, File dir) {
        builder.addValue(FS_PATH, dir.getAbsolutePath());
        try {
            FileStore store = Files.getFileStore(dir.toPath());
            //Especially on Windows it's likely that the FileStore won't have a name ("The volume in drive C
            //is unlabeled" -> No name on the FileStore). Since empty values get stripped, default the name
            //to a simple - if there isn't one
            builder.addValue(FS_NAME, StringUtils.defaultIfBlank(store.name(), "-"));
            //FileStore.toString() includes information about the store that isn't exposed via the API. While
            //there are no guarantees about what that is, or how useful it will be, capturing it could still
            //be interesting later
            builder.addValue(FS_DESCRIPTION, store.toString());
            builder.addValue(FS_SIZE_FREE, NumberUtils.formatSize(store.getUsableSpace()));
            builder.addValue(FS_SIZE_TOTAL, NumberUtils.formatSize(store.getTotalSpace()));
            //This is the big payoff, indicating such helpful things as "ext3" or "nfs" or "NTFS"
            builder.addValue(FS_TYPE, store.type());
        } catch (IOException ignored) {
            //If we can't get the FileStore information, just let it go. There's no point in appending
            //placeholder nodes; we can derive that this failed from the absence of the details
        }
    }

    private void addGeneralInformation(SupportInfoBuilder builder) {
        builder.addValue(APPLICATION_BASE_URL, String.valueOf(propertiesService.getBaseUrl()));
        builder.addValue(APPLICATION_BUILD_NUMBER, propertiesService.getBuildNumber());
        builder.addValue(APPLICATION_BUILD_TIMESTAMP, propertiesService.getBuildTimestamp().toString());
        builder.addValue(APPLICATION_BUILD_VERSION, propertiesService.getBuildVersion());
        builder.addValue(APPLICATION_DISPLAY_NAME, propertiesService.getDisplayName());
        builder.addValue(APPLICATION_HOME, propertiesService.getHomeDir().getAbsolutePath());
        builder.addValue(DEFAULT_TIME_ZONE, timeZoneService.getDefaultTimeZone().getId());
        builder.addValue(JVM_TIME_ZONE, timeZoneService.getJvmTimeZone().getId());
        builder.addValue(COMMIT_HASH, propertiesService.getCommitHash());
        builder.addValue(CAPTCHA_MAX_ATTEMPTS, String.valueOf(propertiesService.getMaxCaptchaAttempts()));
        builder.addValue(CPUS, String.valueOf(Runtime.getRuntime().availableProcessors()));

        BitbucketServerLicense license = licenseService.get();
        if (license != null) {
            builder.addValue(LICENSE_SERVER_ID, license.getServerId());
            builder.addValue(LICENSE_SEN, license.getSupportEntitlementNumber());
        }
    }

    private void addHttpScmHostingInformation(SupportInfoBuilder builder) {
        builder.addValue(HTTP_SCM_HOSTING_ENABLED, String.valueOf(propertiesService.isHttpScmHostingEnabled()));
    }

    private void addMailInformation(SupportInfoBuilder builder) {
        builder.addValue(MAIL_SERVER_ADDRESS, propertiesService.getServerEmailAddress());

        MailHostConfiguration config = propertiesService.getMailHostConfiguration();
        if (config == null) {
            String message = i18nService.getMessage("stp.properties.not.configured");
            builder.addValue(MAIL_SERVER_HOSTNAME, message);
            builder.addValue(MAIL_SERVER_PORT, message);
            builder.addValue(MAIL_USERNAME, message);
            builder.addValue(MAIL_PROTOCOL, message);
            builder.addValue(MAIL_USE_TLS, message);
            builder.addValue(MAIL_REQUIRE_TLS, message);
        } else {
            builder.addValue(MAIL_SERVER_HOSTNAME, config.getHostname());
            builder.addValue(MAIL_SERVER_PORT, String.valueOf(config.getPort()));
            builder.addValue(MAIL_USERNAME, config.getUsername());
            builder.addValue(MAIL_PROTOCOL, config.getProtocol().name());
            builder.addValue(MAIL_USE_TLS, String.valueOf(config.isUseStartTls()));
            builder.addValue(MAIL_REQUIRE_TLS, String.valueOf(config.isRequireStartTls()));
        }
    }
}
