package slacknotifications.teamcity;

import jetbrains.buildServer.Build;
import jetbrains.buildServer.notification.Notificator;
import jetbrains.buildServer.notification.NotificatorRegistry;
import jetbrains.buildServer.responsibility.ResponsibilityEntry;
import jetbrains.buildServer.responsibility.TestNameResponsibilityEntry;
import jetbrains.buildServer.serverSide.*;
import jetbrains.buildServer.serverSide.mute.MuteInfo;
import jetbrains.buildServer.serverSide.problems.BuildProblemInfo;
import jetbrains.buildServer.tests.TestName;
import jetbrains.buildServer.users.NotificatorPropertyKey;
import jetbrains.buildServer.users.PropertyKey;
import jetbrains.buildServer.users.SUser;
import jetbrains.buildServer.util.StringUtil;
import jetbrains.buildServer.vcs.VcsRoot;
import org.apache.http.HttpStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import slacknotifications.SlackNotification;
import slacknotifications.teamcity.payload.SlackNotificationPayloadManager;
import slacknotifications.teamcity.settings.SlackNotificationMainSettings;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

public class SlackNotificator implements Notificator {

    private final SlackNotificationMainSettings mainConfig;
    private final SlackNotificationFactory notificationFactory;
    private final SlackNotificationPayloadManager payloadManager;
    private final SBuildServer buildServer;
    private ArrayList<UserPropertyInfo> userProps;
    private NotificationUtility notificationUtility;

    private static final String SLACK_USERNAME_KEY = "tcSlackNotifier.userName";
    private static final String TYPE = "tcSlackBuildNotifier";

    public static final PropertyKey USERNAME_KEY = new NotificatorPropertyKey(TYPE, SLACK_USERNAME_KEY);

    public SlackNotificator(NotificatorRegistry notificatorRegistry,
                            SBuildServer sBuildServer,
                            SlackNotificationMainSettings configSettings,
                            SlackNotificationFactory factory,
                            SlackNotificationPayloadManager manager){
        Loggers.ACTIVITIES.debug("Registering SlackNotificator...");

        userProps = new ArrayList<UserPropertyInfo>();
        userProps.add(new UserPropertyInfo(SLACK_USERNAME_KEY, "Slack Username"));
        notificatorRegistry.register(this, userProps);
        mainConfig = configSettings;
        notificationFactory = factory;
        payloadManager = manager;
        buildServer = sBuildServer;
        notificationUtility = new NotificationUtility();
    }

    public void register(){

    }

    @Override
    public void notifyBuildStarted(SRunningBuild sRunningBuild, Set<SUser> set) {
        for(SUser sUser : set){
            if(!userHasSlackNameConfigured(sUser)){
                continue;
            }
            SlackNotification slackNotification = createNotification(sUser);
            slackNotification.setPayload(payloadManager.buildStarted(sRunningBuild, getPreviousNonPersonalBuild(sRunningBuild)));
            doNotification(slackNotification);
        }
    }

    @Override
    public void notifyBuildSuccessful(SRunningBuild sRunningBuild, Set<SUser> set) {
        for(SUser sUser : set){
            if(!userHasSlackNameConfigured(sUser)){
                continue;
            }
            SlackNotification slackNotification = createNotification(sUser);
            slackNotification.setPayload(payloadManager.buildFinished(sRunningBuild, getPreviousNonPersonalBuild(sRunningBuild)));
            doNotification(slackNotification);
        }
    }

    @Override
    public void notifyBuildFailed(SRunningBuild sRunningBuild, Set<SUser> set) {
        for(SUser sUser : set){
            if(!userHasSlackNameConfigured(sUser)){
                continue;
            }
            SlackNotification slackNotification = createNotification(sUser);
            slackNotification.setPayload(payloadManager.buildFinished(sRunningBuild, getPreviousNonPersonalBuild(sRunningBuild)));
            doNotification(slackNotification);
        }
    }

    @Override
    public void notifyBuildFailedToStart(SRunningBuild sRunningBuild, Set<SUser> set) {
    }

    @Override
    public void notifyLabelingFailed(Build build, VcsRoot vcsRoot, Throwable throwable, Set<SUser> set) {

    }

    @Override
    public void notifyBuildFailing(SRunningBuild sRunningBuild, Set<SUser> set) {
        for(SUser sUser : set){
            if(!userHasSlackNameConfigured(sUser)){
                continue;
            }
            SlackNotification slackNotification = createNotification(sUser);
            slackNotification.setPayload(payloadManager.beforeBuildFinish(sRunningBuild, getPreviousNonPersonalBuild(sRunningBuild)));
            doNotification(slackNotification);
        }
    }

    @Override
    public void notifyBuildProbablyHanging(SRunningBuild sRunningBuild, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleChanged(SBuildType sBuildType, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleAssigned(SBuildType sBuildType, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleChanged(TestNameResponsibilityEntry testNameResponsibilityEntry, TestNameResponsibilityEntry testNameResponsibilityEntry1, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleAssigned(TestNameResponsibilityEntry testNameResponsibilityEntry, TestNameResponsibilityEntry testNameResponsibilityEntry1, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleChanged(Collection<TestName> collection, ResponsibilityEntry responsibilityEntry, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyResponsibleAssigned(Collection<TestName> collection, ResponsibilityEntry responsibilityEntry, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyBuildProblemResponsibleAssigned(Collection<BuildProblemInfo> collection, ResponsibilityEntry responsibilityEntry, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyBuildProblemResponsibleChanged(Collection<BuildProblemInfo> collection, ResponsibilityEntry responsibilityEntry, SProject sProject, Set<SUser> set) {

    }

    @Override
    public void notifyTestsMuted(Collection<STest> collection, MuteInfo muteInfo, Set<SUser> set) {

    }

    @Override
    public void notifyTestsUnmuted(Collection<STest> collection, MuteInfo muteInfo, SUser sUser, Set<SUser> set) {

    }

    @Override
    public void notifyBuildProblemsMuted(Collection<BuildProblemInfo> collection, MuteInfo muteInfo, Set<SUser> set) {

    }

    @Override
    public void notifyBuildProblemsUnmuted(Collection<BuildProblemInfo> collection, MuteInfo muteInfo, SUser sUser, Set<SUser> set) {

    }

    @NotNull
    @Override
    public String getNotificatorType() {
        return TYPE;
    }

    @NotNull
    @Override
    public String getDisplayName() {
        return "Slack Notifier";
    }


    private boolean userHasSlackNameConfigured(SUser sUser){
        String userName = sUser.getPropertyValue(USERNAME_KEY);

        return userName != null && StringUtil.isNotEmpty(userName);
    }

    private SlackNotification createNotification(SUser sUser){
        SlackNotification notification = notificationFactory.getSlackNotification();
        String userName = sUser.getPropertyValue(USERNAME_KEY);
        if(userName.substring(0,1) == "@"){
            userName = userName.substring(1);
        }
        notification.setChannel("@" + userName);

        notification.setTeamName(mainConfig.getTeamName());
        notification.setToken(mainConfig.getToken());
        notification.setIconUrl(mainConfig.getIconUrl());
        notification.setBotName(mainConfig.getBotName());
        notification.setEnabled(mainConfig.getEnabled());
        notification.setShowBuildAgent(mainConfig.getShowBuildAgent());
        notification.setShowBuildStatus(mainConfig.getShowBuildStatus());
        notification.setShowTitle(mainConfig.getShowTitle());
        notification.setShowElapsedBuildTime(mainConfig.getShowElapsedBuildTime());
        notification.setShowCommits(mainConfig.getShowCommits());
        notification.setMaxCommitsToDisplay(mainConfig.getMaxCommitsToDisplay());
        notification.setMentionChannelEnabled(false);
        notification.setShowFailureReason(mainConfig.getShowFailureReason());

        return notification;

    }

    @Nullable
    private SFinishedBuild getPreviousNonPersonalBuild(SRunningBuild paramSRunningBuild)
    {
        List<SFinishedBuild> localList = buildServer.getHistory().getEntriesBefore(paramSRunningBuild, false);

        for (SFinishedBuild localSFinishedBuild : localList)
            if (!(localSFinishedBuild.isPersonal())) return localSFinishedBuild;
        return null;
    }

    private void doNotification(SlackNotification notification) {
        notificationUtility.doPost(notification);
    }
}
