package main

// Yerel ayar sayfasi: http://127.0.0.1:7878/ayarlar
// Izinler ve eslesmeler yalnizca bu sayfadan degistirilebilir.
// Guvenlik:
//   - Sayfa ve degisiklik uclari, Origin basligi olan HER istegi reddeder
//     (bridge'in kendi sayfasi haric). Web sitesi bu uclara erisemez.
//   - Degisiklik istekleri, sayfaya gomulu, her acilista yeniden uretilen
//     CSRF token'ini ister (sabit zamanli karsilastirma).
//   - Sayfa yalnizca hesap girisi yapildiktan sonra acilir.

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"html"
	"net/http"
	"time"
)

var csrfToken = yeniCsrfToken()

func yeniCsrfToken() string {
	b := make([]byte, 32)
	if _, err := rand.Read(b); err != nil {
		panic(err) // rastgele kaynak yoksa guvenli calisamaz
	}
	return hex.EncodeToString(b)
}

// yerelIstek: Origin yoksa (dogrudan gezinme) ya da bridge'in kendi sayfasindan geliyorsa izin verir.
func yerelIstek(w http.ResponseWriter, r *http.Request) bool {
	o := r.Header.Get("Origin")
	if o != "" && o != "http://"+dinleAdres && o != "http://localhost:7878" {
		http.Error(w, "yetkisiz kaynak", http.StatusForbidden)
		return false
	}
	return true
}

func csrfDogrula(w http.ResponseWriter, r *http.Request) bool {
	if subtle.ConstantTimeCompare([]byte(r.Header.Get("X-Csrf-Token")), []byte(csrfToken)) != 1 {
		http.Error(w, "gecersiz token", http.StatusForbidden)
		return false
	}
	return true
}

func ayarlarHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "yalnizca GET", http.StatusMethodNotAllowed)
		return
	}
	if !yerelIstek(w, r) {
		return
	}
	// Ayarlar yalnizca hesap girisi yapildiktan sonra acilir.
	if _, err := oturumOku(); err != nil {
		http.Redirect(w, r, "/giris", http.StatusSeeOther)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Header().Set("Cache-Control", "no-store")
	w.Write([]byte(ayarlarSayfasi(izinOku(), eslesmeleriOku(), csrfToken)))
}

type izinDegisimIstegi struct {
	Anahtar string `json:"anahtar"`
	Deger   bool   `json:"deger"`
}

func izinDegistirHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "yalnizca POST", http.StatusMethodNotAllowed)
		return
	}
	if !yerelIstek(w, r) || !csrfDogrula(w, r) {
		return
	}
	var istek izinDegisimIstegi
	if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1024)).Decode(&istek); err != nil {
		http.Error(w, "gecersiz istek", http.StatusBadRequest)
		return
	}
	y := izinOku()
	switch istek.Anahtar {
	case "everythingAra":
		y.EverythingAra = istek.Deger
	case "catiaAc":
		y.CatiaAc = istek.Deger
	case "usb":
		y.UsbBaglanti = istek.Deger
	default:
		http.Error(w, "bilinmeyen izin", http.StatusBadRequest)
		return
	}
	if err := izinleriKaydet(y); err != nil {
		http.Error(w, "kaydedilemedi", http.StatusInternalServerError)
		return
	}
	izinDurumGonder()
	if !istek.Deger {
		if err := yetkiGeriAl(istek.Anahtar, ""); err != nil {
			olayLog("site yetkileri silinemedi: %v", err)
		}
	}
	yazJSON(w, http.StatusOK, y)
}

type eslesmeKaldirIstegi struct {
	Ozet string `json:"ozet"`
}

// eslesmeKaldirHandler: bir eslesmeyi siler; o eslesmenin token'i artik gecersiz olur.
func eslesmeKaldirHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "yalnizca POST", http.StatusMethodNotAllowed)
		return
	}
	if !yerelIstek(w, r) || !csrfDogrula(w, r) {
		return
	}
	var istek eslesmeKaldirIstegi
	if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1024)).Decode(&istek); err != nil || istek.Ozet == "" {
		http.Error(w, "gecersiz istek", http.StatusBadRequest)
		return
	}
	var kalan []eslesme
	silindi := false
	for _, e := range eslesmeleriOku() {
		if !silindi && subtle.ConstantTimeCompare([]byte(e.TokenOzeti), []byte(istek.Ozet)) == 1 {
			silindi = true
			continue
		}
		kalan = append(kalan, e)
	}
	if !silindi {
		http.Error(w, "eslesme bulunamadi", http.StatusNotFound)
		return
	}
	if err := eslesmeleriYaz(kalan); err != nil {
		http.Error(w, "kaydedilemedi", http.StatusInternalServerError)
		return
	}
	if err := eslesmeYetkileriniSil(istek.Ozet); err != nil {
		olayLog("eslesme yetkileri silinemedi: %v", err)
	}
	yazJSON(w, http.StatusOK, map[string]any{"ok": true})
}

// ayarlarIzinHandler: ayar sayfasinin guncel izin durumunu verir (sayfa periyodik okur).
func ayarlarIzinHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "yalnizca GET", http.StatusMethodNotAllowed)
		return
	}
	yazJSON(w, http.StatusOK, izinOku())
}

// ayarlarGuncellemeHandler: otomatik guncelleme tercihini okur (GET) ya da degistirir (POST).
// Tercih yalnizca bu bilgisayarin yerel ayar sayfasindan degisir; web arayuzu degistiremez.
func ayarlarGuncellemeHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		if !yerelIstek(w, r) {
			return
		}
		yazJSON(w, http.StatusOK, guncellemeAyarOku())
	case http.MethodPost:
		if !yerelIstek(w, r) || !csrfDogrula(w, r) {
			return
		}
		var istek guncellemeAyar
		if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 256)).Decode(&istek); err != nil {
			http.Error(w, "gecersiz istek", http.StatusBadRequest)
			return
		}
		if err := guncellemeAyarYaz(istek); err != nil {
			http.Error(w, "kaydedilemedi", http.StatusInternalServerError)
			return
		}
		olayLog("otomatik guncelleme tercihi: %v", istek.Otomatik)
		bridgeSurumGonder()
		yazJSON(w, http.StatusOK, istek)
	default:
		http.Error(w, "yalnizca GET veya POST", http.StatusMethodNotAllowed)
	}
}

// ayarlarSayfasi: ayar sayfasinin HTML'i. Yalnizca calisan ayarlar gosterilir.
func ayarlarSayfasi(y Izinler, liste []eslesme, token string) string {
	secili := ""
	if y.EverythingAra {
		secili = "checked"
	}
	secilimUsb := ""
	if y.UsbBaglanti {
		secilimUsb = "checked"
	}

	eslesmeHTML := `<div class="bos-durum"><div class="bos-baslik">Henüz eşleşmiş site yok</div><div class="alt">Bir site bu bilgisayarla eşleşmek istediğinde onay ekranı açılır. Onayladığınız siteler burada listelenir.</div></div>`
	if len(liste) > 0 {
		eslesmeHTML = ""
	}
	for _, e := range liste {
		tarih := e.Tarih
		if t, err := time.Parse(time.RFC3339, e.Tarih); err == nil {
			tarih = t.Format("02.01.2006 15:04")
		}
		eslesmeHTML += `<div class="satir">
  <div class="metin"><div class="baslik">` + html.EscapeString(e.Site) + `</div><div class="alt">Eşleşme tarihi: ` + html.EscapeString(tarih) + `</div></div>
  <button class="dugme tehlike" data-ozet="` + html.EscapeString(e.TokenOzeti) + `" onclick="eslesmeKaldir(this)">Kaldır</button>
</div>`
	}

	return `<!doctype html><html lang="tr"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>FikoYT Yerel Köprü - Ayarlar</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#f3f4f6;font-family:"Segoe UI",system-ui,sans-serif;color:#1f2937;min-height:100vh}
.ust{background:#fff;border-bottom:1px solid #e5e7eb;padding:16px 28px;display:flex;align-items:center;gap:14px}
.logo{width:40px;height:40px;border-radius:11px;background:linear-gradient(135deg,#2185d0,#1678c2);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:18px;box-shadow:0 4px 12px rgba(33,133,208,.3);flex:none}
.ust h1{font-size:17px;margin:0;font-weight:700}
.ust .alt{margin-top:2px}
.ust .sag{margin-left:auto}
main{max-width:680px;margin:28px auto;padding:0 18px;display:flex;flex-direction:column;gap:16px}
.kart{background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.06);border:1px solid rgba(0,0,0,.04);padding:22px}
.kart-ust{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:6px}
.kart h2{font-size:15px;margin:0;color:#111827;font-weight:700}
.kart .aciklama{font-size:13px;color:#6b7280;line-height:1.55;margin:0 0 6px}
.satir{display:flex;justify-content:space-between;align-items:center;gap:18px;padding:14px 0;border-top:1px solid #f1f2f4}
.satir:first-of-type{border-top:0}
.metin{min-width:0;flex:1}
.baslik{font-weight:600;font-size:14px}
.alt{color:#6b7280;font-size:12.5px;margin-top:3px;line-height:1.5}
.rozet{display:inline-block;padding:4px 11px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
.rozet.aktif{background:#ecfdf5;color:#047857}
.rozet.pasif{background:#f3f4f6;color:#6b7280}
.rozet.beklemede{background:#fffbeb;color:#b45309}
.bos-durum{padding:18px;border-radius:12px;background:#f9fafb;text-align:left}
.bos-baslik{font-weight:600;font-size:14px;margin-bottom:4px}
.anahtar{position:relative;width:44px;height:24px;flex:none}
.anahtar input{opacity:0;width:0;height:0}
.kaydir{position:absolute;inset:0;background:#d1d5db;border-radius:24px;transition:.2s;cursor:pointer}
.kaydir:before{content:"";position:absolute;height:18px;width:18px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.2s;box-shadow:0 1px 3px rgba(0,0,0,.2)}
.anahtar input:checked + .kaydir{background:#2185d0}
.anahtar input:checked + .kaydir:before{transform:translateX(20px)}
.dugme{padding:7px 14px;border-radius:8px;border:1px solid #d1d5db;background:#fff;color:#1f2937;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}
.dugme:hover{background:#f9fafb}
.dugme.tehlike{color:#b91c1c;border-color:#fecaca}
.dugme.tehlike:hover{background:#fef2f2}
.bilgi{font-size:12.5px;color:#6b7280;margin:10px 0 0;min-height:18px}
.gunluk{font-family:Consolas,"Courier New",monospace;font-size:12.5px;max-height:300px;overflow:auto;background:#f9fafb;border-radius:12px;padding:6px 14px;border:1px solid #eef0f3}
.gunluk .s{display:flex;gap:14px;padding:9px 0;border-bottom:1px solid #eef0f3;color:#374151;line-height:1.5}
.gunluk .s:last-child{border-bottom:0}
.gunluk .s b{color:#9ca3af;font-weight:600;white-space:nowrap}
.gunluk .s.uyari{color:#b45309}
.gunluk .bos{color:#9ca3af;padding:12px 0;font-family:"Segoe UI",system-ui,sans-serif;font-size:13px}
</style></head><body>
<div class="ust">
  <div class="logo">F</div>
  <div><h1>FikoYT Yerel Köprü</h1><div class="alt">Ayarlar · Sürüm ` + surum + `</div></div>
  <span class="rozet aktif sag">Hesaba bağlı</span>
</div>
<main>
<section class="kart">
  <div class="kart-ust"><h2>Köprü güncellemeleri</h2></div>
  <p class="aciklama">Yeni köprü sürümleri bu bilgisayara kurulmadan önce onay ister. Otomatik güncellemeyi açarsanız yeni sürümler sorulmadan kurulur ve köprü yeniden başlar.</p>
  <div class="satir">
    <div class="metin">
      <div class="baslik">Yeni sürümleri otomatik kur</div>
      <div class="alt">Kapalıyken her güncellemede bu bilgisayarda onay ekranı açılır. Bu tercih yalnızca bu ayar sayfasından değiştirilebilir.</div>
    </div>
    <label class="anahtar"><input type="checkbox" id="otomatikGuncelleme"><span class="kaydir"></span></label>
  </div>
  <p class="bilgi" id="guncellemeDurum"></p>
</section>

<section class="kart">
  <div class="kart-ust"><h2>Eklenti izinleri</h2></div>
  <p class="aciklama">Eklentilere verdiğiniz izinler yalnızca bu bilgisayarda geçerlidir. Kapatınca eklentinin site yetkileri de düşer.</p>
  <div class="satir">
    <div class="metin">
      <div class="baslik">Everything araması</div>
      <div class="alt">Everything eklentisi, bu bilgisayardaki dosya adlarını fikoyt.com üzerinden aramanıza izin verir. Küçük önizlemeler Windows'un kendi özelliğiyle alınır; dosya içeriğini eklenti kendisi açıp okumaz.</div>
    </div>
    <label class="anahtar"><input type="checkbox" data-izin="everythingAra" ` + secili + `><span class="kaydir"></span></label>
  </div>
  <div class="satir">
    <div class="metin">
      <div class="baslik">USB eklentisi</div>
      <div class="alt">USB sürücülerini listeleyip USB eklentisini bu bilgisayarda çalıştırmaya izin verir. Dosya işlemleri ayrıca klasör bazında onaylanır.</div>
    </div>
    <label class="anahtar"><input type="checkbox" data-izin="usb" ` + secilimUsb + `><span class="kaydir"></span></label>
  </div>
  <p class="bilgi" id="izinDurum"></p>
</section>

<section class="kart">
  <div class="kart-ust"><h2>Dosya izinleri</h2></div>
  <p class="aciklama">Web uygulamalarının bu bilgisayarda dosya okuyup yazabilmesi için klasör izni verin. Windows, Program Files ve köprünün kendi klasörü her durumda kapalıdır.</p>
  <div id="dosyaKlasorler"></div>
  <div style="display:flex;flex-direction:column;gap:8px;margin-top:10px">
    <input id="dosyaYol" placeholder="Klasör yolu (ör. D:\Projeler)" style="padding:8px 10px;border:1px solid #d1d5db;border-radius:8px;font-size:13px">
    <div style="display:flex;gap:14px;flex-wrap:wrap;font-size:13px">
      <label><input type="checkbox" class="dosyaIslem" value="oku" checked> Oku</label>
      <label><input type="checkbox" class="dosyaIslem" value="yaz"> Yaz</label>
      <label><input type="checkbox" class="dosyaIslem" value="kopyala"> Kopyala</label>
      <label><input type="checkbox" class="dosyaIslem" value="sil"> Sil</label>
    </div>
    <div style="display:flex;gap:14px;font-size:13px">
      <label><input type="radio" name="dosyaSure" value="saat" checked> 1 saat</label>
      <label><input type="radio" name="dosyaSure" value="kalici"> Her zaman</label>
    </div>
    <div><button class="dugme" onclick="dosyaKlasorEkle()">Klasör ekle</button></div>
  </div>
  <div class="satir">
    <div class="metin">
      <div class="baslik">Tam erişim</div>
      <div class="alt">Bilgisayardaki tüm kullanıcı dosyaları için. Yasak klasörler yine kapalıdır. Açarken onay istenir.</div>
    </div>
    <label class="anahtar"><input type="checkbox" id="dosyaTam"><span class="kaydir"></span></label>
  </div>
  <p class="bilgi" id="dosyaDurum"></p>
</section>
<section class="kart">
  <div class="kart-ust"><h2>Eşleşmiş siteler</h2></div>
  <p class="aciklama">Bu bilgisayarla eşleşen web siteleri. Kaldırdığınız site yeniden eşleşmek zorunda kalır.</p>
  ` + eslesmeHTML + `
  <p class="bilgi" id="eslesmeDurum"></p>
</section>

<section class="kart">
  <div class="kart-ust"><h2>Olay günlüğü</h2></div>
  <p class="aciklama">Bu bilgisayardaki son işlemler. Sayfa her 3 saniyede bir güncellenir.</p>
  <div class="gunluk" id="gunluk"></div>
</section>
</main>
<script>
var TOKEN = "` + token + `";
document.querySelectorAll('input[data-izin]').forEach(function(el){
  el.addEventListener('change', async function(){
    var d = document.getElementById('izinDurum');
    try {
      var r = await fetch('/ayarlar/izin', {method:'POST', headers:{'Content-Type':'application/json','X-Csrf-Token':TOKEN},
        body: JSON.stringify({anahtar: el.dataset.izin, deger: el.checked})});
      if (!r.ok) throw new Error('HTTP ' + r.status);
      d.textContent = el.checked ? 'İzin açıldı.' : 'İzin kapatıldı.';
    } catch(e) {
      el.checked = !el.checked;
      d.textContent = 'Kaydedilemedi: ' + e.message;
    }
  });
});
async function eslesmeKaldir(btn){
  var d = document.getElementById('eslesmeDurum');
  if (!confirm('Bu sitenin eşleşmesi kaldırılsın mı? Site yeniden eşleşmek zorunda kalır.')) return;
  try {
    var r = await fetch('/ayarlar/eslesme-kaldir', {method:'POST', headers:{'Content-Type':'application/json','X-Csrf-Token':TOKEN},
      body: JSON.stringify({ozet: btn.dataset.ozet})});
    if (!r.ok) throw new Error('HTTP ' + r.status);
    btn.closest('.satir').remove();
    d.textContent = 'Eşleşme kaldırıldı.';
  } catch(e) {
    d.textContent = 'Kaldırılamadı: ' + e.message;
  }
}
function gunlukYenile(){
  fetch('/ayarlar/loglar').then(function(r){ return r.ok ? r.json() : []; }).then(function(liste){
    var el = document.getElementById('gunluk');
    el.textContent = '';
    if (!liste.length) {
      var p = document.createElement('p'); p.className = 'bos'; p.textContent = 'Henüz olay yok.'; el.appendChild(p); return;
    }
    liste.slice().reverse().forEach(function(e){
      var d = document.createElement('div');
      d.className = 's' + (e.seviye === 'uyari' ? ' uyari' : '');
      var b = document.createElement('b'); b.textContent = e.zaman;
      var m = document.createElement('span'); m.textContent = e.mesaj;
      d.appendChild(b); d.appendChild(m); el.appendChild(d);
    });
  }).catch(function(){});
}
gunlukYenile(); setInterval(gunlukYenile, 3000);
// Dosya izinleri: klasor listesi, ekleme, kaldirma ve tam erisim.
function dosyaSureMetni(bitis){
  var kalan = bitis - Date.now()/1000;
  if (kalan > 1576800000) return 'her zaman';
  return Math.max(0, Math.round(kalan/60)) + ' dakika kaldı';
}
function dosyaYenile(){
  fetch('/ayarlar/dosya-izin').then(function(r){ return r.ok ? r.json() : null; }).then(function(y){
    if (!y) return;
    var kap = document.getElementById('dosyaKlasorler');
    kap.textContent = '';
    (y.klasorler || []).forEach(function(k){
      var d = document.createElement('div'); d.className = 'satir';
      var m = document.createElement('div'); m.className = 'metin';
      var b = document.createElement('div'); b.className = 'baslik'; b.textContent = k.kok;
      var a = document.createElement('div'); a.className = 'alt';
      a.textContent = (k.islemler || []).join(', ') + ' · ' + dosyaSureMetni(k.bitis);
      m.appendChild(b); m.appendChild(a);
      var s = document.createElement('button'); s.className = 'dugme tehlike'; s.textContent = 'Kaldır';
      s.onclick = function(){ dosyaIstek({islem:'kaldir', kok:k.kok}); };
      d.appendChild(m); d.appendChild(s); kap.appendChild(d);
    });
    document.getElementById('dosyaTam').checked = (y.tamErisim || 0) > Date.now()/1000;
  }).catch(function(){});
}
function dosyaIstek(govde){
  var d = document.getElementById('dosyaDurum');
  return fetch('/ayarlar/dosya-izin', {method:'POST', headers:{'Content-Type':'application/json','X-Csrf-Token':TOKEN},
    body: JSON.stringify(govde)}).then(function(r){
      if (!r.ok) return r.text().then(function(t){ throw new Error(t.trim() || ('HTTP ' + r.status)); });
      d.textContent = 'Kaydedildi.';
      dosyaYenile();
    }).catch(function(e){ d.textContent = 'Kaydedilemedi: ' + e.message; });
}
function dosyaKlasorEkle(){
  var yol = document.getElementById('dosyaYol').value.trim();
  var islemler = Array.prototype.map.call(document.querySelectorAll('.dosyaIslem:checked'), function(e){ return e.value; });
  var sure = document.querySelector('input[name="dosyaSure"]:checked').value;
  if (!yol) { document.getElementById('dosyaDurum').textContent = 'Klasör yolu yazın.'; return; }
  dosyaIstek({islem:'ekle', kok: yol, islemler: islemler, sure: sure}).then(function(){
    document.getElementById('dosyaYol').value = '';
  });
}
document.getElementById('dosyaTam').addEventListener('change', function(){
  var kutu = this;
  var d = document.getElementById('dosyaDurum');
  if (kutu.checked) {
    if (!confirm('Tam erişim açılsın mı? Bu, bilgisayardaki kullanıcı dosyalarının tamamına web uygulamalarının erişmesine izin verir (Windows ve program klasörleri hariç).')) {
      kutu.checked = false; return;
    }
    var sure = document.querySelector('input[name="dosyaSure"]:checked').value;
    dosyaIstek({islem:'tam-erisim', acik: true, onay: true, sure: sure});
  } else {
    dosyaIstek({islem:'tam-erisim', acik: false});
  }
});
dosyaYenile();
// Izin durumu web'den (fikoyt.com) degisebilir; sayfa bunu periyodik okur.
function izinYenile(){
  fetch('/ayarlar/izinler').then(function(r){ return r.ok ? r.json() : null; }).then(function(y){
    if (!y) return;
    [['everythingAra', y.everythingAra], ['usb', y.usbBaglanti]].forEach(function(p){
      var el = document.querySelector('input[data-izin="' + p[0] + '"]');
      if (el && document.activeElement !== el && el.checked !== !!p[1]) {
        el.checked = !!p[1];
        document.getElementById('izinDurum').textContent = 'İzin fikoyt.com üzerinden güncellendi.';
      }
    });
  }).catch(function(){});
}
setInterval(izinYenile, 2000);
// Otomatik guncelleme tercihi: yalnizca bu ayar sayfasindan degisir.
var otoKutu = document.getElementById('otomatikGuncelleme');
fetch('/ayarlar/guncelleme').then(function(r){ return r.ok ? r.json() : null; }).then(function(y){
  if (y) otoKutu.checked = !!y.otomatik;
}).catch(function(){});
otoKutu.addEventListener('change', async function(){
  var d = document.getElementById('guncellemeDurum');
  try {
    var r = await fetch('/ayarlar/guncelleme', {method:'POST', headers:{'Content-Type':'application/json','X-Csrf-Token':TOKEN},
      body: JSON.stringify({otomatik: otoKutu.checked})});
    if (!r.ok) throw new Error('HTTP ' + r.status);
    d.textContent = otoKutu.checked ? 'Otomatik güncelleme açıldı.' : 'Otomatik güncelleme kapatıldı.';
  } catch(e) {
    otoKutu.checked = !otoKutu.checked;
    d.textContent = 'Kaydedilemedi: ' + e.message;
  }
});
</script>
</body></html>`
}
