package scheduler

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"local/firestormy/config"
	"local/firestormy/config/ns"
	"local/firestormy/logger"
	"os/exec"
	"strings"
	"time"

	"github.com/google/uuid"
)

type Job struct {
	Title       string
	Name        string
	Schedule    string
	Raw         string
	Runner      Runner
	Disabled    bool
	foo         func()
	LastStatus  int
	LastOutput  string
	LastRuntime time.Duration
	LastRun     time.Time
}

func NewJob(runner Runner, schedule, raw string) (*Job, error) {
	switch runner {
	case Bash:
		return newBashJob(schedule, raw)
	default:
		return nil, ErrBadRunner
	}
}

func newBashJob(schedule, sh string, title ...string) (*Job, error) {
	if !validCron(schedule) {
		return nil, ErrBadCron
	}
	j := &Job{
		Name:     uuid.New().String(),
		Schedule: schedule,
		Raw:      sh,
		Runner:   Bash,
	}
	if len(title) == 0 || len(title[0]) == 0 {
		j.Title = j.Name
	} else {
		j.Title = title[0]
	}
	j.foo = func() {
		if j.Disabled {
			return
		}
		cmd := exec.Command("bash", "-c", sh)
		j.LastRun = time.Now()
		start := time.Now()
		out, err := cmd.CombinedOutput()
		j.LastRuntime = time.Since(start)
		if err != nil {
			out = []byte(fmt.Sprintf("error running command: %v: %s", err, out))
		}
		j.LastOutput = strings.TrimSpace(string(out))
		if cmd != nil && cmd.ProcessState != nil {
			j.LastStatus = cmd.ProcessState.ExitCode()
		}
		b, err := j.Encode()
		if err == nil {
			config.Store.Set(j.Name, b, ns.Jobs...)
		}
		logger.New().Info("result", fmt.Sprintf("%+v", j))
	}
	return j, nil
}

func (j *Job) Run() {
	j.foo()
}

func (j *Job) Encode() ([]byte, error) {
	buff := bytes.NewBuffer(nil)
	encoder := gob.NewEncoder(buff)
	err := encoder.Encode(*j)
	return buff.Bytes(), err
}

func (j *Job) Decode(b []byte) error {
	buff := bytes.NewReader(b)
	decoder := gob.NewDecoder(buff)
	err := decoder.Decode(j)
	if err != nil {
		return err
	}
	k, err := NewJob(j.Runner, j.Schedule, j.Raw)
	if err == nil {
		k.Name = j.Name
		k.Title = j.Title
		k.LastStatus = j.LastStatus
		k.LastOutput = j.LastOutput
		k.LastRuntime = j.LastRuntime
		k.LastRun = j.LastRun
		k.Disabled = j.Disabled
		*j = *k
	}
	return err
}
