package repo

import (
	"fmt"
	"math"
	"time"

	"github.com/ditatompel/xmr-nodes/internal/database"
)

type CronRepository interface {
	RunCronProcess()
	Crons() ([]CronTask, error)
}

type CronRepo struct {
	db *database.DB
}

type CronTask struct {
	Id          int     `json:"id" db:"id"`
	Title       string  `json:"title" db:"title"`
	Slug        string  `json:"slug" db:"slug"`
	Description string  `json:"description" db:"description"`
	RunEvery    int     `json:"run_every" db:"run_every"`
	LastRun     int64   `json:"last_run" db:"last_run"`
	NextRun     int64   `json:"next_run" db:"next_run"`
	RunTime     float64 `json:"run_time" db:"run_time"`
	CronState   int     `json:"cron_state" db:"cron_state"`
	IsEnabled   int     `json:"is_enabled" db:"is_enabled"`
}

var rerunTimeout = 300

func NewCron(db *database.DB) CronRepository {
	return &CronRepo{db}
}

func (repo *CronRepo) RunCronProcess() {
	for {
		time.Sleep(60 * time.Second)
		fmt.Println("Running cron...")
		list, err := repo.queueList()
		if err != nil {
			fmt.Println("Error parsing to struct:", err)
			continue
		}
		for _, task := range list {
			startTime := time.Now()
			currentTs := startTime.Unix()
			delayedTask := currentTs - task.NextRun
			if task.CronState == 1 && delayedTask <= int64(rerunTimeout) {
				fmt.Println("SKIP STATE 1:", task.Slug)
				continue
			}

			repo.preRunTask(task.Id, currentTs)

			repo.execCron(task.Slug)

			runTime := math.Ceil(time.Since(startTime).Seconds()*1000) / 1000
			fmt.Println("Runtime:", runTime)
			nextRun := currentTs + int64(task.RunEvery)

			repo.postRunTask(task.Id, nextRun, runTime)
		}
		fmt.Println("Cron done!")
	}
}

func (repo *CronRepo) Crons() ([]CronTask, error) {
	tasks := []CronTask{}
	query := `SELECT * FROM tbl_cron`
	err := repo.db.Select(&tasks, query)
	return tasks, err
}

func (repo *CronRepo) queueList() ([]CronTask, error) {
	tasks := []CronTask{}
	query := `SELECT id, run_every, last_run, slug, next_run, cron_state FROM tbl_cron
    WHERE is_enabled = ? AND next_run <= ?`
	err := repo.db.Select(&tasks, query, 1, time.Now().Unix())

	return tasks, err
}

func (repo *CronRepo) preRunTask(id int, lastRunTs int64) {
	query := `UPDATE tbl_cron SET cron_state = ?, last_run = ? WHERE id = ?`
	row, err := repo.db.Query(query, 1, lastRunTs, id)
	if err != nil {
		fmt.Println("ERROR PRERUN:", err)
	}
	defer row.Close()
}

func (repo *CronRepo) postRunTask(id int, nextRun int64, runtime float64) {
	query := `UPDATE tbl_cron SET cron_state = ?, next_run = ?, run_time = ? WHERE id = ?`
	row, err := repo.db.Query(query, 0, nextRun, runtime, id)
	if err != nil {
		fmt.Println("ERROR PRERUN:", err)
	}
	defer row.Close()
}

func (repo *CronRepo) execCron(slug string) {
	switch slug {
	case "something":
		fmt.Println("Running task", slug)
		// do task
		break
	}
}