package FikoYTGorevHavuzu

import (
	"sync"
	"sync/atomic"
	"time"
)

type Task func() error

type TaskStatus string

const (
	StatusPending   TaskStatus = "Pending"
	StatusRunning   TaskStatus = "Running"
	StatusCompleted TaskStatus = "Completed"
	StatusFailed    TaskStatus = "Failed"
)

type TaskInfo struct {
	ID       int
	Status   TaskStatus
	Error    error
	Duration time.Duration
	WorkerID int       // Görevi gerçekleştiren işçi
	Created  time.Time // Görev oluşturulma zamanı
	Started  time.Time // Görev başlama zamanı
	Finished time.Time // Görev bitiş zamanı
	GroupID  string    // Görevin bağlı olduğu grup
}

type WorkerPool struct {
	taskQueue  chan Task
	numWorkers int
	wg         sync.WaitGroup
	shutdown   chan struct{}
	progress   int32
	totalTasks int32

	// Streamler
	resultStream chan TaskInfo
	errorStream  chan TaskInfo
	progressChan chan int32

	// Görev bilgisi için bir map
	taskStatuses sync.Map

	// Yeni Özellikler
	durations       sync.Map // Her bir görev için çalışma süresi
	successCount    int32    // Başarılı görev sayısı
	failureCount    int32    // Başarısız görev sayısı
	averageDuration int64    // Ortalama görev süresi (nanoseconds)
	workerUsage     sync.Map // İşçilerin iş yükünü izlemek için
	startTimes      sync.Map // Görevlerin başlama zamanları

	// Grup yönetimi
	groups sync.Map // Grup ID'lerine göre görev kümeleri
}

// Yeni bir WorkerPool oluştur
func New(numWorkers int) *WorkerPool {
	return &WorkerPool{
		taskQueue:    make(chan Task, 100),
		numWorkers:   numWorkers,
		shutdown:     make(chan struct{}),
		resultStream: make(chan TaskInfo, 100),
		errorStream:  make(chan TaskInfo, 100),
		progressChan: make(chan int32, 100),
	}
}

// İşçileri başlat
func (wp *WorkerPool) Start() {
	for i := 1; i <= wp.numWorkers; i++ {
		wp.wg.Add(1)
		go wp.worker(i)
	}
}

// Yeni bir görev ekle
func (wp *WorkerPool) AddTask(task Task, taskID int, groupID string) {
	atomic.AddInt32(&wp.totalTasks, 1)
	taskInfo := TaskInfo{
		ID:      taskID,
		Status:  StatusPending,
		Created: time.Now(),
		GroupID: groupID,
	}
	wp.taskStatuses.Store(taskID, taskInfo)
	wp.taskQueue <- task
	wp.addToGroup(taskID, groupID)
}

// Gruplara görev ekle
func (wp *WorkerPool) addToGroup(taskID int, groupID string) {
	if group, ok := wp.groups.Load(groupID); ok {
		group.(*sync.Map).Store(taskID, struct{}{})
	} else {
		newGroup := &sync.Map{}
		newGroup.Store(taskID, struct{}{})
		wp.groups.Store(groupID, newGroup)
	}
}

// İşçi fonksiyonu
func (wp *WorkerPool) worker(workerID int) {
	defer wp.wg.Done()
	for {
		select {
		case task := <-wp.taskQueue:
			wp.safeExecute(task, workerID)
		case <-wp.shutdown:
			return
		}
	}
}

// Görev çalıştırma ve durum takibi
func (wp *WorkerPool) safeExecute(task Task, workerID int) {
	atomic.AddInt32(&wp.progress, 1)
	taskID := int(atomic.LoadInt32(&wp.progress))
	taskInfo := TaskInfo{
		ID:       taskID,
		Status:   StatusRunning,
		Started:  time.Now(),
		WorkerID: workerID,
	}
	wp.taskStatuses.Store(taskID, taskInfo)

	startTime := time.Now()
	if err := task(); err != nil {
		duration := time.Since(startTime)
		taskInfo := TaskInfo{
			ID:       taskID,
			Status:   StatusFailed,
			Error:    err,
			Duration: duration,
			Finished: time.Now(),
			WorkerID: workerID,
		}
		wp.taskStatuses.Store(taskID, taskInfo)
		wp.durations.Store(taskID, duration)
		wp.errorStream <- taskInfo
		atomic.AddInt32(&wp.failureCount, 1)
		wp.updateAverageDuration(duration)
		return
	}
	duration := time.Since(startTime)
	taskInfo = TaskInfo{
		ID:       taskID,
		Status:   StatusCompleted,
		Duration: duration,
		Finished: time.Now(),
		WorkerID: workerID,
	}
	wp.taskStatuses.Store(taskID, taskInfo)
	wp.durations.Store(taskID, duration)
	wp.resultStream <- taskInfo
	atomic.AddInt32(&wp.successCount, 1)
	wp.updateAverageDuration(duration)
	wp.incrementWorkerUsage(workerID)
}

// Görev akışlarını izlemek için akış sağlayıcılar
func (wp *WorkerPool) Results() <-chan TaskInfo {
	return wp.resultStream
}

func (wp *WorkerPool) Errors() <-chan TaskInfo {
	return wp.errorStream
}

func (wp *WorkerPool) Progress() <-chan int32 {
	return wp.progressChan
}

// Ortalama süreyi güncelle
func (wp *WorkerPool) updateAverageDuration(duration time.Duration) {
	totalDuration := atomic.LoadInt64(&wp.averageDuration)
	totalCount := atomic.LoadInt32(&wp.successCount) + atomic.LoadInt32(&wp.failureCount)
	newAverage := (totalDuration + int64(duration)) / int64(totalCount)
	atomic.StoreInt64(&wp.averageDuration, newAverage)
}

// İşçi kullanımını artır
func (wp *WorkerPool) incrementWorkerUsage(workerID int) {
	if val, ok := wp.workerUsage.Load(workerID); ok {
		wp.workerUsage.Store(workerID, val.(int)+1)
	} else {
		wp.workerUsage.Store(workerID, 1)
	}
}

// Ekstra metrikleri almak için fonksiyonlar
func (wp *WorkerPool) SuccessCount() int32 {
	return atomic.LoadInt32(&wp.successCount)
}

func (wp *WorkerPool) FailureCount() int32 {
	return atomic.LoadInt32(&wp.failureCount)
}

func (wp *WorkerPool) AverageDuration() time.Duration {
	return time.Duration(atomic.LoadInt64(&wp.averageDuration))
}

func (wp *WorkerPool) WorkerUsage(workerID int) int {
	if val, ok := wp.workerUsage.Load(workerID); ok {
		return val.(int)
	}
	return 0
}

func (wp *WorkerPool) TaskInfo(taskID int) (TaskInfo, bool) {
	if val, ok := wp.taskStatuses.Load(taskID); ok {
		return val.(TaskInfo), true
	}
	return TaskInfo{}, false
}

// Grup bilgisi almak için fonksiyonlar
func (wp *WorkerPool) GroupTasks(groupID string) []TaskInfo {
	if group, ok := wp.groups.Load(groupID); ok {
		var tasks []TaskInfo
		group.(*sync.Map).Range(func(key, value interface{}) bool {
			taskID := key.(int)
			if taskInfo, ok := wp.TaskInfo(taskID); ok {
				tasks = append(tasks, taskInfo)
			}
			return true
		})
		return tasks
	}
	return nil
}

func (wp *WorkerPool) Shutdown() {
	close(wp.shutdown)
	wp.wg.Wait()
}
