package op

import (
	"context"
	"gardening/src/lib/uuider"
	"log"
	"sync"
	"time"
)

type OpStore struct {
	Uuider *uuider.Uuider
	Data   struct {
		opsMap        sync.Map
		cancelCleanup context.CancelFunc
	}
}

func (this *OpStore) InitOpStore() {
	ctx, cancel := context.WithCancel(context.Background())
	this.Data.cancelCleanup = cancel
	go this.startCleanup(ctx)
}

func (this *OpStore) CreateOp(title string) *Op {
	op := &Op{
		Id:        this.Uuider.GenerateUuid(),
		Completed: false,
		Success:   false,
		Error:     nil,
		Result:    nil,
		Title:     title,
		StartedAt: time.Now(),
		EndedAt:   time.Time{},
	}
	this.AddOp(op)
	return op
}

func (this *OpStore) Close() {
	if this.Data.cancelCleanup != nil {
		this.Data.cancelCleanup()
		log.Println("OpStore cleanup goroutine stopped.")
	}
}

func (this *OpStore) AddOp(op *Op) string {
	this.Data.opsMap.Store(op.Id, op)
	return op.Id
}

func (this *OpStore) GetOp(opId string) *Op {
	if actual, ok := this.Data.opsMap.Load(opId); ok {
		return actual.(*Op)
	}
	return nil
}

func (this *OpStore) RemoveOp(opId string) {
	this.Data.opsMap.Delete(opId)
	log.Printf("OpStore: Removed completed operation Id: %s\n", opId)
}

func (this *OpStore) startCleanup(ctx context.Context) {
	cleanUpInterval := time.Hour * 1
	retentionPeriod := time.Hour * 1
	ticker := time.NewTicker(cleanUpInterval)
	defer ticker.Stop()

	log.Printf("OpStore: Cleanup goroutine started with interval %s and retention period %s.\n", cleanUpInterval, retentionPeriod)

	for {
		select {
		case <-ticker.C:
			this.cleanOldCompletedOps()
		case <-ctx.Done():
			log.Println("OpStore: Cleanup goroutine received shutdown signal.")
			return
		}
	}
}

func (this *OpStore) cleanOldCompletedOps() {
	now := time.Now()
	var opsToRemove []string

	this.Data.opsMap.Range(func(key, value interface{}) bool {
		op := value.(*Op)
		op.mu.Lock()
		completed := op.Completed
		endedAt := op.EndedAt
		op.mu.Unlock()

		if completed && now.Sub(endedAt) > 1*time.Hour {
			opsToRemove = append(opsToRemove, key.(string))
		}
		return true
	})

	for _, id := range opsToRemove {
		this.RemoveOp(id)
	}
	if len(opsToRemove) > 0 {
		log.Printf("OpStore: Cleaned up %d completed operations.\n", len(opsToRemove))
	}
}
