package main

// Eklenti yetkileri, iki katman:
//  1) Ana anahtar (Izinler): yalnizca yerel tepsi/ayar arayuzunden acilir/kapanir.
//  2) Site yetkisi: fikoyt.com eslesmesinin, bu bilgisayarda onaylanmis ve SURELI yetkisi.
// Bir eklenti ancak iki katman da aciksa ve hiz siniri asilmamissa calisir.
// Site yetkileri profil basina tutulur (bridge-yetki.json, profil klasorunde).

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"sync"
	"time"
)

const siteYetkiSuresi = 24 * time.Hour
const dakikaSiniri = 60

// eklentiYetkiAnahtarlari: paket anahtari -> site yetki anahtari. Tek kaynak: izin verme
// (izin-iste), genel cagri (/eklenti/cagir) ve ozel uclar ayni adi kullanir. Tabloda olmayan
// eklentilerde yetki anahtari paket anahtaridir.
var eklentiYetkiAnahtarlari = map[string]string{
	"everything": "everythingAra",
}

// yetkiAnahtari: bir eklenti anahtarinin site yetki anahtarini doner.
func yetkiAnahtari(paket string) string {
	if y, ok := eklentiYetkiAnahtarlari[paket]; ok {
		return y
	}
	return paket
}

type yetkiKaydi struct {
	Anahtar string    `json:"anahtar"`
	Eslesme string    `json:"eslesme"` // eslesmenin token ozeti
	Bitis   time.Time `json:"bitis"`
}

var (
	yetkiMu sync.Mutex
	// yetkiListeler: profil anahtarina gore site yetkileri. Bos anahtar: profil secilmemis (kok klasor).
	yetkiListeler = map[string][]yetkiKaydi{}
	// yuklenenYetki: bu profilin yetkileri bellege okunmus mu (tembel yukleme).
	yuklenenYetki = map[string]bool{}
	hizMu         sync.Mutex
	hizKaydi      = map[string][]time.Time{}
)

// yetkiDosyasiProfil: verilen profilin site yetki dosyasi. Bos anahtar: kok klasor.
func yetkiDosyasiProfil(profil string) (string, error) {
	klasor, err := profilKlasorP(profil)
	if err != nil {
		return "", err
	}
	return filepath.Join(klasor, "bridge-yetki.json"), nil
}

// yetkiDosyasi: aktif profilin site yetki dosyasi.

// yetkiOkuDosya: dosyadaki yetkileri okur. Dosya yoksa bos liste.
func yetkiOkuDosya(yol string) ([]yetkiKaydi, error) {
	veri, err := os.ReadFile(yol)
	if errors.Is(err, os.ErrNotExist) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	var liste []yetkiKaydi
	if err := json.Unmarshal(veri, &liste); err != nil {
		return nil, err
	}
	return liste, nil
}

// yetkiYukleProfil: verilen profilin site yetkilerini diskten okur.
func yetkiYukleProfil(profil string) error {
	yol, err := yetkiDosyasiProfil(profil)
	if err != nil {
		return err
	}
	liste, err := yetkiOkuDosya(yol)
	if err != nil {
		return err
	}
	yetkiMu.Lock()
	yetkiListeler[profil] = suresiDolmayanlar(liste)
	yuklenenYetki[profil] = true
	yetkiMu.Unlock()
	return nil
}

// yetkiListeAlLocked: profilin yetki listesi. Bellekte yoksa diskten yuklenir. yetkiMu kilitliyken cagrilmali.
func yetkiListeAlLocked(profil string) []yetkiKaydi {
	if !yuklenenYetki[profil] {
		if yol, err := yetkiDosyasiProfil(profil); err == nil {
			if liste, err := yetkiOkuDosya(yol); err == nil {
				yetkiListeler[profil] = suresiDolmayanlar(liste)
			}
		}
		yuklenenYetki[profil] = true
	}
	return yetkiListeler[profil]
}

func suresiDolmayanlar(liste []yetkiKaydi) []yetkiKaydi {
	now := time.Now()
	out := make([]yetkiKaydi, 0, len(liste))
	for _, k := range liste {
		if k.Bitis.After(now) {
			out = append(out, k)
		}
	}
	return out
}

// yetkiKaydetLocked: profilin yetki listesini diske yazar. yetkiMu kilitliyken cagrilmali.
func yetkiKaydetLocked(profil string) error {
	yol, err := yetkiDosyasiProfil(profil)
	if err != nil {
		return err
	}
	liste := suresiDolmayanlar(yetkiListeler[profil])
	yetkiListeler[profil] = liste
	veri, err := json.MarshalIndent(liste, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(yol, veri, 0o600)
}

// yetkiVerProfil: profilde bir site icin bir anahtara sureli yetki verir (ayni kayit varsa yenilenir).
func yetkiVerProfil(profil, anahtar, eslesme string, sure time.Duration) error {
	yetkiMu.Lock()
	defer yetkiMu.Unlock()
	mevcut := yetkiListeAlLocked(profil)
	kalan := make([]yetkiKaydi, 0, len(mevcut)+1)
	for _, k := range mevcut {
		if !(k.Anahtar == anahtar && k.Eslesme == eslesme) {
			kalan = append(kalan, k)
		}
	}
	yetkiListeler[profil] = append(kalan, yetkiKaydi{Anahtar: anahtar, Eslesme: eslesme, Bitis: time.Now().Add(sure)})
	return yetkiKaydetLocked(profil)
}

// yetkiGeriAlProfil: eslesme "" ise profilde anahtarin tum site yetkilerini, degilse yalnizca o sitenin yetkisini siler.
func yetkiGeriAlProfil(profil, anahtar, eslesme string) error {
	yetkiMu.Lock()
	defer yetkiMu.Unlock()
	mevcut := yetkiListeAlLocked(profil)
	kalan := make([]yetkiKaydi, 0, len(mevcut))
	for _, k := range mevcut {
		if k.Anahtar == anahtar && (eslesme == "" || k.Eslesme == eslesme) {
			continue
		}
		kalan = append(kalan, k)
	}
	yetkiListeler[profil] = kalan
	return yetkiKaydetLocked(profil)
}

// eslesmeYetkileriniSilProfil: profilde bir eslesme kaldirildiginda o sitenin tum yetkileri silinir.
func eslesmeYetkileriniSilProfil(profil, eslesme string) error {
	yetkiMu.Lock()
	defer yetkiMu.Unlock()
	mevcut := yetkiListeAlLocked(profil)
	kalan := make([]yetkiKaydi, 0, len(mevcut))
	for _, k := range mevcut {
		if k.Eslesme != eslesme {
			kalan = append(kalan, k)
		}
	}
	yetkiListeler[profil] = kalan
	return yetkiKaydetLocked(profil)
}

// yetkiGecerliMiProfil: profilde bu site icin bu anahtarda suresi dolmamis bir yetki var mi?
func yetkiGecerliMiProfil(profil, anahtar, eslesme string) bool {
	yetkiMu.Lock()
	defer yetkiMu.Unlock()
	now := time.Now()
	for _, k := range yetkiListeAlLocked(profil) {
		if k.Anahtar == anahtar && k.Eslesme == eslesme && k.Bitis.After(now) {
			return true
		}
	}
	return false
}

// hizIzinVer: anahtar+site basina dakikada en fazla dakikaSiniri istek.
func hizIzinVer(anahtar, eslesme string) bool {
	hizMu.Lock()
	defer hizMu.Unlock()
	anahtarBirlesik := anahtar + "|" + eslesme
	simdi := time.Now()
	birDakikaOnce := simdi.Add(-time.Minute)
	kayit := hizKaydi[anahtarBirlesik][:0]
	for _, t := range hizKaydi[anahtarBirlesik] {
		if t.After(birDakikaOnce) {
			kayit = append(kayit, t)
		}
	}
	if len(kayit) >= dakikaSiniri {
		hizKaydi[anahtarBirlesik] = kayit
		return false
	}
	hizKaydi[anahtarBirlesik] = append(kayit, simdi)
	return true
}
