package workerpool

import (
	"container/heap"
	"context"
	"fmt"
	"sync"
	"time"
)

type Gorev func() error

type GorevSonucu struct {
	GorevID   string
	GrupID    string
	Basarili  bool
	Hata      error
	Ilerleme  float64
	GecenSure time.Duration
}

type IsciHavuzu struct {
	isciSayisi    int
	gorevler      OncelikKuyrugu
	sonuclar      chan GorevSonucu
	eventler      chan interface{}
	gruplar       map[string]*Grup
	kilit         sync.RWMutex
	beklemeGrubu  sync.WaitGroup
	zamanAsimlari map[string]time.Duration
}

type GorevYapisi struct {
	GorevID       string
	GrupID        string
	Gorev         Gorev
	Oncelik       int
	Bagimliliklar []string
	Index         int // Oncelik kuyruğu için kullanılır
}

type Grup struct {
	GrupID   string
	Gorevler []*GorevYapisi
	Durum    string
}

type OncelikKuyrugu []*GorevYapisi

func (pq OncelikKuyrugu) Uzunluk() int { return len(pq) }

func (pq OncelikKuyrugu) DahaOnce(i, j int) bool {
	return pq[i].Oncelik > pq[j].Oncelik
}

func (pq OncelikKuyrugu) Degistir(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].Index = i
	pq[j].Index = j
}

func (pq *OncelikKuyrugu) Ekle(x interface{}) {
	n := len(*pq)
	eleman := x.(*GorevYapisi)
	eleman.Index = n
	*pq = append(*pq, eleman)
}

func (pq *OncelikKuyrugu) Cikar() interface{} {
	eski := *pq
	n := len(eski)
	eleman := eski[n-1]
	eski[n-1] = nil   // Bellek sızıntısını önler
	eleman.Index = -1 // Güvenlik için
	*pq = eski[0 : n-1]
	return eleman
}

func (pq *OncelikKuyrugu) Zirve() *GorevYapisi {
	if len(*pq) == 0 {
		return nil
	}
	return (*pq)[0]
}

func YeniIsciHavuzu(isciSayisi int) *IsciHavuzu {
	kuyruk := make(OncelikKuyrugu, 0)
	heap.Init(&kuyruk)
	return &IsciHavuzu{
		isciSayisi:    isciSayisi,
		gorevler:      kuyruk,
		sonuclar:      make(chan GorevSonucu, 100),
		eventler:      make(chan interface{}, 100),
		gruplar:       make(map[string]*Grup),
		zamanAsimlari: make(map[string]time.Duration),
	}
}

func (ih *IsciHavuzu) Baslat() {
	for i := 0; i < ih.isciSayisi; i++ {
		go ih.isci()
	}
}

func (ih *IsciHavuzu) isci() {
	for {
		ih.kilit.Lock()
		if ih.gorevler.Uzunluk() == 0 {
			ih.kilit.Unlock()
			time.Sleep(10 * time.Millisecond)
			continue
		}
		gorev := heap.Pop(&ih.gorevler).(*GorevYapisi)
		ih.kilit.Unlock()

		if !ih.bagimlilikKontrol(gorev) {
			ih.eventler <- fmt.Sprintf("Görev %s bağımlılıkları karşılanmadı", gorev.GorevID)
			continue
		}

		baslangic := time.Now()
		ctx, iptal := context.WithTimeout(context.Background(), ih.zamanAsimlari[gorev.GorevID])
		tamamlandi := make(chan error)
		go func() {
			tamamlandi <- gorev.Gorev()
		}()

		var hata error
		select {
		case <-ctx.Done():
			hata = ctx.Err()
		case hata = <-tamamlandi:
		}
		iptal()

		sonuc := GorevSonucu{
			GorevID:   gorev.GorevID,
			GrupID:    gorev.GrupID,
			Basarili:  hata == nil,
			Hata:      hata,
			GecenSure: time.Since(baslangic),
		}
		ih.sonuclar <- sonuc
		ih.eventler <- sonuc
		ih.beklemeGrubu.Done()
	}
}

func (ih *IsciHavuzu) GorevEkle(grupID, gorevID string, gorev Gorev, oncelik int, zamanAsimi time.Duration, bagimliliklar []string) {
	ih.kilit.Lock()
	defer ih.kilit.Unlock()
	gorevYapisi := &GorevYapisi{
		GorevID:       gorevID,
		GrupID:        grupID,
		Gorev:         gorev,
		Oncelik:       oncelik,
		Bagimliliklar: bagimliliklar,
	}
	if _, varMi := ih.gruplar[grupID]; !varMi {
		ih.gruplar[grupID] = &Grup{GrupID: grupID, Durum: "beklemede"}
	}
	ih.gruplar[grupID].Gorevler = append(ih.gruplar[grupID].Gorevler, gorevYapisi)
	ih.zamanAsimlari[gorevID] = zamanAsimi
	ih.beklemeGrubu.Add(1)
	heap.Push(&ih.gorevler, gorevYapisi)
	ih.eventler <- fmt.Sprintf("Görev %s Grup %s'ye eklendi", gorevID, grupID)
}

func (ih *IsciHavuzu) Bekle() {
	ih.beklemeGrubu.Wait()
	close(ih.sonuclar)
	close(ih.eventler)
}

func (ih *IsciHavuzu) Izle(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case sonuc := <-ih.sonuclar:
			fmt.Printf("Görev %s Grup %s içinde tamamlandı. Başarılı: %v, Hata: %v, Geçen Süre: %v\n", sonuc.GorevID, sonuc.GrupID, sonuc.Basarili, sonuc.Hata, sonuc.GecenSure)
		case event := <-ih.eventler:
			fmt.Printf("Etkinlik: %v\n", event)
		}
	}
}

func (ih *IsciHavuzu) GrupIptal(grupID string) {
	ih.kilit.Lock()
	defer ih.kilit.Unlock()
	if grup, varMi := ih.gruplar[grupID]; varMi {
		grup.Durum = "iptal edildi"
		for _, gorev := range grup.Gorevler {
			// Çalışan görevlerin iptal mantığı buraya eklenebilir
			ih.eventler <- fmt.Sprintf("Görev %s Grup %s içinde iptal edildi", gorev.GorevID, grupID)
		}
		ih.eventler <- fmt.Sprintf("Grup %s iptal edildi", grupID)
	}
}

func (ih *IsciHavuzu) bagimlilikKontrol(gorev *GorevYapisi) bool {
	for _, bagimli := range gorev.Bagimliliklar {
		if _, varMi := ih.zamanAsimlari[bagimli]; !varMi {
			return false
		}
	}
	return true
}

func (ih *IsciHavuzu) IsciSayisiAyarla(yeniSayi int) {
	ih.isciSayisi = yeniSayi
	ih.eventler <- fmt.Sprintf("İşçi sayısı %d olarak ayarlandı", yeniSayi)
}

func (ih *IsciHavuzu) GrupIlerleme(grupID string) float64 {
	ih.kilit.RLock()
	defer ih.kilit.RUnlock()
	if grup, varMi := ih.gruplar[grupID]; varMi {
		tamamlanan := 0
		for _, gorev := range grup.Gorevler {
			if gorev != nil {
				tamamlanan++
			}
		}
		ilerleme := float64(tamamlanan) / float64(len(grup.Gorevler)) * 100
		ih.eventler <- fmt.Sprintf("Grup %s ilerlemesi güncellendi: %.2f%%", grupID, ilerleme)
		return ilerleme
	}
	return 0
}
