package ruby_grpc

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"code.justin.tv/dta/twitch-create-service/internal/bootstrap/structs"
	gh "code.justin.tv/dta/twitch-create-service/internal/github"
	"code.justin.tv/dta/twitch-create-service/internal/templates"
	"github.com/google/go-github/github"
)

type App struct {
	client  *github.Client
	repo    *github.Repository
	verbose bool
}

type appTemplate struct {
	ServerClassName string
	Owner           string
	Name            string
}

var _ structs.AppType = (*App)(nil)

func (a *App) SetClient(client *github.Client, repo *github.Repository, verbose bool) {
	a.client = client
	a.repo = repo
	a.verbose = verbose
}

func (a *App) PreflightCheck() error {
	return nil
}

func (a *App) MakeUsable() error {
	commit, err := gh.PrepareTemplatesForCommit([]gh.TemplateToCommit{
		{
			TemplatePath: "internal/templates/ruby_grpc/project/Vagrantfile.tmpl",
			RepoPath:     "Vagrantfile",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/project/vagrant.sh.tmpl",
			RepoPath:     "scripts/vagrant.sh",
		},
	}, a.repo)
	if err != nil {
		return err
	}

	err = gh.CommitFilesToBranch(a.client, a.repo, "master", "Setup dev env", commit)
	if err != nil {
		return err
	}

	return nil
}

func (a *App) MakeBuildable() error {
	commit, err := gh.PrepareTemplatesForCommit([]gh.TemplateToCommit{
		{
			TemplatePath: "internal/templates/ruby_grpc/project/jenkins.groovy.tmpl",
			RepoPath:     "jenkins.groovy",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/project/build.json.tmpl",
			RepoPath:     "build.json",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/project/deploy.json.tmpl",
			RepoPath:     "deploy.json",
		},
	}, a.repo)
	if err != nil {
		return err
	}

	err = gh.CommitFilesToBranch(a.client, a.repo, "master", "Define build job", commit)
	if err != nil {
		return err
	}

	wait := 1 * time.Minute

	log.Printf("Waiting for Jenkins job to exist (%v)", (wait * 2).String())

	time.Sleep(wait)

	jenkinsTmpl, err := templates.Asset("internal/templates/ruby_grpc/project/jenkins.groovy.tmpl")
	if err != nil {
		return err
	}

	err = gh.CommitFilesToBranch(a.client, a.repo, "master", "noop to trigger dsl job again", []gh.CommitTemplate{
		{
			Path: "jenkins.groovy",
			T:    string(jenkinsTmpl) + " ",
			Data: a.repo,
		},
	})
	if err != nil {
		return err
	}

	time.Sleep(wait)

	return nil
}

func (a *App) prepareAppTemplate() appTemplate {
	name := *a.repo.Name
	capsName := strings.Replace(name, "-", "", -1)
	upper := strings.ToUpper(string(capsName[0]))
	capsName = upper + capsName[1:]

	return appTemplate{
		ServerClassName: capsName,
		Owner:           *a.repo.Owner.Login,
		Name:            *a.repo.Name,
	}
}

func (a *App) CreateApp(user, token string, tmpRoot string) error {
	data := a.prepareAppTemplate()

	commit, err := gh.PrepareTemplatesForCommit([]gh.TemplateToCommit{
		{
			TemplatePath: "internal/templates/ruby_grpc/app/bin/main.rb.tmpl",
			RepoPath:     "bin/main.rb",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/app/Gemfile.tmpl",
			RepoPath:     "Gemfile",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/app/Gemfile.lock.tmpl",
			RepoPath:     "Gemfile.lock",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/app/gemspec.tmpl",
			RepoPath:     fmt.Sprintf("%v.gemspec", data.Name),
		},
	}, data)
	if err != nil {
		return err
	}

	err = gh.CommitFilesToBranch(a.client, a.repo, "master", "Simple ruby gRPC application", commit)
	if err != nil {
		return err
	}

	return nil
}

func (a *App) CreatePuppetModule() error {
	t := structs.PuppetModuleTemplate{
		PuppetModuleName: strings.Replace(*a.repo.Name, "-", "_", -1),
		Name:             *a.repo.Name,
		FullName:         *a.repo.FullName,
	}

	commit, err := gh.PrepareTemplatesForCommit([]gh.TemplateToCommit{
		{
			TemplatePath: "internal/templates/ruby_grpc/puppet/manifests/init.pp.tmpl",
			RepoPath:     fmt.Sprintf("modules/%v/manifests/init.pp", t.PuppetModuleName),
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/puppet/manifests/params.pp.tmpl",
			RepoPath:     fmt.Sprintf("modules/%v/manifests/params.pp", t.PuppetModuleName),
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/puppet/manifests/install.pp.tmpl",
			RepoPath:     fmt.Sprintf("modules/%v/manifests/install.pp", t.PuppetModuleName),
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/puppet/hiera.yaml.tmpl",
			RepoPath:     fmt.Sprintf("hiera/cluster/%v.yaml", t.Name),
		},
	}, t)
	if err != nil {
		return err
	}

	puppetRepo, _, err := a.client.Repositories.Get("systems", "puppet")
	if err != nil {
		return err
	}

	exists := gh.FileExists(a.client, puppetRepo, fmt.Sprintf("modules/%v/manifests/init.pp", t.PuppetModuleName))

	if !exists {
		err = gh.CommitViaPullRequest(a.client, puppetRepo, *a.repo.Name, fmt.Sprintf("twitch-create-service-%v", rand.Int()), "new app puppet module", commit)
		if err != nil {
			return err
		}
	}

	return nil
}

func (a *App) CreateTerraformFiles(user, org string, awsAccounts map[string]string, subnetList string) error {
	terra := structs.TerraformTemplate{
		PuppetModuleName: strings.Replace(*a.repo.Name, "-", "_", -1),
		Name:             *a.repo.Name,
		FullName:         *a.repo.FullName,
		User:             user,
		DNSSafeName:      strings.Replace(*a.repo.Name, "_", "-", -1),
		Environment:      "production",
		AWSAccounts:      awsAccounts,
		Org:              org,
		TerraformSubnetListName: subnetList,
	}

	commit, err := gh.PrepareTemplatesForCommit([]gh.TemplateToCommit{
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/asg_puppetizer.template.tmpl",
			RepoPath:     "terraform/templates/asg_puppetizer.template",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/canary.tf.tmpl",
			RepoPath:     "terraform/production/app/canary.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/cloudwatch.tf.tmpl",
			RepoPath:     "terraform/production/app/cloudwatch.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/iam.tf.tmpl",
			RepoPath:     "terraform/production/app/iam.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/main.tf.tmpl",
			RepoPath:     "terraform/production/app/main.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/outputs.tf.tmpl",
			RepoPath:     "terraform/production/app/outputs.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/provider.tf.tmpl",
			RepoPath:     "terraform/production/app/provider.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/remotes.tf.tmpl",
			RepoPath:     "terraform/production/app/remotes.tf",
		},
		{
			TemplatePath: "internal/templates/ruby_grpc/terraform/variables.tf.tmpl",
			RepoPath:     "terraform/production/app/variables.tf",
		},
	}, terra)
	if err != nil {
		return err
	}

	err = gh.CommitFilesToBranch(a.client, a.repo, "master", "Production infrastructure", commit)
	if err != nil {
		return err
	}

	return nil
}
