package cron

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"strings"

	appjson "github.com/dokku/dokku/plugins/app-json"
	"github.com/dokku/dokku/plugins/common"

	"github.com/multiformats/go-base36"
	cronparser "github.com/robfig/cron/v3"
)

type TemplateCommand struct {
	ID         string `json:"id"`
	App        string `json:"app"`
	Command    string `json:"command"`
	Schedule   string `json:"schedule"`
	AltCommand string `json:"-"`
	LogFile    string `json:"-"`
}

func (t TemplateCommand) CronCommand() string {
	if t.AltCommand != "" {
		if t.LogFile != "" {
			return fmt.Sprintf("%s &>> %s", t.AltCommand, t.LogFile)
		}
		return t.AltCommand
	}

	return fmt.Sprintf("dokku run --cron-id %s %s %s", t.ID, t.App, t.Command)
}

func FetchCronEntries(appName string) ([]TemplateCommand, error) {
	commands := []TemplateCommand{}
	appjsonPath := appjson.GetAppjsonPath(appName)
	if !common.FileExists(appjsonPath) {
		return commands, nil
	}

	b, err := ioutil.ReadFile(appjsonPath)
	if err != nil {
		return commands, fmt.Errorf("Cannot read app.json file for %s: %v", appName, err)
	}

	if strings.TrimSpace(string(b)) == "" {
		return commands, nil
	}

	var appJSON appjson.AppJSON
	if err = json.Unmarshal(b, &appJSON); err != nil {
		return commands, fmt.Errorf("Cannot parse app.json for %s: %v", appName, err)
	}

	for _, c := range appJSON.Cron {
		parser := cronparser.NewParser(cronparser.Minute | cronparser.Hour | cronparser.Dom | cronparser.Month | cronparser.Dow | cronparser.Descriptor)
		_, err := parser.Parse(c.Schedule)
		if err != nil {
			return commands, err
		}

		commands = append(commands, TemplateCommand{
			App:      appName,
			Command:  c.Command,
			Schedule: c.Schedule,
			ID:       GenerateCommandID(appName, c),
		})
	}

	return commands, nil
}

// GenerateCommandID creates a unique ID for a given app/command/schedule combination
func GenerateCommandID(appName string, c appjson.CronCommand) string {
	return base36.EncodeToStringLc([]byte(appName + "===" + c.Command + "===" + c.Schedule))
}
