package main

// Ilk acilis girisi. Mevcut fikoyt.com giris endpoint'i kullanilir (web arayuzu ile ayni):
//   POST /fapiv1/fikoyt.com.FikoYT.sistem.giris.yap
//   multipart/form-data, alan: req_body = {"kullaniciAdi":"...","parola":"..."} (JSON metni)
// Basarili girisde sunucu oturum cerezi (Set-Cookie) doner. Bridge bu cerezleri
// yerel oturum.json dosyasinda saklar. Parola yalnizca bu istek sirasinda HTTPS ile
// fikoyt.com'a gider; diske yazilmaz.

import (
	"bytes"
	"encoding/json"
	"errors"
	"html"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const girisAdres = "https://fikoyt.com:9000/fapiv1/fikoyt.com.FikoYT.sistem.giris.yap"

type oturumKaydi struct {
	KullaniciID  string         `json:"kullaniciId,omitempty"`
	KullaniciAdi string         `json:"kullaniciAdi"`
	Cerezler     []*http.Cookie `json:"cerezler"`
}

// oturumYoluProfil: verilen profilin oturum dosyasi.
func oturumYoluProfil(anahtar string) (string, error) {
	klasor, err := profilKlasorP(anahtar)
	if err != nil {
		return "", err
	}
	return filepath.Join(klasor, "oturum.json"), nil
}

// oturumOkuProfil: verilen profilin oturumunu okur. Cerez yoksa hata.
func oturumOkuProfil(anahtar string) (*oturumKaydi, error) {
	yol, err := oturumYoluProfil(anahtar)
	if err != nil {
		return nil, err
	}
	veri, err := os.ReadFile(yol)
	if err != nil {
		return nil, err
	}
	var k oturumKaydi
	if err := json.Unmarshal(veri, &k); err != nil {
		return nil, err
	}
	if len(k.Cerezler) == 0 {
		return nil, errors.New("oturum cerezi yok")
	}
	return &k, nil
}

// oturumYazProfil: verilen profilin oturumunu yazar.
func oturumYazProfil(anahtar string, k *oturumKaydi) error {
	yol, err := oturumYoluProfil(anahtar)
	if err != nil {
		return err
	}
	veri, err := json.MarshalIndent(k, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(yol, veri, 0o600)
}

// oturumSilProfil: verilen profilin oturum dosyasini siler.
func oturumSilProfil(anahtar string) {
	if yol, err := oturumYoluProfil(anahtar); err == nil {
		os.Remove(yol)
	}
}

// ilkOturum: etkin hesaplardan oturumu olan ilkinin kaydi. Yoksa false.
func ilkOturum() (*oturumKaydi, bool) {
	for _, a := range etkinHesaplar() {
		if k, err := oturumOkuProfil(a); err == nil {
			return k, true
		}
	}
	return nil, false
}

// girisYapanKullanicilar: oturumu olan tum etkin hesaplarin fikoyt kullanici adlari (tekil, hesap sirasiyla).
// Web, kendi kullanicisinin bu listede olup olmadigina bakar; ilk oturum tek basina yeterli degil.
func girisYapanKullanicilar() []string {
	gorulen := map[string]bool{}
	liste := []string{}
	for _, a := range etkinHesaplar() {
		k, err := oturumOkuProfil(a)
		if err != nil || k.KullaniciAdi == "" || gorulen[k.KullaniciAdi] {
			continue
		}
		gorulen[k.KullaniciAdi] = true
		liste = append(liste, k.KullaniciAdi)
	}
	return liste
}

// herhangiOturumVar: etkin hesaplardan en az birinde kayitli oturum var mi.
func herhangiOturumVar() bool {
	_, ok := ilkOturum()
	return ok
}

// oturumlariSil: tum etkin hesaplarin oturumunu siler.
func oturumlariSil() {
	for _, a := range etkinHesaplar() {
		oturumSilProfil(a)
	}
}

// oturumGerekli: korunan ucu, giris yapilmamissa 401 ile kapatir. Token'li istekte istegin hesabina,
// yerel ayar sayfasinda (token yok) etkin hesaplardan birinde giris olmasina bakar.
func oturumGerekli(h http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var girisVar bool
		if p := profilDe(r); p != "" {
			_, err := oturumOkuProfil(p)
			girisVar = err == nil
		} else {
			girisVar = herhangiOturumVar()
		}
		if !girisVar {
			yazJSON(w, http.StatusUnauthorized, map[string]any{"hata": "giris gerekli"})
			return
		}
		h(w, r)
	}
}

// girisHandler: GET formu gosterir, POST kimligi fikoyt.com'a dogrulatir.
func girisHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Header().Set("Cache-Control", "no-store")
		w.Write([]byte(girisSayfasi("")))
	case http.MethodPost:
		if err := r.ParseForm(); err != nil {
			http.Error(w, "gecersiz form", http.StatusBadRequest)
			return
		}
		kullanici := strings.TrimSpace(r.PostFormValue("kullaniciAdi"))
		parola := r.PostFormValue("parola")
		if kullanici == "" || parola == "" {
			w.Header().Set("Content-Type", "text/html; charset=utf-8")
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(girisSayfasi("Kullanıcı adı ve parola zorunlu.")))
			return
		}
		kayit, err := fikoytGirisYap(kullanici, parola)
		if err != nil {
			olayLog("giriş denemesi başarısız (%s): %v", kullanici, err)
			w.Header().Set("Content-Type", "text/html; charset=utf-8")
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(girisSayfasi("Giriş başarısız: " + err.Error())))
			return
		}
		olayLog("giriş yapıldı: %s", kayit.KullaniciAdi)
		eskiAnahtar, _ := profilAnahtari(kayit.KullaniciAdi)
		anahtar, err := profilAnahtari(kayit.KullaniciID)
		if err != nil {
			http.Error(w, "kullanıcı adı profile çevrilemedi", http.StatusBadRequest)
			return
		}
		if kok, kokErr := veriKlasoru(); kokErr == nil {
			if err := profilKlasorTasi(kok, eskiAnahtar, anahtar); err != nil {
				olayLog("profil klasoru tasinamadi: %v", err)
			}
		}
		if err := profilHazirla(anahtar); err != nil {
			olayLog("profil etkinleştirilemedi: %v", err)
			http.Error(w, "profil etkinleştirilemedi", http.StatusInternalServerError)
			return
		}
		// Yeni profilin izinleri yuklenir (onceki hesabin izinleri bellekten gider).
		if err := kuruluYukleProfil(anahtar); err != nil {
			olayLog("kurulu liste okunamadi: %v", err)
		}
		if err := izinleriYukleProfil(anahtar); err != nil {
			olayLog("izinler okunamadi: %v", err)
		}
		if err := dosyaIzinleriYukle(anahtar); err != nil {
			olayLog("dosya izinleri okunamadi: %v", err)
		}
		if err := oturumYazProfil(anahtar, kayit); err != nil {
			http.Error(w, "oturum kaydedilemedi", http.StatusInternalServerError)
			return
		}
		// Bu profilin kendi sunucu baglantisi acilir (diger profillerin baglantilari etkilenmez).
		profilSoketBaslat(anahtar)
		http.Redirect(w, r, "/", http.StatusSeeOther)
	default:
		http.Error(w, "yalnizca GET/POST", http.StatusMethodNotAllowed)
	}
}

// oturumKapatHandler: yerel oturumu siler.
func oturumKapatHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "yalnizca POST", http.StatusMethodNotAllowed)
		return
	}
	oturumlariSil()
	yazJSON(w, http.StatusOK, map[string]any{"ok": true})
}

func fikoytGirisYap(kullanici, parola string) (*oturumKaydi, error) {
	reqBody, _ := json.Marshal(map[string]string{"kullaniciAdi": kullanici, "parola": parola})

	var govde bytes.Buffer
	mw := multipart.NewWriter(&govde)
	if err := mw.WriteField("req_body", string(reqBody)); err != nil {
		return nil, err
	}
	mw.Close()

	istemci := &http.Client{
		Timeout: 15 * time.Second,
		CheckRedirect: func(*http.Request, []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}
	istek, err := http.NewRequest(http.MethodPost, girisAdres, &govde)
	if err != nil {
		return nil, err
	}
	istek.Header.Set("Content-Type", mw.FormDataContentType())
	istek.Header.Set("Accept", "application/json")

	yanit, err := istemci.Do(istek)
	if err != nil {
		return nil, errors.New("sunucuya ulasilamadi")
	}
	defer yanit.Body.Close()
	icerik, _ := io.ReadAll(io.LimitReader(yanit.Body, 1<<20))

	// Sunucu hata durumunda {"sonuc":{"body":{"durum":{"hata":true,"hatalar":[{"mesaj":...}]}}}}
	// ya da dogrudan {"durum":{...}} donebilir; ikisini de dene.
	var cevap map[string]any
	mesajlar := []string{}
	if json.Unmarshal(icerik, &cevap) == nil {
		durum, _ := cevap["durum"].(map[string]any)
		if durum == nil {
			if sonuc, ok := cevap["sonuc"].(map[string]any); ok {
				if b, ok := sonuc["body"].(map[string]any); ok {
					durum, _ = b["durum"].(map[string]any)
				}
			}
		}
		if durum != nil {
			if hata, _ := durum["hata"].(bool); hata {
				if hs, ok := durum["hatalar"].([]any); ok {
					for _, h := range hs {
						if hm, ok := h.(map[string]any); ok {
							if m, ok := hm["mesaj"].(string); ok && m != "" {
								mesajlar = append(mesajlar, m)
							}
						}
					}
				}
				if len(mesajlar) == 0 {
					mesajlar = append(mesajlar, "sunucu hata dondu (HTTP "+http.StatusText(yanit.StatusCode)+")")
				}
				return nil, errors.New(strings.Join(mesajlar, " "))
			}
		}
	}
	if yanit.StatusCode != http.StatusOK {
		return nil, errors.New("sunucu HTTP " + http.StatusText(yanit.StatusCode) + " dondu")
	}
	cerezler := yanit.Cookies()
	if len(cerezler) == 0 {
		return nil, errors.New("sunucu oturum cerezi vermedi")
	}
	kimlik := kullaniciKimligiCevapta(cevap)
	if kimlik == "" {
		return nil, errors.New("sunucu kullanici kimligi vermedi")
	}
	return &oturumKaydi{KullaniciAdi: kullanici, KullaniciID: kimlik, Cerezler: cerezler}, nil
}

// kullaniciKimligiCevapta: basarili girisin kullanici kimligi. Sunucu belgeyi dogrudan ya da
// {"durum":..,"sonuc":belge} zarfi icinde donebilir; _id duz metin ya da {"$oid":..} olabilir.
func kullaniciKimligiCevapta(cevap map[string]any) string {
	adaylar := []map[string]any{cevap}
	if sonuc, ok := cevap["sonuc"].(map[string]any); ok {
		adaylar = append(adaylar, sonuc)
		if govde, ok := sonuc["body"].(map[string]any); ok {
			adaylar = append(adaylar, govde)
		}
	}
	for _, belge := range adaylar {
		switch v := belge["_id"].(type) {
		case string:
			if v != "" {
				return v
			}
		case map[string]any:
			if s, ok := v["$oid"].(string); ok && s != "" {
				return s
			}
		}
	}
	return ""
}

// fikoytIstekCerezleriProfil: verilen profilin oturum cerezlerini istege ekler.
func fikoytIstekCerezleriProfil(istek *http.Request, anahtar string) error {
	k, err := oturumOkuProfil(anahtar)
	if err != nil {
		return err
	}
	for _, c := range k.Cerezler {
		istek.AddCookie(c)
	}
	return nil
}

func girisSayfasi(hata string) string {
	mesaj := ""
	if hata != "" {
		mesaj = `<p class="hata">` + html.EscapeString(hata) + `</p>`
	}
	return `<!doctype html><html lang="tr"><head><meta charset="utf-8">
<title>FikoYT Yerel Köprü - Giriş</title>
<style>` + arayuzTemaCSS + `
.giris{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px}
.giris form{width:100%;max-width:360px;background:var(--yuzey);border:1px solid var(--kenar);border-radius:var(--yaricap);box-shadow:var(--golge);padding:28px}
.giris .logo{margin:0 auto 14px}
.giris h1{text-align:center;font-size:19px;margin:0 0 4px}
.giris p{color:var(--soluk);text-align:center;margin:0 0 18px;font-size:13px}
.giris label{display:block;font-size:12px;font-weight:600;color:var(--soluk);margin:12px 0 6px}
.giris input{width:100%}
.giris .dugme{width:100%;margin-top:18px;padding:11px}
</style></head><body>
<div class="giris"><form method="post" action="/giris">
<div class="logo">F</div>
<h1>FikoYT Yerel Köprü</h1>
<p>Devam etmek için fikoyt.com hesabınızla giriş yapın.</p>
` + mesaj + `
<label>Kullanıcı adı</label><input name="kullaniciAdi" autocomplete="username" required>
<label>Parola</label><input name="parola" type="password" autocomplete="current-password" required>
<button class="dugme birincil" type="submit">Giriş yap</button>
</form></div></body></html>`
}
