package main

import (
	"context"
	"sync"
	"testing"
	"time"
)

func TestFark(t *testing.T) {
	onceki := []Surucu{{Harf: "E", Etiket: "Bellek"}, {Harf: "F", Etiket: "Kart"}}
	simdi := []Surucu{{Harf: "F", Etiket: "Kart"}, {Harf: "G", Etiket: "Yeni"}}
	takilan, cikarilan := fark(onceki, simdi)
	if len(takilan) != 1 || takilan[0].Harf != "G" {
		t.Fatalf("takılan beklenen G, gelen %+v", takilan)
	}
	if len(cikarilan) != 1 || cikarilan[0].Harf != "E" {
		t.Fatalf("çıkarılan beklenen E, gelen %+v", cikarilan)
	}
}

func TestAyarlaUygula(t *testing.T) {
	takma := Mesaj{Tip: "olay", Olay: "takildi", Surucu: &Surucu{Harf: "H", Etiket: "SD"}}
	cikarma := Mesaj{Tip: "olay", Olay: "cikarildi", Surucu: &Surucu{Harf: "H", Etiket: "SD"}}

	kapali := Ayar{TakmaBildir: false, CikarmaBildir: true, EtiketGoster: true}
	if _, ok := ayarlaUygula(takma, kapali); ok {
		t.Fatal("takma bildirimi kapalıyken olay gönderilmemeli")
	}
	if _, ok := ayarlaUygula(cikarma, kapali); !ok {
		t.Fatal("çıkarma bildirimi açıkken olay gönderilmeli")
	}

	gizliEtiket := Ayar{TakmaBildir: true, CikarmaBildir: true, EtiketGoster: false}
	m, ok := ayarlaUygula(takma, gizliEtiket)
	if !ok || m.Surucu.Etiket != "" || m.Surucu.Harf != "H" {
		t.Fatalf("etiket gizliyken etiket boş, harf korunmalı: %+v", m.Surucu)
	}
	// Orijinal mesaj degismemeli.
	if takma.Surucu.Etiket != "SD" {
		t.Fatal("ayarlaUygula orijinal mesajı değiştirmemeli")
	}
}

// izle: sahte tarayici sirayla farkli listeler dondurur; her tetikte olaylar uretilmeli.
func TestIzleTakmaVeCikarma(t *testing.T) {
	sirali := [][]Surucu{
		{{Harf: "E", Etiket: "Bellek"}},
		{{Harf: "E", Etiket: "Bellek"}, {Harf: "H", Etiket: "SD"}},
		{{Harf: "H", Etiket: "SD"}},
	}
	var tmu sync.Mutex
	sira := 0
	tara := func() ([]Surucu, error) {
		tmu.Lock()
		defer tmu.Unlock()
		if sira >= len(sirali) {
			return sirali[len(sirali)-1], nil
		}
		s := sirali[sira]
		sira++
		return s, nil
	}

	var olaylar []Mesaj
	var omu sync.Mutex
	yaz := func(m Mesaj) {
		omu.Lock()
		defer omu.Unlock()
		olaylar = append(olaylar, m)
	}
	tetik := make(chan struct{})
	ayarAl := func() Ayar { return varsayilanAyar() }

	ctx, iptal := context.WithCancel(context.Background())
	defer iptal()
	go izle(ctx, tara, yaz, tetik, ayarAl)

	bekle := func(adet int) {
		for i := 0; i < 400; i++ {
			omu.Lock()
			n := len(olaylar)
			omu.Unlock()
			if n >= adet {
				return
			}
			time.Sleep(5 * time.Millisecond)
		}
	}
	bekle(1) // durum
	tetik <- struct{}{}
	bekle(2) // + takildi
	tetik <- struct{}{}
	bekle(3) // + cikarildi
	iptal()

	omu.Lock()
	defer omu.Unlock()
	if len(olaylar) < 3 {
		t.Fatalf("durum + takıldı + çıkarıldı beklendi, gelen %d", len(olaylar))
	}
	if olaylar[0].Tip != "durum" || len(olaylar[0].Suruculer) != 1 {
		t.Fatalf("ilk mesaj başlangıç durumu olmalı: %+v", olaylar[0])
	}
	var takildi, cikarildi bool
	for _, m := range olaylar[1:] {
		if m.Tip != "olay" || m.Surucu == nil {
			continue
		}
		if m.Olay == "takildi" && m.Surucu.Harf == "H" {
			takildi = true
		}
		if m.Olay == "cikarildi" && m.Surucu.Harf == "E" {
			cikarildi = true
		}
	}
	if !takildi || !cikarildi {
		t.Fatalf("takıldı (H) ve çıkarıldı (E) olayları beklendi: %+v", olaylar)
	}
}
