/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Release
 * (c) Copyright IBM Corporation 2014. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 */
package com.urbancode.urelease

import java.io.File;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.text.SimpleDateFormat;

import org.apache.log4j.Logger;

import com.urbancode.release.rest.models.internal.RelatedDeployment;
import com.urbancode.release.rest.models.internal.ScheduledDeployment;
import com.urbancode.release.rest.models.internal.ScheduledDeploymentVersion;
import com.urbancode.release.rest.models.internal.Milestone;
import com.urbancode.release.rest.models.Release;
import com.urbancode.release.rest.models.Application;
import com.urbancode.release.rest.models.Version;
import com.urbancode.release.rest.models.internal.ScheduledDeploymentVersion;
import com.urbancode.release.rest.models.internal.VersionStatus;
import com.urbancode.release.rest.models.internal.Gate;
import com.urbancode.release.rest.models.internal.ApprovalSet;
import com.urbancode.release.rest.models.internal.ApprovalItem;
import com.urbancode.urelease.UrlHelper;
import com.urbancode.commons.util.query.QueryFilter.FilterType;
import com.urbancode.release.rest.framework.QueryParams.FilterClass;
import com.urbancode.release.rest.models.Release.ChangeCount;
import com.urbancode.release.rest.models.internal.DeploymentExecutionUpdate;
import groovy.time.TimeDuration;
import groovy.time.TimeCategory;
import com.urbancode.urelease.ScheduledDeploymentWithTiming;

/**
 * Utilities to assist with email sending in tests and plugins.
 * @author pcentgraf
 */
public class DeploymentTrendContextGenerator {
    protected static Logger log = Logger.getLogger(ReleaseEventContextGenerator.class);

    RelatedDeployment rd;
    String releaseServerUrl;
    def releaseIds;
    def planNameToSDMap;
    String reportUrl;

    public DeploymentTrendContextGenerator(planNameToSDMap, releaseServerUrl, reportUrl) {
        this.releaseServerUrl = releaseServerUrl
        this.planNameToSDMap = planNameToSDMap
        this.reportUrl = reportUrl
    }

    public static def queryForDeployments(FilterObj filterObj) {
        def oneWeek = 7L * 24L * 60L * 60L * 1000L;
        ScheduledDeployment query = new ScheduledDeployment();
        if (filterObj.releases) {
            query.filter("release.name", FilterClass.STRING, FilterType.IN, (String[])(filterObj.releases))
        }
        if (filterObj.phases) {
            query.filter("phase.phaseModel.name", FilterClass.STRING, FilterType.IN, (String[])(filterObj.phases))
        } else {
            query.filter("phase.phaseModel.name", FilterClass.STRING, FilterType.EQUALS, "PROD");
        }

        def startTime = System.currentTimeMillis() - oneWeek
        if (filterObj.startDate) {
            startTime = filterObj.startDate
        } 

        def endTime = System.currentTimeMillis()
        if (filterObj.endDate) {
            endTime = filterObj.endDate
        }

        query.filter("scheduledDate", FilterClass.LONG, FilterType.RANGE, startTime, endTime);
        query.format("list");

        ScheduledDeployment[] sds = query.getAll()

        def deploymentPlanNames = []

        for(def sd : sds) {
            def deploymentPlanName = sd.deploymentExecution.deploymentPlan.name
            if(!deploymentPlanNames.contains(deploymentPlanName)) {
                deploymentPlanNames << deploymentPlanName
            }
        }

        def result = [:]

        for(def depName : deploymentPlanNames) {
            result[depName] = DeploymentTrendContextGenerator.retrieveLastFiveDeploymentsOfPlanName(depName)
        }

        return result;
    }

    private static def retrieveLastFiveDeploymentsOfPlanName(planName) {
        ScheduledDeployment query = new ScheduledDeployment();
        query.filter("deploymentExecution.deploymentPlan.name", FilterClass.STRING, FilterType.EQUALS, planName);
        query.filter("scheduledDate", FilterClass.LONG, FilterType.LESS_THAN, System.currentTimeMillis());
        query.orderBy("scheduledDate", false) // The false causes results to descend (ascend = false)
        def result = query.getPage(0,4);

        return result
    }

    public def generateContext() {
        def result = [:]

        addDeploymentPlanContext(result)

        addStyleContext(result)

        result["reportUrl"] = this.reportUrl;

        return result
    }

    private addDeploymentPlanContext(context) {

        def planNameKeySet = planNameToSDMap.keySet()

        def deploymentPlans = []

        for(def planName : planNameKeySet) {
            def deploymentPlanObj = [:]
            def sdList = planNameToSDMap[planName]

            def deployments = []

            def manAutoGraph = [
                ["\"Task Type\"", "\"Manual\"", "\"Automated\"", "{ role: \"annotation\" }"]
            ]

            def plannedVsActualGraph = [
                ["\"\"", "\"Planned Duration\"", "\"Actual Duation\"", "{ role: \"annotation\" }"]
            ]

            def tagNamesToColors = [:]

            for(def sd : sdList) {
                def sdObj = [:]
                sd.format("detail")
                sd = sd.get()

                sdObj["release"] = sd.release.name
                sdObj["env"] = sd.environment.name
                sdObj["date"] = new SimpleDateFormat("MM/dd/YYYY HH:mm").format(new Date(sd.scheduledDate))
                // sdObj["date"] = sd.scheduledDate
                sdObj["url"] = UrlHelper.concatPaths(releaseServerUrl, "scheduledDeployment", sd.id)
                if(sd.deploymentExecution.status != null) {
                    sdObj["status"] = sd.deploymentExecution.status
                }

                def endTimePlanned = getPlannedEndTime(sd)

                def plannedDurationMin = 0
                if (endTimePlanned != null) {
                    sdObj["plannedDuration"] = getDurationString(new Date(endTimePlanned), new Date(sd.scheduledDate))

                    plannedDurationMin = (int)((endTimePlanned - sd.scheduledDate) / (1000 * 60))
                } else {
                    sdObj["plannedDuration"] = ""
                }

                def actualDurationMin = 0
                if (sd.deploymentExecution.endTimeActual != null) {
                    sdObj["actualDuration"] = getDurationString(new Date(sd.deploymentExecution.endTimeActual), new Date(sd.deploymentExecution.startTimeActual))

                    actualDurationMin = (int)((sd.deploymentExecution.endTimeActual - sd.deploymentExecution.startTimeActual) / (1000 * 60))
                } else {
                    sdObj["actualDuration"] = ""
                }

                if(sd.versions != null) {
                    sdObj["versionCount"] = sd.versions.length
                } else {
                    sdObj["versionCount"] = 0
                }

                sdObj["manAuto"] = getManAutoCount(sd)
                sdObj["tasksByTags"] = getTasksByTagsCount(sd)

                for (def tagName : sdObj["tasksByTags"].keySet()) {
                    if(!tagNamesToColors.containsKey(tagName)) {
                        tagNamesToColors[tagName] = sdObj["tasksByTags"][tagName].color
                    }
                }

                manAutoGraph << ["\"" + sdObj["date"] + "\"", sdObj["manAuto"].total - sdObj["manAuto"].automated, sdObj["manAuto"].automated, "\"\"" ]
                plannedVsActualGraph << ["\"" + sdObj["date"] + "\"", plannedDurationMin, actualDurationMin, "\"\"" ]
                deployments << sdObj
            }

            deploymentPlanObj["deployments"] = deployments
            deploymentPlanObj["name"] = planName
            deploymentPlanObj["tagNamesToColors"] = tagNamesToColors
            
            deploymentPlanObj["manAutoGraph"] = manAutoGraph
            deploymentPlanObj["plannedVsActualGraph"] = plannedVsActualGraph
            deploymentPlans << deploymentPlanObj
        }

        context["deploymentPlans"] = deploymentPlans
    }

    private def getManAutoCount(sd) {
        def totalCount = 0
        def automatedCount = 0
        def result = [:]
        for(def se : sd.deploymentExecution.segments) {
            for(def te: se.tasks) {
                if (te.isApplicable) {
                    totalCount++
                    if(te.automated) {
                        automatedCount++
                    }
                }
            }
        }

        def percent = 0
        if(totalCount > 0) {
            percent = Math.round(automatedCount / totalCount * 100)
        }

        result["total"] = totalCount
        result["automated"] = automatedCount
        result["percent"] = percent

        return result
    }

    private def getTasksByTagsCount(sd) {
        def result = [:]
        for(def se : sd.deploymentExecution.segments) {
            for(def te: se.tasks) {
                if (te.isApplicable) {
                    for(def tag : te.taskTags) {
                        if(result.containsKey(tag.name)) {
                            result[tag.name].count = result[tag.name].count + 1;
                        } else {
                            def tagObj = [:]
                            tagObj["color"] = tag.color
                            tagObj["count"] = 1
                            result[tag.name] = tagObj
                        }
                    }
                }
            }
        }

        return result
    }

    private def getPlannedEndTime(sd) {
        if(sd.deploymentExecution.endTimePlanned != null) {
            return sd.deploymentExecution.endTimePlanned
        }

        ScheduledDeploymentWithTiming sdwt = new ScheduledDeploymentWithTiming();

        ScheduledDeployment newSD = sdwt.getSDWithTiming(sd.id);

        return newSD.deploymentExecution.endTimePlanned
        //return result
    }

    // Normalize method to return a new TimeDuration
    private TimeDuration normalize( TimeDuration tc ) {
        return new TimeDuration( ( tc.days != 0 ? tc.days * 24 : 0 ) + tc.hours,
                        tc.minutes, tc.seconds, tc.millis )
    }

    private def getDurationString (end, start) {
        def tc = TimeCategory.minus(end, start)

        return (normalizeDigit(tc.hours) + ":" + normalizeDigit(tc.minutes) + ":" + normalizeDigit(tc.seconds))
    }

    private def normalizeDigit(digit) {
        if (digit < 10) {
            return "0" + digit
        } else {
            return digit
        }
    }

    private def addStyleContext(context) {
        context["spacerStyle"] = "height: 35px; width: 100%;"
        context["subHeadingStyle"] = "font-weight: bold;font-size: 20px;font-family: sans-serif; margin-top: 10px;"
        context["subSectionStyle"] = "margin-left: 25px; margin-top: 10px;"
        context["indentStyle"] = "margin-left: 15px;"
        context["smallHeadingStyle"] = "font-weight: bolder;font-size: 15px;font-family: sans-serif;"

        context["tableStyle"] = "border-collapse: collapse; text-align: left; width: 100%;"
        context["thStyle"] = "background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #006699), color-stop(1, #00557F) );background:-moz-linear-gradient( center top, #006699 5%, #00557F 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#006699', endColorstr='#00557F');background-color:#006699; color:#FFFFFF; font-size: 12px; font-weight: bolder; border-left: 1px solid #0070A8; width: 145px;"
        //context["thStyle"] = "background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #d3d3d3), color-stop(1, #fcfcfc) );background:-moz-linear-gradient( center top, #d3d3d3 5%, #fcfcfc 100% );filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#d3d3d3', endColorstr='#00557F');background-color:#d3d3d3; color:#FFFFFF; font-size: 12px; font-weight: bolder; border-left: 1px solid #fcfcfc; width: 145px;"
        context["thStyleTags"] = "background: #fcfcfc; background: -moz-linear-gradient(top, #fcfcfc 0%, #d3d3d3 99%); background: -webkit-linear-gradient(top, #fcfcfc 0%,#d3d3d3 99%); background: linear-gradient(to bottom, #fcfcfc 0%,#d3d3d3 99%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fcfcfc', endColorstr='#d3d3d3',GradientType=0 ); font-size: 12px; font-weight: bolder; border-left: 1px solid #FFFFFF; width: 145px; color:#000000;"
        context["trStyle"] = "padding: 3px 10px;"
        context["tdStyle"] = "padding: 3px 10px; background: #FFFFFF; color: #00496B; border-left: 1px solid #E1EEF4;font-size: 12px;font-weight: normal;"
        context["td-altStyle"] = "padding: 3px 10px; background: #E1EEF4; color: #00496B; border-left: 1px solid #E1EEF4;font-size: 12px;font-weight: normal;"
        context["redTdStyle"] = "padding: 3px 10px; background: #ff8080; color: #00496B; border-left: 1px solid #E1EEF4;font-size: 12px;font-weight: normal;"
        context["lateOutstandingStyle"] = "clear:left; font-weight: bolder; color: #cc3300; margin-top: 5px; margin-bottom: 15px;"
        context["taskCountsLabel"] = "text-align: center; font-size: 12px;font-weight: normal;"

        context["panelTitle"] = "text-align: center; color: #000000; "
    }
}
