package main

// Eklenti islemleri socket uzerinden: sunucu "FikoYT.bridge.eklenti.istegi" gonderir, bridge
// islemi yapar (kurulum/etkinlestirme/kaldirma PC'de onay ister), "FikoYT.bridge.eklenti.sonuc" ile doner.
// Kurulu eklenti listesi "FikoYT.bridge.eklenti.durum" ile sunucuya bildirilir.
// Gizli anahtar bu akista hic kullanilmaz; kimlik zaten cihaz hazir adimiyla kanitlanmistir.
//
// Bridge eklentiye ozel hicbir is bilmez. Eklentiye ozel islemler (arama, klasor acma, dosya indirme)
// "calistir" istegiyle eklentiye iletilir; eklenti kendi isini yapar.

import (
	"encoding/json"
	"errors"
	"net/http"
)

type eklentiSocketIstegi struct {
	Id      string          `json:"id"`
	Islem   string          `json:"islem"`
	Anahtar string          `json:"anahtar"`
	Surum   string          `json:"surum"`
	Sha256  string          `json:"sha256"`
	Imza    string          `json:"imza"`
	Bicim   string          `json:"bicim"`
	Acilis  bool            `json:"acilis"`
	Url     string          `json:"url"`
	Veri    json.RawMessage `json:"veri,omitempty"`
}

// eklentiDurumGonder: kurulu listeyi sunucuya yollar. Soket yoksa sessizce gecer.
func eklentiDurumGonder() {
	sokAktifMu.Lock()
	b := sokAktif
	hazir := sokHazirOldu
	sokAktifMu.Unlock()
	if b == nil || !hazir {
		return
	}
	govde, err := json.Marshal(map[string]any{"eklentiler": kuruluListeKopya()})
	if err != nil {
		return
	}
	_ = b.yaz(`42["FikoYT.bridge.eklenti.durum",` + string(govde) + `]`)
}

// eklentiSonucGonder: islem sonucunu sunucuya bildirir.
func eklentiSonucGonder(id, islem, anahtar string, tamam bool, hata string) {
	sokAktifMu.Lock()
	b := sokAktif
	hazir := sokHazirOldu
	sokAktifMu.Unlock()
	if b == nil || !hazir {
		return
	}
	govde, err := json.Marshal(map[string]any{
		"id": id, "islem": islem, "anahtar": anahtar, "tamam": tamam, "hata": hata,
	})
	if err != nil {
		return
	}
	_ = b.yaz(`42["FikoYT.bridge.eklenti.sonuc",` + string(govde) + `]`)
}

// eklentiVeriSonucGonder: veri iceren sonucu sunucuya bildirir. Veri boyutu sinirlidir.
func eklentiVeriSonucGonder(id, islem, anahtar string, tamam bool, hata string, veri json.RawMessage) {
	sokAktifMu.Lock()
	b := sokAktif
	hazir := sokHazirOldu
	sokAktifMu.Unlock()
	if b == nil || !hazir {
		return
	}
	govde, err := json.Marshal(map[string]any{
		"id": id, "islem": islem, "anahtar": anahtar, "tamam": tamam, "hata": hata, "veri": veri,
	})
	if err != nil {
		return
	}
	_ = b.yaz(`42["FikoYT.bridge.eklenti.sonuc",` + string(govde) + `]`)
}

// eklentiAramaSonucGonder: arama sonucunu (veri dahil) sunucuya bildirir.
func eklentiAramaSonucGonder(id, anahtar string, tamam bool, hata string, veri json.RawMessage) {
	eklentiVeriSonucGonder(id, "ara", anahtar, tamam, hata, veri)
}

// eklentiParcaGonder: eklentinin bir sonuc parcasini (fikoyt/parca) birlestirmeden sunucuya iletir.
// Sunucu parcalari parcaId/sira'ya gore birlestirir; sonuc mesaji parcaId tasir. Soket yoksa parca atilir.
var eklentiParcaGonder = func(params json.RawMessage) {
	sokAktifMu.Lock()
	b := sokAktif
	hazir := sokHazirOldu
	sokAktifMu.Unlock()
	if b == nil || !hazir || len(params) == 0 {
		return
	}
	_ = b.yaz(`42["FikoYT.bridge.eklenti.parca",` + string(params) + `]`)
}

// eklentiIstegiIsle: sunucudan gelen tek bir eklenti islemini uygular ve sonucunu bildirir.
func eklentiIstegiIsle(ham json.RawMessage) {
	var istek eklentiSocketIstegi
	if err := json.Unmarshal(ham, &istek); err != nil || istek.Id == "" {
		return
	}
	bitir := func(tamam bool, hata string) {
		eklentiSonucGonder(istek.Id, istek.Islem, istek.Anahtar, tamam, hata)
	}
	if !anahtarDesen.MatchString(istek.Anahtar) {
		bitir(false, "geçersiz eklenti")
		return
	}
	switch istek.Islem {
	case "kur":
		p := paketBilgisi{Anahtar: istek.Anahtar, Surum: istek.Surum, Sha256: istek.Sha256, Imza: istek.Imza, Bicim: istek.Bicim, Acilis: istek.Acilis}
		kod, err := eklentiKurOnayli(p, istek.Url)
		if kod != http.StatusOK {
			bitir(false, socketHataMetni(kod, err))
			return
		}
		bitir(true, "")
	case "etkinlestir":
		if !kuruluVarMi(istek.Anahtar) {
			bitir(false, "eklenti kurulu değil")
			return
		}
		onay, err := pcOnayi("Eklenti etkinleştirilsin mi: "+istek.Anahtar,
			"Eklenti yeniden çalışabilir hale gelir ve site yetkileriyle kullanılabilir.")
		if err != nil || !onay {
			bitir(false, onayHataMetni(err, onay))
			return
		}
		if err := kuruluEtkinAyarla(istek.Anahtar, true); err != nil {
			bitir(false, "işlem başarısız")
			return
		}
		eklentiDurumGonder()
		bitir(true, "")
	case "ara":
		// Arama: yerel arama izni acik ve eklenti etkin olmali. Onay istemez (izin zaten verildi).
		if !izinOku().EverythingAra || istek.Anahtar != "everything" || !kuruluEtkinMi(istek.Anahtar) {
			bitir(false, "bu bilgisayarda arama kapalı")
			return
		}
		yol, err := kuruluEklentiYolu(istek.Anahtar)
		if err != nil {
			bitir(false, "eklenti dosyası bulunamadı")
			return
		}
		cikti, err := eklentiCagirVarsayilan(eklentiCagri{Anahtar: istek.Anahtar, Yol: yol, Islem: "ara", Veri: istek.Veri, Ayar: kayitliAyar(istek.Anahtar)})
		if err != nil || !cikti.Tamam {
			hata := "arama başarısız"
			if err == nil {
				hata = cikti.Hata
			}
			eklentiAramaSonucGonder(istek.Id, istek.Anahtar, false, hata, nil)
			return
		}
		if len(cikti.Veri) > 768*1024 {
			eklentiAramaSonucGonder(istek.Id, istek.Anahtar, false, "sonuç çok büyük, daha kısa arayın", nil)
			return
		}
		eklentiAramaSonucGonder(istek.Id, istek.Anahtar, true, "", cikti.Veri)
	case "calistir":
		// Genel eklenti cagrisi: sayfa, kurulu bir eklentinin islemini ister. Bridge islemin icerigini bilmez;
		// eklentiyi calistirip sonucunu veriyle geri yollar. Arama izni ve etkin eklenti gerekir.
		var c struct {
			Islem string          `json:"islem"`
			Veri  json.RawMessage `json:"veri"`
		}
		if err := json.Unmarshal(istek.Veri, &c); err != nil || c.Islem == "" {
			eklentiVeriSonucGonder(istek.Id, "calistir", istek.Anahtar, false, "geçersiz istek", nil)
			return
		}
		if istek.Anahtar == dosyaEklentiAnahtari {
			dosyaCalistir(istek.Id, c.Islem, c.Veri)
			return
		}
		if !izinOku().EverythingAra || istek.Anahtar != "everything" || !kuruluEtkinMi(istek.Anahtar) {
			eklentiVeriSonucGonder(istek.Id, "calistir", istek.Anahtar, false, "bu bilgisayarda arama kapalı", nil)
			return
		}
		yol, err := kuruluEklentiYolu(istek.Anahtar)
		if err != nil {
			eklentiVeriSonucGonder(istek.Id, "calistir", istek.Anahtar, false, "eklenti dosyası bulunamadı", nil)
			return
		}
		cikti, err := eklentiCagirVarsayilan(eklentiCagri{Anahtar: istek.Anahtar, Yol: yol, Islem: c.Islem, Veri: c.Veri, Ayar: kayitliAyar(istek.Anahtar)})
		if err != nil || !cikti.Tamam {
			hata := "işlem başarısız"
			if err == nil {
				hata = cikti.Hata
			}
			eklentiVeriSonucGonder(istek.Id, "calistir", istek.Anahtar, false, hata, nil)
			return
		}
		eklentiVeriSonucGonder(istek.Id, "calistir", istek.Anahtar, true, "", cikti.Veri)
	case "acilis-kapat":
		// Acilista otomatik baslatmayi kapatir: yetki azaltma, onay gerektirmez.
		if !kuruluVarMi(istek.Anahtar) {
			bitir(false, "eklenti kurulu değil")
			return
		}
		if err := kuruluAcilistaAyarla(istek.Anahtar, false); err != nil {
			bitir(false, "işlem başarısız")
			return
		}
		olayLog("acilista otomatik baslatma kapatildi (web): %s", istek.Anahtar)
		eklentiDurumGonder()
		bitir(true, "")
	case "devre-disi":
		// Yetki azaltma: onay gerektirmez.
		if err := kuruluEtkinAyarla(istek.Anahtar, false); err != nil {
			bitir(false, "eklenti kurulu değil")
			return
		}
		olayLog("eklenti devre dışı bırakıldı (web): %s", istek.Anahtar)
		eklentiDurumGonder()
		bitir(true, "")
	case "kaldir":
		if !kuruluVarMi(istek.Anahtar) {
			bitir(false, "eklenti kurulu değil")
			return
		}
		onay, err := pcOnayi("Eklenti kaldırılsın mı: "+istek.Anahtar,
			"Eklentinin dosyaları silinir ve tüm site yetkileri kaldırılır. Bu işlem geri alınamaz.")
		if err != nil || !onay {
			bitir(false, onayHataMetni(err, onay))
			return
		}
		if err := eklentiKaldir(istek.Anahtar); err != nil {
			bitir(false, "işlem başarısız")
			return
		}
		eklentiDurumGonder()
		bitir(true, "")
	default:
		bitir(false, "bilinmeyen işlem")
	}
}

// izinDurumGonder: yerel izin durumunu sunucuya soket uzerinden bildirir (yerel izin kaynaktir).
func izinDurumGonder() {
	sokAktifMu.Lock()
	b := sokAktif
	hazir := sokHazirOldu
	sokAktifMu.Unlock()
	if b == nil || !hazir {
		return
	}
	y := izinOku()
	govde, err := json.Marshal(map[string]any{
		"izinler": map[string]bool{"everythingAra": y.EverythingAra, "catiaAc": y.CatiaAc, "usb": y.UsbBaglanti},
	})
	if err != nil {
		return
	}
	_ = b.yaz(`42["FikoYT.bridge.izin.durum",` + string(govde) + `]`)
}

// eslesmeKaldirildi: sunucuda eslesme web'den kaldirildiginda cagrilir. Oturumu kapatir ve
// soketi keser; boylece bu bilgisayar hesaba yeniden kaydolmaz. Yeniden eslesmek icin giris gerekir.
func eslesmeKaldirildi() {
	olayLog("eşleşme web'den kaldırıldı; oturum kapatıldı")
	oturumSil()
	sokAktifMu.Lock()
	b := sokAktif
	sokAktifMu.Unlock()
	if b != nil {
		_ = b.conn.Close()
	}
}

// pcOnayi: bridge penceresinde onay ister.
func pcOnayi(baslik, aciklama string) (bool, error) {
	return onayBekle(baslik, aciklama)
}

func onayHataMetni(err error, onay bool) string {
	switch {
	case errors.Is(err, errMesgul):
		return "bu bilgisayarda başka bir onay bekleniyor"
	case err != nil:
		return "onay ekranı açılamadı"
	case !onay:
		return "kullanıcı reddetti"
	}
	return ""
}

func socketHataMetni(kod int, err error) string {
	switch kod {
	case http.StatusForbidden:
		return "kullanıcı reddetti"
	case http.StatusTooManyRequests:
		return "bu bilgisayarda başka bir onay bekleniyor"
	case http.StatusBadGateway:
		return "indirilemedi"
	case http.StatusServiceUnavailable:
		return "eklenti kurulumu kapalı"
	}
	if err != nil {
		return "kurulum başarısız: " + err.Error()
	}
	return "kurulum başarısız"
}
