package kokDB

import (
	"context"

	FikoYTCtx "server/sistem/fikoyt_ctx"
	FikoYTMap "server/sistem/fikoyt_map"

	"github.com/gofiber/fiber/v3"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// Yazma güvenliği uygulanan koleksiyonlar (paylaşım sistemi verisi)
var korunanKoleksiyonlar = map[string]bool{
	"hareketler": true,
}

// ── Generic DB uçları için beyaz liste ────────────────────────────────────────
//
// :db yolu İSTEMCİDEN geliyor ve FikoYT.Koleksiyon() yalnızca
// Database("fikoyt").Collection(db) çağırır — oturum/yonetici denetimi YAPMAZ.
// Bu haliyle oturumu olan (hatta Fsid header'ı ile erişen) herkes `goUyeler`
// koleksiyonunu parolalar dahil okuyabilir, kendini Super Yonetici yapabilir
// ve $out/$merge ile istediği koleksiyona yazabilir.
//
// dbErisimKontrol bunu kapatır: önce oturum, sonra ROL, sonra beyaz liste.
// Erişim listesi iki sınıfa ayrıldı:
//
//   • saltOkunurKoleksiyonlar — aggregate (okuma) + yazma
//   • yoneticiKoleksiyonlar  — aggregate (okuma) + yazma, ama SADECE yönetici
var saltOkunurKoleksiyonlar = map[string]bool{
	"fikocadLisanslari": true,  // uzaktan lisans/bilet deposu (üretici yazar, fikocad okur)
	"fikocadKomutlar":   true,  // uzaktan komut kuyruğu
	"fikocadOlaylar":    true,  // lisans/uzak komut audit günlüğü
	"fikocadCihazlar":   true,  // fikocad envanteri (fikocad'ın soket kaydı ayrı)
	"goCihazlar":        true,  // mevcut cihaz kaydı (üretici paneli buradan okur)
	"goSoketler":        true,  // soket<->cihaz eşlemesi
	// Tetikleyici kurallari da burada yasar: "manuel tetikle" ucu once kurali DB'den
	// okur (vt=FikoYT.sistem.tetikleyiciler), yazma bu koleksiyondadır. Erişim
	// yine de ayrı denetimli — aşağıdaki dbErisimKontrol bloğu.
	"hareketler": true,
}

// Yalnız yönetici/super yönetici erişebilir (kullanıcı verisi içerir).
var yoneticiKoleksiyonlar = map[string]bool{
	"goUyeler":   true, // kullanıcılar + PAROLALAR
	"go_sessions": true, // oturum jetonları
	"firma":      true,
	"personel":   true,
}

// dbErisimKontrol — aggregate/ekle/sil/guncelle uçlarının başında çağrılır.
// Dönüş: nil = izinli, aksi halde Fiber hatası.
//
// ⚠️ GÜVENLİK DÜZELTMESİ: önceki hâlde "saltOkunurKoleksiyonlar" listesi
//    aggregate (okuma) kadar EKLE/SİL/GÜNCELLE'ye de izin veriyordu. Böylece
//    oturumu olan HERKES fikocadLisanslari / goCihazlar / fikocadKomutlar gibi
//    koleksiyonlara yazabiliyordu — üretici/denetçi verisi tahrip edilebilir.
//    Artık iki ayrı kapı var:
//      • okuma  → beyaz listedeki herkes
//      • yazma  → SADECE yönetici/üretici (saltYazmaKoleksiyonlar)
func dbErisimKontrol(FikoYT *FikoYTCtx.FikoYTCtx, db string) error {
	return dbErisimKontrolYazma(dbErisimKontrolIzin(db), FikoYT, db, false)
}

func dbErisimKontrolIzin(db string) bool { return true }

// dbErisimKontrolYazma — writing=true ise yazma kapısı da uygulanır.
func dbErisimKontrolYazma(_ bool, FikoYT *FikoYTCtx.FikoYTCtx, db string, writing bool) error {
	if FikoYT == nil || FikoYT.AktifKullanici == nil {
		return fiber.NewError(fiber.StatusUnauthorized, "Oturum gerekli")
	}
	// Yönetici/üretici her şeyi yapabilir.
	if ureticiVeyaYoneticiMi(FikoYT) {
		return nil
	}
	if writing {
		// Yazma: yalnızca açıkça yazma yetkisi verilmiş koleksiyonlar.
		if saltYazmaKoleksiyonlar[db] {
			return nil
		}
		return fiber.NewError(fiber.StatusForbidden,
			"Bu koleksiyona yazma yetkiniz yok: "+db)
	}
	// Okuma: beyaz liste.
	if saltOkunurKoleksiyonlar[db] {
		return nil
	}
	if yoneticiKoleksiyonlar[db] {
		return nil // yukarıda yönetici kontrolü zaten geçti
	}
	// Beyaz listede yok: yeni koleksiyon eklenirse varsayılan KAPALI.
	// (Açık varsayılan kabul etmek, bir gün unutulan koleksiyonla tüm DB'yi açar.)
	return fiber.NewError(fiber.StatusForbidden, "Koleksiyon erişimi reddedildi: "+db)
}

// saltYazmaKoleksiyonlar — yazmaya açık koleksiyonlar (okuma zaten beyaz listede).
// Varsayılan KAPALI: yeni koleksiyon eklenirse yazılamaz.
var saltYazmaKoleksiyonlar = map[string]bool{
	"fikocadLisanslari": true, // lisans/bilet deposu (üretici yazar, fikocad okur)
	"fikocadKomutlar":   true, // uzaktan komut kuyruğu
	"fikocadOlaylar":    true, // audit günlüğü
	"fikocadCihazlar":   true, // cihaz envanteri
	"goCihazlar":        true, // kayıtlı cihazlar
	"goSoketler":        true, // soket<->cihaz eşlemesi
	"hareketler":        true, // paylaşım hareketleri (sahiplik kısıtı zaten uygulanır)
	"hareketlerUretici": true,
}

// ureticiVeyaYoneticiMi: yazma yetkisi olan roller.
func ureticiVeyaYoneticiMi(FikoYT *FikoYTCtx.FikoYTCtx) bool {
	if FikoYT == nil || FikoYT.AktifKullanici == nil {
		return false
	}
	return FikoYT.AktifKullanici.YetkiVarmi("Super Yonetici") ||
		FikoYT.AktifKullanici.YetkiVarmi("Yonetici") ||
		FikoYT.AktifKullanici.YetkiVarmi("Uretici") ||
		FikoYT.AktifKullanici.YetkiVarmi("Üretici")
}

// Tek dosya için üst yükleme sınırı (bayt)
const MaxDosyaBoyutu = 500 << 20 // 500 MB

func korunanKoleksiyonMu(db string) bool {
	return korunanKoleksiyonlar[db]
}

// aktifKullaniciOid: oturumdaki kullanıcının ObjectID'sini döner
func aktifKullaniciOid(FikoYT *FikoYTCtx.FikoYTCtx) (primitive.ObjectID, bool) {
	if FikoYT == nil || FikoYT.AktifKullanici == nil {
		return primitive.NilObjectID, false
	}
	switch v := FikoYT.AktifKullanici.Id.(type) {
	case primitive.ObjectID:
		return v, true
	case string:
		if oid, err := primitive.ObjectIDFromHex(v); err == nil {
			return oid, true
		}
	}
	return primitive.NilObjectID, false
}

func yoneticiMi(FikoYT *FikoYTCtx.FikoYTCtx) bool {
	return FikoYT != nil && FikoYT.AktifKullanici != nil &&
		(FikoYT.AktifKullanici.YetkiVarmi("Super Yonetici") || FikoYT.AktifKullanici.YetkiVarmi("Yonetici"))
}

func sahipEsit(sahip interface{}, oid primitive.ObjectID) bool {
	switch s := sahip.(type) {
	case primitive.ObjectID:
		return s == oid
	case string:
		return s == oid.Hex()
	}
	return false
}

// sahiplikSorgusu: kullanıcının dokunabileceği kayıtlar
// (kendi kayıtları + sahipsiz eski kayıtlar — geriye uyumluluk)
func sahiplikSorgusu(oid primitive.ObjectID) bson.M {
	return bson.M{"$or": []bson.M{
		{"sahip": oid},
		{"sahip": oid.Hex()},
		{"sahip": bson.M{"$exists": false}},
		{"sahip": nil},
	}}
}

// hareketYazmaKontrol: ekle/upsert öncesi belge bazlı yetki denetimi.
// - Oturum zorunlu.
// - _id'li istek: DB'deki mevcut kaydın sahibi başkasıysa 403 (yönetici hariç).
// - Yeni kayıt: sahip alanı boşsa sunucuda damgalanır.
//   (sahip dolu bırakılır — bildirim gibi "alıcı = sahip" akışları meşrudur.)
// Dönüş: (izinli, httpKod, mesaj)
func hareketYazmaKontrol(FikoYT *FikoYTCtx.FikoYTCtx, koleksiyon *mongo.Collection, belge FikoYTMap.FikoYTMap) (bool, int, string) {
	oid, ok := aktifKullaniciOid(FikoYT)
	if !ok {
		return false, 401, "Oturum gerekli"
	}
	if belge == nil {
		return true, 0, ""
	}
	if id, varId := belge["_id"]; varId && id != nil {
		if yoneticiMi(FikoYT) {
			return true, 0, ""
		}
		var mevcut bson.M
		err := koleksiyon.FindOne(
			context.TODO(),
			bson.M{"_id": id},
			options.FindOne().SetProjection(bson.M{"sahip": 1}),
		).Decode(&mevcut)
		if err == nil && mevcut != nil {
			if s, varS := mevcut["sahip"]; varS && s != nil && !sahipEsit(s, oid) {
				return false, 403, "Bu kaydı düzenleme yetkiniz yok"
			}
			return true, 0, ""
		}
		// Kayıt yok → upsert-insert; aşağıda damgala
	}
	if s, varS := belge["sahip"]; !varS || s == nil {
		belge["sahip"] = oid
	}
	return true, 0, ""
}

// hareketFiltreKisitla: sil/guncelle filtresine sahiplik kısıtı ekler (yönetici hariç).
// Dönüş: (kısıtlanmış filtre, izinli, httpKod, mesaj)
func hareketFiltreKisitla(FikoYT *FikoYTCtx.FikoYTCtx, filtre FikoYTMap.FikoYTMap) (interface{}, bool, int, string) {
	oid, ok := aktifKullaniciOid(FikoYT)
	if !ok {
		return nil, false, 401, "Oturum gerekli"
	}
	if yoneticiMi(FikoYT) {
		return filtre, true, 0, ""
	}
	return bson.M{"$and": []interface{}{map[string]interface{}(filtre), sahiplikSorgusu(oid)}}, true, 0, ""
}
