package exmonitor

import (
	"log"
	"time"

	"github.com/robfig/cron/v3"
)

const (
	K_FIELD_MIN   = time.Minute
	K_FIELD_HOUR  = time.Hour
	K_FIELD_DAY   = 24 * time.Hour
	K_FIELD_WEEK  = 7 * 24 * time.Hour
	K_FIELD_MONTH = 30 * 24 * time.Hour
	K_FIELD_YEAR  = 365 * 24 * time.Hour
)

type CoinProcessorFactory interface {
	GetCoinProcessor(symbol, baseCoin string) *DefaultCoinProcessor
	GetProcessorMap() map[string]*DefaultCoinProcessor
}

type KLineGeneratorJob struct {
	ProcessorFactory CoinProcessorFactory
}

func NewKLineGeneratorJob(factory CoinProcessorFactory, service MarketService) *KLineGeneratorJob {
	return &KLineGeneratorJob{
		ProcessorFactory: factory,
	}
}

func (job *KLineGeneratorJob) Handle5MinKLine() {
	now := time.Now()
	log.Printf("Minute KLine: %v", now)

	// Set seconds and milliseconds to 0
	truncatedTime := now.Truncate(time.Minute)
	minute := truncatedTime.Minute()
	hour := truncatedTime.Hour()

	for symbol, processor := range job.ProcessorFactory.GetProcessorMap() {
		if !processor.IsStopKline() {
			log.Printf("Generating 1-minute KLine for %s", symbol)
			processor.AutoGenerate()
			processor.Update24HVolume(truncatedTime.UnixMilli())

			if minute%5 == 0 {
				processor.GenerateKLine(5, K_FIELD_MIN, truncatedTime.UnixMilli())
			}
			if minute%10 == 0 {
				processor.GenerateKLine(10, K_FIELD_MIN, truncatedTime.UnixMilli())
			}
			if minute%15 == 0 {
				processor.GenerateKLine(15, K_FIELD_MIN, truncatedTime.UnixMilli())
			}
			if minute%30 == 0 {
				processor.GenerateKLine(30, K_FIELD_MIN, truncatedTime.UnixMilli())
			}
			if hour == 0 && minute == 0 {
				processor.ResetThumb()
			}
		}
	}
}

func (job *KLineGeneratorJob) HandleHourKLine() {
	now := time.Now()
	log.Printf("Hour KLine: %v", now)

	// Set minutes, seconds, and milliseconds to 0
	truncatedTime := now.Truncate(time.Hour)

	for _, processor := range job.ProcessorFactory.GetProcessorMap() {
		if !processor.IsStopKline() {
			processor.GenerateKLine(1, K_FIELD_HOUR, truncatedTime.UnixMilli())
		}
	}
}

func (job *KLineGeneratorJob) HandleDayKLine() {
	now := time.Now()
	log.Printf("Day KLine: %v", now)

	// Set hours, minutes, seconds, and milliseconds to 0
	truncatedTime := now.Truncate(24 * time.Hour)
	week := int(truncatedTime.Weekday())
	dayOfMonth := truncatedTime.Day()

	for _, processor := range job.ProcessorFactory.GetProcessorMap() {
		if !processor.IsStopKline() {
			if week == 0 { // Sunday
				processor.GenerateKLine(1, K_FIELD_WEEK, truncatedTime.UnixMilli())
			}
			if dayOfMonth == 1 {
				processor.GenerateKLine(1, K_FIELD_MONTH, truncatedTime.UnixMilli())
			}
			processor.GenerateKLine(1, K_FIELD_YEAR, truncatedTime.UnixMilli())
		}
	}
}

func (job *KLineGeneratorJob) Start() {
	c := cron.New()

	// Schedule tasks
	c.AddFunc("0 * * * *", job.Handle5MinKLine) // Every minute
	c.AddFunc("0 0 * * *", job.HandleHourKLine) // Every hour
	c.AddFunc("0 0 0 * *", job.HandleDayKLine)  // Every day at midnight

	// Start the cron scheduler
	c.Start()
}
