package kok

import (
	"context"
	"fmt"
	"strings"
	"time"

	"server/sistem/kokDB"
	FikoYTCtx "server/sistem/fikoyt_ctx"
	FikoYTHata "server/sistem/fikoyt_ctx/fikoyt_hata"

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

// anketKimlikMetni: _id değerini (ObjectID ya da metin) karşılaştırılabilir metne çevirir
func anketKimlikMetni(v interface{}) string {
	switch t := v.(type) {
	case primitive.ObjectID:
		return t.Hex()
	case string:
		return t
	}
	return ""
}

// anketGorunebilirMi: oy verilebilecek kart mı?
// Herkese açık kartlar ve kart sahibi için izin verilir. Daha kısıtlı gizlilik seviyelerinde
// (takipçi, grup, liste vb.) oy yalnızca kart sahibinden gelebilir (muhafazakâr).
func anketGorunebilirMi(kart bson.M, kullanici string) bool {
	if g, _ := kart["gizlilik"].(string); g == "Herkese Açık" {
		return true
	}
	return anketKimlikMetni(kart["sahip"]) == kullanici
}

// anketHata: projenin standart hata zarfıyla (FikoYT.Cevap) yanıt döner.
func anketHata(FikoYT *FikoYTCtx.FikoYTCtx, kod int, mesaj string) error {
	FikoYT.Cevap.AyarlaDurumKodu(kod)
	FikoYT.Cevap.Durum.Hatalar.Ekle(FikoYTHata.YeniFikoYTHataKomutCalistirilirkenHata(mesaj, nil).FikoYTHata)
	return nil
}

// fikoyt_sistem_anket_oy: kullanıcının anket seçimlerini kaydeder.
// Veri modeli: kart belgesi (hareketler) içeriklerin _id referanslarını veri.icerikler'de tutar;
// her içerik ayrı bir belgedir (hareketler) ve anket ek.anket altında durur.
// secenekIds, kullanıcının o anki TÜM seçimleridir: önceki oyu değiştirir, boş dizi oyu geri çeker.
func fikoyt_sistem_anket_oy(c fiber.Ctx) error {
	FikoYT := c.Locals("FikoYTCtx").(*FikoYTCtx.FikoYTCtx)
	if FikoYT.AktifKullanici == nil {
		return anketHata(FikoYT, 401, "Oy vermek için giriş yapmalısınız")
	}
	// Kimlik düz hex metin olarak saklanır (ObjectID("...") biçimi karşılaştırmayı bozar)
	kullanici := anketKimlikMetni(FikoYT.AktifKullanici.Id)
	if kullanici == "" {
		kullanici = fmt.Sprintf("%v", FikoYT.AktifKullanici.Id)
	}

	kartId := anketKimlikMetni(FikoYT.Istek.Json["kartId"])
	icerikId := anketKimlikMetni(FikoYT.Istek.Json["icerikId"])
	kartOid, errKart := primitive.ObjectIDFromHex(kartId)
	icOid, errIc := primitive.ObjectIDFromHex(icerikId)
	if errKart != nil || errIc != nil {
		return anketHata(FikoYT, 400, "kartId ve icerikId zorunlu")
	}

	secimler := map[string]bool{}
	secenekHam, _ := FikoYT.Istek.Json["secenekIds"].([]interface{})
	for _, s := range secenekHam {
		if id := anketKimlikMetni(s); id != "" {
			secimler[id] = true
		}
	}

	ad, _ := FikoYT.Istek.Json["ad"].(string)
	ad = strings.TrimSpace(ad)
	if len([]rune(ad)) > 80 {
		ad = string([]rune(ad)[:80])
	}

	ctx := context.TODO()
	koleksiyon := FikoYT.Koleksiyon("hareketler")

	// 1. Kart: var mı, oy verilebilir mi, içerik bu karta ait mi?
	var kart bson.M
	if err := koleksiyon.FindOne(ctx, bson.M{"_id": kartOid}).Decode(&kart); err != nil || kart == nil {
		return anketHata(FikoYT, 404, "Kart bulunamadı (kartId: "+kartId+")")
	}
	if !anketGorunebilirMi(kart, kullanici) {
		return anketHata(FikoYT, 403, "Bu ankete oy veremezsiniz")
	}
	veri, _ := kart["veri"].(bson.M)
	refler, _ := veri["icerikler"].(primitive.A)
	karttaVar := false
	for _, r := range refler {
		if anketKimlikMetni(r) == icerikId {
			karttaVar = true
			break
		}
	}
	if !karttaVar {
		return anketHata(FikoYT, 404, "İçerik bu karta ait değil (icerikId: "+icerikId+")")
	}

	// 2. İçerik belgesi ve anket
	var icerik bson.M
	if err := koleksiyon.FindOne(ctx, bson.M{"_id": icOid}).Decode(&icerik); err != nil || icerik == nil {
		return anketHata(FikoYT, 404, "İçerik bulunamadı (icerikId: "+icerikId+")")
	}
	ek, _ := icerik["ek"].(bson.M)
	anket, _ := ek["anket"].(bson.M)
	if anket == nil {
		return anketHata(FikoYT, 404, "Anket bulunamadı (bu içerikte anket kaydı yok)")
	}

	simdi := time.Now()
	if bitisMetin, _ := anket["bitis"].(string); bitisMetin != "" {
		if bitis, perr := time.Parse(time.RFC3339, bitisMetin); perr == nil && simdi.After(bitis) {
			return anketHata(FikoYT, 409, "Anket sona erdi")
		}
	}

	tekliMi, _ := anket["tekliMi"].(bool)
	if tekliMi && len(secimler) > 1 {
		return anketHata(FikoYT, 400, "Tekli ankette yalnızca bir seçenek işaretlenebilir")
	}

	secenekler, _ := anket["secenekler"].(primitive.A)
	gecerli := map[string]bool{}
	for _, s := range secenekler {
		if m, ok := s.(bson.M); ok {
			gecerli[anketKimlikMetni(m["id"])] = true
		}
	}
	for id := range secimler {
		if !gecerli[id] {
			return anketHata(FikoYT, 400, "Geçersiz seçenek")
		}
	}

	// 3. Kullanıcının eski oylarını kaldır, yeni seçimleri ekle
	yeniSecenekler := primitive.A{}
	for _, s := range secenekler {
		m, ok := s.(bson.M)
		if !ok {
			continue
		}
		id := anketKimlikMetni(m["id"])
		oylar := primitive.A{}
		if mevcut, ok := m["oylar"].(primitive.A); ok {
			for _, o := range mevcut {
				if om, ok := o.(bson.M); ok && anketKimlikMetni(om["sahip"]) == kullanici {
					continue
				}
				oylar = append(oylar, o)
			}
		}
		if secimler[id] {
			oylar = append(oylar, bson.M{"sahip": kullanici, "ad": ad, "tarih": simdi.Format(time.RFC3339)})
		}
		yeniSecenekler = append(yeniSecenekler, bson.M{"id": m["id"], "metin": m["metin"], "oylar": oylar})
	}

	// 4. Yalnızca bu içerik belgesinin anket seçenekleri güncellenir
	if _, err := koleksiyon.UpdateOne(ctx, bson.M{"_id": icOid}, bson.M{"$set": bson.M{"ek.anket.secenekler": yeniSecenekler}}); err != nil {
		return anketHata(FikoYT, 500, "Oy kaydedilemedi: "+err.Error())
	}

	// Diğer görüntüleyenlere canlı yayın
	kokDB.PaylasimKartYayinlaDis(FikoYT, "guncellendi", kartId)

	// Güncel anketi istemciye döndür
	guncelAnket := bson.M{}
	for k, v := range anket {
		guncelAnket[k] = v
	}
	guncelAnket["secenekler"] = yeniSecenekler

	FikoYT.Cevap.Sonuc = fiber.Map{"anket": guncelAnket}
	return nil
}
