// Copyright 2017 HootSuite Media Inc.
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Modified hereafter by contributors to runatlantis/atlantis.

package events

import (
	"fmt"
	"strings"

	"github.com/runatlantis/atlantis/server/events/command"
	"github.com/runatlantis/atlantis/server/events/models"
	"github.com/runatlantis/atlantis/server/events/vcs"
)

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_commit_status_updater.go CommitStatusUpdater

// CommitStatusUpdater updates the status of a commit with the VCS host. We set
// the status to signify whether the plan/apply succeeds.
type CommitStatusUpdater interface {
	// UpdateCombined updates the combined status of the head commit of pull.
	// A combined status represents all the projects modified in the pull.
	UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, cmdName command.Name) error
	// UpdateCombinedCount updates the combined status to reflect the
	// numSuccess out of numTotal.
	UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, cmdName command.Name, numSuccess int, numTotal int) error
	// UpdateProject sets the commit status for the project represented by
	// ctx.
	UpdateProject(ctx command.ProjectContext, cmdName command.Name, status models.CommitStatus, url string) error
}

// DefaultCommitStatusUpdater implements CommitStatusUpdater.
type DefaultCommitStatusUpdater struct {
	Client vcs.Client
	// StatusName is the name used to identify Atlantis when creating PR statuses.
	StatusName string
}

func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, cmdName command.Name) error {
	src := fmt.Sprintf("%s/%s", d.StatusName, cmdName.String())
	var descripWords string
	switch status {
	case models.PendingCommitStatus:
		descripWords = "in progress..."
	case models.FailedCommitStatus:
		descripWords = "failed."
	case models.SuccessCommitStatus:
		descripWords = "succeeded."
	}
	descrip := fmt.Sprintf("%s %s", strings.Title(cmdName.String()), descripWords)
	return d.Client.UpdateStatus(repo, pull, status, src, descrip, "")
}

func (d *DefaultCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, cmdName command.Name, numSuccess int, numTotal int) error {
	src := fmt.Sprintf("%s/%s", d.StatusName, cmdName.String())
	cmdVerb := "unknown"

	switch cmdName {
	case command.Plan:
		cmdVerb = "planned"
	case command.PolicyCheck:
		cmdVerb = "policies checked"
	case command.Apply:
		cmdVerb = "applied"
	}

	return d.Client.UpdateStatus(repo, pull, status, src, fmt.Sprintf("%d/%d projects %s successfully.", numSuccess, numTotal, cmdVerb), "")
}

func (d *DefaultCommitStatusUpdater) UpdateProject(ctx command.ProjectContext, cmdName command.Name, status models.CommitStatus, url string) error {
	projectID := ctx.ProjectName
	if projectID == "" {
		projectID = fmt.Sprintf("%s/%s", ctx.RepoRelDir, ctx.Workspace)
	}
	src := fmt.Sprintf("%s/%s: %s", d.StatusName, cmdName.String(), projectID)
	var descripWords string
	switch status {
	case models.PendingCommitStatus:
		descripWords = "in progress..."
	case models.FailedCommitStatus:
		descripWords = "failed."
	case models.SuccessCommitStatus:
		descripWords = "succeeded."
	}

	descrip := fmt.Sprintf("%s %s", strings.Title(cmdName.String()), descripWords)
	return d.Client.UpdateStatus(ctx.BaseRepo, ctx.Pull, status, src, descrip, url)
}
