package main

// Eklenti aciklamasi uretici. Kullanim:
//   go run main.go -depo <eklenti-depo> -anahtar <eklenti> -girdi <bilgi.json> -gizli <gizli-anahtar.txt>
// bilgi.json: {"ad": "...", "aciklama": "...", "ozellikler": ["..."], "erisim": "..."}
// Cikti: <depo>/<eklenti>/aciklama.json ve aciklama.imza.json (imza etiketi: fikoyt-aciklama-v1).
// Eklenti klasorunde en az bir surum (manifest.json) olmalidir; yoksa durur.

import (
	"crypto/ed25519"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

const gomulu = "bE1qYWOzKZasuepzdrtWGEIcDYAm6RvI6Ymy+HVpYcA="

var anahtarDesen = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]{0,39}$`)

type tanitim struct {
	Ad         string   `json:"ad"`
	Aciklama   string   `json:"aciklama"`
	Ozellikler []string `json:"ozellikler"`
	Erisim     string   `json:"erisim"`
}

func hata(format string, a ...any) {
	fmt.Fprintf(os.Stderr, "HATA: "+format+"\n", a...)
	os.Exit(1)
}

func surumVar(klasor string) bool {
	giris, err := os.ReadDir(klasor)
	if err != nil {
		return false
	}
	for _, g := range giris {
		if !g.IsDir() {
			continue
		}
		if _, err := os.Stat(filepath.Join(klasor, g.Name(), "manifest.json")); err == nil {
			return true
		}
	}
	return false
}

func main() {
	depo := flag.String("depo", "", "eklenti deposu klasoru")
	anahtar := flag.String("anahtar", "", "eklenti anahtari (or: everything)")
	girdi := flag.String("girdi", "", "aciklama bilgisi (JSON) dosyasi")
	gizliYolu := flag.String("gizli", "", "gizli-anahtar.txt yolu")
	flag.Parse()
	if *depo == "" || *anahtar == "" || *girdi == "" || *gizliYolu == "" {
		hata("-depo, -anahtar, -girdi ve -gizli zorunlu")
	}
	if !anahtarDesen.MatchString(*anahtar) {
		hata("gecersiz anahtar: %s", *anahtar)
	}
	klasor := filepath.Join(*depo, *anahtar)
	if fi, err := os.Stat(klasor); err != nil || !fi.IsDir() {
		hata("eklenti klasoru yok: %s", klasor)
	}
	if !surumVar(klasor) {
		hata("eklentinin hic surumu yok (manifest.json bulunamadi): %s", klasor)
	}

	ham, err := os.ReadFile(*girdi)
	if err != nil {
		hata("girdi okunamadi: %v", err)
	}
	var t tanitim
	if err := json.Unmarshal(ham, &t); err != nil {
		hata("girdi gecerli JSON degil: %v", err)
	}
	if strings.TrimSpace(t.Ad) == "" || strings.TrimSpace(t.Aciklama) == "" || strings.TrimSpace(t.Erisim) == "" {
		hata("ad, aciklama ve erisim bos olamaz")
	}
	if len(t.Ozellikler) == 0 {
		hata("en az bir ozellik gerekli")
	}
	for _, o := range t.Ozellikler {
		if strings.TrimSpace(o) == "" {
			hata("bos ozellik var")
		}
	}

	gizli, err := os.ReadFile(*gizliYolu)
	if err != nil {
		hata("gizli anahtar okunamadi: %v", err)
	}
	priv, err := base64.StdEncoding.DecodeString(strings.TrimSpace(string(gizli)))
	if err != nil || len(priv) != ed25519.PrivateKeySize {
		hata("gizli anahtar bicimi gecersiz")
	}
	pub := ed25519.PrivateKey(priv).Public().(ed25519.PublicKey)
	if base64.StdEncoding.EncodeToString(pub) != gomulu {
		hata("gizli anahtar bridge'e gomulu acik anahtarla eslesmiyor")
	}

	cikti, err := json.MarshalIndent(t, "", "  ")
	if err != nil {
		hata("JSON uretilemedi: %v", err)
	}
	aciklamaYolu := filepath.Join(klasor, "aciklama.json")
	if err := os.WriteFile(aciklamaYolu, cikti, 0o644); err != nil {
		hata("aciklama.json yazilamadi: %v", err)
	}

	sum := sha256.Sum256(cikti)
	ozet := hex.EncodeToString(sum[:])
	mesaj := []byte("fikoyt-aciklama-v1\n" + *anahtar + "\n" + ozet)
	imza := ed25519.Sign(priv, mesaj)
	if !ed25519.Verify(pub, mesaj, imza) {
		hata("imza dogrulanamadi")
	}
	imzaJSON, _ := json.MarshalIndent(map[string]string{"ozet": ozet, "imza": base64.StdEncoding.EncodeToString(imza)}, "", "  ")
	if err := os.WriteFile(filepath.Join(klasor, "aciklama.imza.json"), imzaJSON, 0o644); err != nil {
		hata("aciklama.imza.json yazilamadi: %v", err)
	}
	fmt.Printf("tamam: %s (%d bayt), ozet: %s\n", aciklamaYolu, len(cikti), ozet)
}