package vcs

import (
	"time"

	"github.com/pkg/errors"
	"github.com/runatlantis/atlantis/server/logging"
	"github.com/runatlantis/atlantis/server/scheduled"
)

// GithubTokenRotator continuously tries to rotate the github app access token every 30 seconds and writes the ~/.git-credentials file
type GithubTokenRotator interface {
	Run()
	GenerateJob() (scheduled.JobDefinition, error)
}

type githubTokenRotator struct {
	log               logging.SimpleLogging
	githubCredentials GithubCredentials
	githubHostname    string
	gitUser           string
	homeDirPath       string
}

func NewGithubTokenRotator(
	log logging.SimpleLogging,
	githubCredentials GithubCredentials,
	githubHostname string,
	gitUser string,
	homeDirPath string) GithubTokenRotator {

	return &githubTokenRotator{
		log:               log,
		githubCredentials: githubCredentials,
		githubHostname:    githubHostname,
		gitUser:           gitUser,
		homeDirPath:       homeDirPath,
	}
}

// make sure interface is implemented correctly
var _ GithubTokenRotator = (*githubTokenRotator)(nil)

func (r *githubTokenRotator) GenerateJob() (scheduled.JobDefinition, error) {

	return scheduled.JobDefinition{
		Job:    r,
		Period: 30 * time.Second,
	}, r.rotate()
}

func (r *githubTokenRotator) Run() {
	err := r.rotate()
	if err != nil {
		// at least log the error message here, as we want to notify the that user that the key rotation wasn't successful
		r.log.Err(err.Error())
	}
}

func (r *githubTokenRotator) rotate() error {
	r.log.Debug("Refreshing Github tokens for .git-credentials")

	token, err := r.githubCredentials.GetToken()
	if err != nil {
		return errors.Wrap(err, "Getting github token")
	}
	r.log.Debug("Token successfully refreshed")

	// https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#http-based-git-access-by-an-installation
	if err := WriteGitCreds(r.gitUser, token, r.githubHostname, r.homeDirPath, r.log, true); err != nil {
		return errors.Wrap(err, "Writing ~/.git-credentials file")
	}
	return nil
}
