package deploy

import (
	"context"
	"time"

	"github.com/go-logr/logr"
)

type Deployer struct {
	logger       logr.Logger
	tasks        []Task
	results      []Result
	ignoreDelete []string
}

func NewDeployer(tasks []Task, ignoreDelete []string, logger logr.Logger) *Deployer {
	return &Deployer{
		tasks:        tasks,
		logger:       logger,
		results:      []Result{},
		ignoreDelete: ignoreDelete,
	}
}

func (d *Deployer) Deploy(ctx context.Context) []Result {
	d.logger.Info("Deployment Start")
	for _, t := range d.tasks {
		r := d.execute(ctx, t.Name(), t.Create, stepCreate)
		if r.state == stateDone || r.state == stateError {
			_ = d.execute(ctx, t.Name(), t.PostCreate, stepPostCreate)
		}
		if r.state == statePanic {
			break
		}
	}
	return d.results
}

func (d *Deployer) Destroy(ctx context.Context) []Result {
	d.logger.Info("Destroyment Start")
	// Execute the task in reverse and move first to the last.
	// Guardian task must be executed first.
	d.tasks = append(d.tasks, d.tasks[0])
	ignoreDelete := func(t string) bool {
		for _, v := range d.ignoreDelete {
			if v == t {
				return true
			}
		}
		return false
	}
	for index := range d.tasks[1:] {
		t := d.tasks[len(d.tasks)-index-1]
		if ignoreDelete(t.Name()) {
			continue
		}
		r := d.execute(ctx, t.Name(), t.Delete, stepDelete)
		if r.state == stateDone || r.state == stateError {
			d.execute(ctx, t.Name(), t.PostDelete, stepPostDelete)
		}
		if r.state == statePanic {
			break
		}
	}
	return d.results
}

func (d *Deployer) execute(ctx context.Context, task string, f func(context.Context) Result, step stepType) Result {
	d.logger.Info("Running "+string(step)+":", "task", task)
	start := time.Now()
	result := f(ctx)
	takes := time.Since(start)
	result.task = task
	result.stepType = step
	logger := d.logger.WithValues("takes", takes)
	switch result.state {
	case stateDone:
		logger.Info("Done", "task", result.task)
	default:
		logger.Info(string(result.state), "task", result.task, "reason", result.err)
	}
	d.results = append(d.results, result)
	return result
}
