package main

import (
	"encoding/json"
	"errors"
	"io"
	"os"
	"path/filepath"
	"testing"
	"time"

	"fikoyt-local-bridge/eklenti_protokol"
)

// yetkiBellekSifirla: profil testleri arasi global yetki bellegini temizler.
func yetkiBellekSifirla() {
	yetkiMu.Lock()
	yetkiListeler = map[string][]yetkiKaydi{}
	yuklenenYetki = map[string]bool{}
	yetkiMu.Unlock()
}

// VerisiniSil ile cikarmada profilin bridge-yetki.json dosyasi da silinir;
// diger profilin yetki dosyasi etkilenmez.
func TestProfilCikarVerisiniSilYetkiDosyasi(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())
	yetkiBellekSifirla()

	for _, a := range []string{"ahmet", "ayse"} {
		d, err := profilKlasoru(a)
		if err != nil {
			t.Fatal(err)
		}
		icerik := `{"kullaniciAdi":"` + a + `","cerezler":[{"Name":"s","Value":"x"}]}`
		if err := os.WriteFile(filepath.Join(d, "oturum.json"), []byte(icerik), 0o600); err != nil {
			t.Fatal(err)
		}
		if err := yetkiVerProfil(a, "everything", "site-1", time.Hour); err != nil {
			t.Fatal(err)
		}
	}

	if err := profilCikar("ahmet", true); err != nil {
		t.Fatalf("verisiyle cikarma basarisiz: %v", err)
	}

	kok, _ := veriKlasoru()
	if _, err := os.Stat(filepath.Join(kok, "profiller", "ahmet")); !os.IsNotExist(err) {
		t.Errorf("ahmet klasoru ve icindeki bridge-yetki.json silinmeliydi")
	}
	// Ayse'nin yetki dosyasi silinmis olmamali.
	yol, err := yetkiDosyasiProfil("ayse")
	if err != nil {
		t.Fatal(err)
	}
	liste, err := yetkiOkuDosya(yol)
	if err != nil {
		t.Fatalf("ayse'nin yetki dosyasi okunamadi: %v", err)
	}
	if len(liste) != 1 {
		t.Errorf("ayse'nin yetkisi korunmaliydi, %d kayit bulundu", len(liste))
	}
}

// VerisiniSil false ile cikarmada yalnizca oturum gider; bridge-yetki.json kalir.
func TestProfilCikarVerisiniSilmezYetkiDosyasi(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())
	yetkiBellekSifirla()

	d, err := profilKlasoru("ahmet")
	if err != nil {
		t.Fatal(err)
	}
	icerik := `{"kullaniciAdi":"ahmet","cerezler":[{"Name":"s","Value":"x"}]}`
	if err := os.WriteFile(filepath.Join(d, "oturum.json"), []byte(icerik), 0o600); err != nil {
		t.Fatal(err)
	}
	if err := yetkiVerProfil("ahmet", "everything", "site-1", time.Hour); err != nil {
		t.Fatal(err)
	}

	if err := profilCikar("ahmet", false); err != nil {
		t.Fatalf("cikarma basarisiz: %v", err)
	}

	if _, err := os.Stat(filepath.Join(d, "oturum.json")); !os.IsNotExist(err) {
		t.Errorf("oturum silinmeliydi")
	}
	yol, err := yetkiDosyasiProfil("ahmet")
	if err != nil {
		t.Fatal(err)
	}
	_, err = yetkiOkuDosya(yol)
	if err != nil {
		t.Fatalf("yetki dosyasi korunamadi: %v", err)
	}

}
func testProfilHazirla(t *testing.T, anahtar string) string {
	t.Helper()
	d, err := profilKlasoru(anahtar)
	if err != nil {
		t.Fatal(err)
	}
	icerik := `{"kullaniciAdi":"` + anahtar + `","cerezler":[{"Name":"s","Value":"x"}]}`
	if err := os.WriteFile(filepath.Join(d, "oturum.json"), []byte(icerik), 0o600); err != nil {
		t.Fatal(err)
	}
	return d
}

func testCihazKaldirSahtele(t *testing.T, sonuc error) *string {
	t.Helper()
	onceki := cihazKaldirGonder
	t.Cleanup(func() { cihazKaldirGonder = onceki })
	cagrilan := ""
	cihazKaldirGonder = func(anahtar string) error {
		cagrilan = anahtar
		return sonuc
	}
	return &cagrilan
}

// TestProfilCikarSunucuKaldirir: profil cikarmada once sunucu cihazi kaldirilir.
// Sunucu reddederse (baska hesabin cihazi) yerel veriye dokunulmaz.
func TestProfilCikarSunucuKaldirir(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())

	d := testProfilHazirla(t, "ahmet")
	cagrilan := testCihazKaldirSahtele(t, nil)
	if err := profilCikar("ahmet", true); err != nil {
		t.Fatalf("cikarma basarisiz: %v", err)
	}
	if *cagrilan != "ahmet" {
		t.Fatalf("sunucu kaldirma cagrilmadi: %q", *cagrilan)
	}
	if _, err := os.Stat(d); !os.IsNotExist(err) {
		t.Fatalf("klasor silinmeliydi")
	}
}

// TestProfilCikarIzinleriSifirlar: cikarmada yerel tum ana izinler kapatilip diske yazilir.
func TestProfilCikarIzinleriSifirlar(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())

	testCihazKaldirSahtele(t, nil)
	d := testProfilHazirla(t, "ahmet")
	acik := Izinler{EverythingAra: true, CatiaAc: true, UsbBaglanti: true, DosyaYonetimiBitis: 1792005896}
	if err := izinleriKaydetProfil("ahmet", acik); err != nil {
		t.Fatal(err)
	}
	if err := profilCikar("ahmet", false); err != nil {
		t.Fatalf("cikarma basarisiz: %v", err)
	}
	if y := izinOkuProfil("ahmet"); y != (Izinler{}) {
		t.Fatalf("izinler sifirlanmaliydi: %+v", y)
	}
	veri, err := os.ReadFile(filepath.Join(d, "bridge-izin.json"))
	if err != nil {
		t.Fatalf("izin dosyasi okunamadi: %v", err)
	}
	var yazili Izinler
	if err := json.Unmarshal(veri, &yazili); err != nil {
		t.Fatal(err)
	}
	if yazili != (Izinler{}) {
		t.Fatalf("izin dosyasi sifirlanmaliydi: %+v", yazili)
	}
}

// TestProfilCikarSunucuReddi: sunucu kaldirma reddederse yerel oturum ve klasor korunur.
func TestProfilCikarSunucuReddi(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())

	testCihazKaldirSahtele(t, errors.New("sunucu hata dondu"))
	d := testProfilHazirla(t, "ahmet")
	if err := profilCikar("ahmet", true); err == nil {
		t.Fatal("sunucu reddi hata donmeliydi")
	}
	if _, err := os.Stat(filepath.Join(d, "oturum.json")); err != nil {
		t.Fatalf("oturum korunmaliydi: %v", err)
	}
	if _, err := os.Stat(d); err != nil {
		t.Fatalf("klasor korunmaliydi: %v", err)
	}
}

// TestProfilCikarDigerProfiliEtkilemez: bir profil cikarildiginda diger profilin
// eklenti surecleri kapatilamaz. Yalnizca cikarilan profilin surecleri durdurulur.
func TestProfilCikarDigerProfiliEtkilemez(t *testing.T) {
	t.Setenv("APPDATA", t.TempDir())
	yetkiBellekSifirla()

	// ahmet ve ayse profilleri olustur, oturumlari ekle
	for _, a := range []string{"ahmet", "ayse"} {
		d, err := profilKlasoru(a)
		if err != nil {
			t.Fatal(err)
		}
		icerik := `{"kullaniciAdi":"` + a + `","cerezler":[{"Name":"s","Value":"x"}]}`
		if err := os.WriteFile(filepath.Join(d, "oturum.json"), []byte(icerik), 0o600); err != nil {
			t.Fatal(err)
		}
	}

	// Iki proficin icin sahte eklenti oturumlari olustur
	pr1, pw1 := io.Pipe()
	pr2, pw2 := io.Pipe()
	istemci1 := protokol.Yeni(pr1, pw1, nil)
	istemci2 := protokol.Yeni(pr2, pw2, nil)

	ekMu.Lock()
	ekOturumlar["ahmet|eklenti-1"] = &ekOturum{
		tablo:   "ahmet|eklenti-1",
		anahtar: "ahmet",
		istemci: istemci1,
		kapali:  make(chan struct{}),
	}
	ekOturumlar["ayse|eklenti-1"] = &ekOturum{
		tablo:   "ayse|eklenti-1",
		anahtar: "ayse",
		istemci: istemci2,
		kapali:  make(chan struct{}),
	}
	ekMu.Unlock()

	// cihaz kaldirma cagrisini sahtele (basarili)
	testCihazKaldirSahtele(t, nil)

	// ahmet'i cikar (aktif profil)
	if err := profilCikar("ahmet", false); err != nil {
		t.Fatalf("cikarma basarisiz: %v", err)
	}

	// ahmet'in oturumu silinmeli
	dAhmet, _ := profilKlasoru("ahmet")
	if _, err := os.Stat(filepath.Join(dAhmet, "oturum.json")); !os.IsNotExist(err) {
		t.Errorf("ahmet'in oturumu silinmeliydi")
	}

	// ayse'nin oturumu korunmali
	dAyse, _ := profilKlasoru("ayse")
	if _, err := os.Stat(filepath.Join(dAyse, "oturum.json")); err != nil {
		t.Errorf("ayse'nin oturumu korunmaliydi: %v", err)
	}

	// ahmet'in protokol istemcisi kapatilmali (Kapat cagrildi)
	if err := istemci1.Bildir("test", nil); err != protokol.ErrKapali {
		t.Errorf("ahmet'in protokol istemcisi kapatilmamis olmaliydi, hata: %v", err)
	}

	// ayse'nin protokol istemcisi acik kalmali (Kapat cagrilmamis)
	if err := istemci2.Bildir("test", nil); err == protokol.ErrKapali {
		t.Errorf("ayse'nin protokol istemcisi kapatilmamis olmaliydi")
	}

	// Temizlik: protokol istemcilerini kapat
	istemci1.Kapat()
	istemci2.Kapat()
}
