package main

import (
	"encoding/json"
	"strings"
	"sync"
	"time"
)

// dosya-yoneticisi eklentisinin anahtari. Bu eklentinin istekleri, eklentiye gitmeden once
// dosya izinlerinden gecer (dosya_izin.go). Diger eklentiler bu yolu kullanmaz.
const dosyaEklentiAnahtari = "dosya-yoneticisi"

// Parcali yazma oturumu: ilk parcada izin alinir, sonraki parcalar bu oturumla gelir.
const dosyaOturumSuresi = 10 * time.Minute

type dosyaYazmaOturumu struct {
	yol   string
	bitis time.Time
}

var (
	dosyaOturumMu   sync.Mutex
	dosyaOturumlari = map[string]dosyaYazmaOturumu{}
)

// yazOturumuKaydet: yeni oturum kaydeder; suresi dolmus oturumlari temizler.
func yazOturumuKaydet(oturum, yol string, simdi time.Time) {
	dosyaOturumMu.Lock()
	defer dosyaOturumMu.Unlock()
	for k, o := range dosyaOturumlari {
		if !o.bitis.After(simdi) {
			delete(dosyaOturumlari, k)
		}
	}
	dosyaOturumlari[oturum] = dosyaYazmaOturumu{yol: yol, bitis: simdi.Add(dosyaOturumSuresi)}
}

// yazOturumuDogrula: oturum var, suresi dolmamis ve ayni hedef yolu gosteriyorsa true.
func yazOturumuDogrula(oturum, yol string, simdi time.Time) bool {
	dosyaOturumMu.Lock()
	defer dosyaOturumMu.Unlock()
	o, ok := dosyaOturumlari[oturum]
	if !ok || !o.bitis.After(simdi) {
		return false
	}
	return strings.EqualFold(o.yol, yol)
}

func yazOturumuSil(oturum string) {
	dosyaOturumMu.Lock()
	defer dosyaOturumMu.Unlock()
	delete(dosyaOturumlari, oturum)
}

// dosyaIzniAl: yol icin islem izni yoksa sorar (dosyaIslemIzniIste). Hata ve red ayni sekilde doner.
func dosyaIzniAl(profil, yol, islem string) bool {
	izin, err := dosyaIslemIzniIste(profil, "fikoyt.com", yol, islem)
	return err == nil && izin
}

// dosyaCalistir: istegi once izinden gecirir, sonra eklentiyi calistirir.
// Izin yoksa kullanicidan sorulur (bu sefer / 1 saat / her zaman). Izin verilmezse eklenti calismaz.
func dosyaCalistir(profil, id, islem string, veri json.RawMessage) {
	const tur = "calistir"
	fail := func(hata string) {
		eklentiVeriSonucGonder(profil, id, tur, dosyaEklentiAnahtari, false, hata, nil)
	}
	var v struct {
		Yol    string `json:"yol"`
		Kaynak string `json:"kaynak"`
		Hedef  string `json:"hedef"`
		Oturum string `json:"oturum"`
		Ilk    bool   `json:"ilk"`
		Bitti  bool   `json:"bitti"`
	}
	if err := json.Unmarshal(veri, &v); err != nil {
		fail("gecersiz istek")
		return
	}
	if !kuruluEtkinMiProfil(profil, dosyaEklentiAnahtari) {
		fail("dosya eklentisi kurulu degil ya da kapali")
		return
	}
	simdi := time.Now()
	switch islem {
	case "listele", "oku":
		if v.Yol == "" || !dosyaIzniAl(profil, v.Yol, "oku") {
			fail("izin verilmedi")
			return
		}
	case "sil":
		if v.Yol == "" || !dosyaIzniAl(profil, v.Yol, "sil") {
			fail("izin verilmedi")
			return
		}
	case "yaz":
		if v.Yol == "" {
			fail("gecersiz istek")
			return
		}
		if v.Oturum != "" {
			if v.Ilk {
				if !dosyaIzniAl(profil, v.Yol, "yaz") {
					fail("izin verilmedi")
					return
				}
				yazOturumuKaydet(v.Oturum, v.Yol, simdi)
			} else if !yazOturumuDogrula(v.Oturum, v.Yol, simdi) {
				fail("yazma oturumu suresi doldu")
				return
			}
		} else {
			// Tek parcali yazma: oturum gerekmez, ama parca tum dosyayi icermeli.
			if !v.Bitti {
				fail("parcali yazma icin oturum gerekli")
				return
			}
			if !dosyaIzniAl(profil, v.Yol, "yaz") {
				fail("izin verilmedi")
				return
			}
		}
	case "kopyala":
		if v.Kaynak == "" || v.Hedef == "" {
			fail("gecersiz istek")
			return
		}
		if !dosyaIzniAl(profil, v.Kaynak, "oku") || !dosyaIzniAl(profil, v.Hedef, "yaz") {
			fail("izin verilmedi")
			return
		}
	default:
		fail("bu islem desteklenmiyor")
		return
	}
	yol, err := kuruluEklentiYoluProfil(profil, dosyaEklentiAnahtari)
	if err != nil {
		fail("eklenti dosyasi bulunamadi")
		return
	}
	cikti, err := eklentiCagirVarsayilan(eklentiCagri{Profil: profil, Anahtar: dosyaEklentiAnahtari, Yol: yol, Islem: islem, Veri: veri, Ayar: kayitliAyarProfil(profil, dosyaEklentiAnahtari)})
	if v.Oturum != "" && (v.Bitti || err != nil || !cikti.Tamam) {
		yazOturumuSil(v.Oturum)
	}
	if err != nil {
		fail("islem basarisiz")
		return
	}
	if !cikti.Tamam {
		fail(cikti.Hata)
		return
	}
	if len(cikti.Veri) > 768*1024 {
		fail("sonuc cok buyuk")
		return
	}
	eklentiVeriSonucGonder(profil, id, tur, dosyaEklentiAnahtari, true, "", cikti.Veri)
}
