package main

// Eklenti kurulumu. Sira: alan dogrulama -> indirme adresi (yalnizca fikoyt.com) -> YEREL ONAY
// -> indirme -> imza ve ozet dogrulamasi -> surum klasorune yazma -> kurulu kaydi.
// Dogrulama basarisizsa hicbir dosya yazilmaz.

import (
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"time"
)

const maksPaketBoyutu = 50 << 20 // 50 MB
const paketIndirmeZamanAsimi = 60 * time.Second

var (
	errIndirmeAdresi = errors.New("indirme adresi izinli degil")
	anahtarDesen     = regexp.MustCompile(`^[a-z0-9-]{2,40}$`)
	surumDesen       = regexp.MustCompile(`^[0-9A-Za-z][0-9A-Za-z.-]{0,19}$`)
	ozetDesen        = regexp.MustCompile(`^[0-9a-fA-F]{64}$`)
)

// paketAlanlariGecerli: yol kacisini (../) ve bozuk alanlari daha baslamadan reddeder.
func paketAlanlariGecerli(p paketBilgisi) error {
	if !anahtarDesen.MatchString(p.Anahtar) {
		return errors.New("eklenti anahtari gecersiz")
	}
	if !surumDesen.MatchString(p.Surum) {
		return errors.New("surum gecersiz")
	}
	if !ozetDesen.MatchString(p.Sha256) {
		return errors.New("ozet gecersiz")
	}
	return nil
}

// izinliIndirmeAdresi: yalnizca https://fikoyt.com/ (port yok) veya https://fikoyt.com:9000/ indirilir.
// Kullanici bilgisi (user@host) ve baska portlar reddedilir.
func izinliIndirmeAdresi(adres string) error {
	u, err := url.Parse(adres)
	if err != nil || u.Scheme != "https" || u.User != nil || u.Hostname() != "fikoyt.com" {
		return errIndirmeAdresi
	}
	if p := u.Port(); p != "" && p != "9000" {
		return errIndirmeAdresi
	}
	return nil
}

// paketIndir: yonlendirmeler de yalnizca izinli adrese gidebilir.
func paketIndir(adres string) ([]byte, error) {
	if err := izinliIndirmeAdresi(adres); err != nil {
		return nil, err
	}
	istemci := &http.Client{
		Timeout: paketIndirmeZamanAsimi,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return izinliIndirmeAdresi(req.URL.String())
		},
	}
	yanit, err := istemci.Get(adres)
	if err != nil {
		return nil, err
	}
	defer yanit.Body.Close()
	if yanit.StatusCode != http.StatusOK {
		return nil, errors.New("indirme basarisiz")
	}
	veri, err := io.ReadAll(io.LimitReader(yanit.Body, maksPaketBoyutu+1))
	if err != nil {
		return nil, err
	}
	if len(veri) > maksPaketBoyutu {
		return nil, errors.New("paket cok buyuk")
	}
	return veri, nil
}

// eklentiKur: dogrulanmis paketi eklentiler\<anahtar>\<surum>\plugin.exe olarak yazar ve kurulu kaydini ekler.
func eklentiKur(profil string, p paketBilgisi, veri []byte) error {
	if err := paketAlanlariGecerli(p); err != nil {
		return err
	}
	if err := paketDogrula(p, paketAcikAnahtar, veri); err != nil {
		return err
	}
	klasor, err := veriKlasoru()
	if err != nil {
		return err
	}
	hedef := filepath.Join(klasor, "eklentiler", p.Anahtar, p.Surum)
	if err := os.MkdirAll(hedef, 0o700); err != nil {
		return err
	}
	gecici := filepath.Join(hedef, "plugin.exe.tmp")
	if err := os.WriteFile(gecici, veri, 0o600); err != nil {
		return err
	}
	if err := os.Rename(gecici, filepath.Join(hedef, "plugin.exe")); err != nil {
		os.Remove(gecici)
		return err
	}
	// Calisan eski surum varsa kapat: oturum kapaninca surec sonlanir, sonraki cagride yeni surum baslar.
	_ = ekEklentiDurdurProfil(profil, p.Anahtar)
	if err := kuruluEkleProfil(profil, kuruluEklenti{Anahtar: p.Anahtar, Surum: p.Surum, Etkin: true}); err != nil {
		return err
	}
	olayLog("eklenti kuruldu: %s %s", p.Anahtar, p.Surum)
	return nil
}
