// Package sdk: FikoYT eklentileri icin ortak protokol katmani (Asama 3).
// Bridge ile ayni cerceve bicimini kullanir: Content-Length basligi + JSON-RPC 2.0 govdesi.
// Bu paket bridge'in ic paketi olan eklenti_protokol ile ayni sabitleri tasir; bayt bayt uyumludur.
package sdk

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

const (
	// ProtokolSurumu: bridge ile ayni cerceveli protokol surumu. El sikisma bunu dogrular.
	ProtokolSurumu = 2
	// MaksMesaj: tek bir JSON govdesinin ust siniri (bridge ile ayni).
	MaksMesaj = 4 * 1024 * 1024
	// MaksParca: tek parca veri alaninin ust siniri (bridge birlestirmez, sunucu birlestirir).
	MaksParca = 256 * 1024

	maksBaslikSatiri = 1024

	YontemMerhaba  = "fikoyt/merhaba"
	YontemPing     = "fikoyt/ping"
	YontemAyar     = "fikoyt/ayar"
	YontemParca    = "fikoyt/parca"
	YontemOlay     = "fikoyt/olay"
	YontemOnayIste = "onay.iste"
)

// ErrCerceve: cerceve bicimi bozuk ya da boyut siniri asildi.
var ErrCerceve = errors.New("sdk: gecersiz cerceve")

// Hata: JSON-RPC hata nesnesi.
type Hata struct {
	Kod   int    `json:"code"`
	Mesaj string `json:"message"`
}

func (h *Hata) Error() string {
	return fmt.Sprintf("rpc %d: %s", h.Kod, h.Mesaj)
}

// Mesaj: JSON-RPC 2.0 zarfi.
type Mesaj struct {
	JSONRPC string          `json:"jsonrpc"`
	Id      json.RawMessage `json:"id,omitempty"`
	Method  string          `json:"method,omitempty"`
	Params  json.RawMessage `json:"params,omitempty"`
	Result  json.RawMessage `json:"result,omitempty"`
	Error   *Hata           `json:"error,omitempty"`
}

// cerceve: mesaji Content-Length basligiyla birlikte baytlara cevirir.
func cerceve(m Mesaj) ([]byte, error) {
	m.JSONRPC = "2.0"
	govde, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}
	if len(govde) > MaksMesaj {
		return nil, ErrCerceve
	}
	baslik := fmt.Sprintf("Content-Length: %d\r\n\r\n", len(govde))
	return append([]byte(baslik), govde...), nil
}

// oku: bir cerceve okur. Boyut siniri asilirsa akis senkron degildir; cagiran baglantiyi kapatmalidir.
func oku(r *bufio.Reader) (Mesaj, error) {
	var m Mesaj
	uzunluk := -1
	for {
		satir, err := r.ReadString('\n')
		if err != nil {
			return m, err
		}
		if len(satir) > maksBaslikSatiri {
			return m, ErrCerceve
		}
		satir = strings.TrimRight(satir, "\r\n")
		if satir == "" {
			break
		}
		ad, deger, ok := strings.Cut(satir, ":")
		if !ok {
			continue
		}
		if strings.EqualFold(strings.TrimSpace(ad), "Content-Length") {
			v, err := strconv.Atoi(strings.TrimSpace(deger))
			if err != nil || v < 0 {
				return m, ErrCerceve
			}
			uzunluk = v
		}
	}
	if uzunluk < 0 || uzunluk > MaksMesaj {
		return m, ErrCerceve
	}
	govde := make([]byte, uzunluk)
	if _, err := io.ReadFull(r, govde); err != nil {
		return m, err
	}
	if err := json.Unmarshal(govde, &m); err != nil {
		return m, ErrCerceve
	}
	return m, nil
}
