package capability

import (
	"crypto/ed25519"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"strings"
)

type Signer struct {
	KeyID      string
	PrivateKey ed25519.PrivateKey
}

type devCapabilityKeyFile struct {
	KeyID      string `json:"key_id"`
	PublicKey  string `json:"public_key"`
	PrivateKey string `json:"private_key"`
}

func LoadSignerFromFile(path string) (Signer, error) {
	trimmedPath := strings.TrimSpace(path)
	if trimmedPath == "" {
		return Signer{}, fmt.Errorf("AGENTTEAM_MAAS_CAPABILITY_ED25519_PRIVATE_KEY_FILE is required")
	}
	data, err := os.ReadFile(trimmedPath)
	if err != nil {
		return Signer{}, fmt.Errorf("read MaaS capability private key file: %w", err)
	}
	var key devCapabilityKeyFile
	if err := json.Unmarshal(data, &key); err != nil {
		return Signer{}, fmt.Errorf("decode MaaS capability private key file: %w", err)
	}
	keyID := strings.TrimSpace(key.KeyID)
	if keyID == "" {
		return Signer{}, fmt.Errorf("MaaS capability private key file key_id is required")
	}
	privateKey, err := base64.StdEncoding.DecodeString(strings.TrimSpace(key.PrivateKey))
	if err != nil {
		return Signer{}, fmt.Errorf("decode MaaS capability private key: %w", err)
	}
	if len(privateKey) != ed25519.PrivateKeySize {
		return Signer{}, fmt.Errorf("MaaS capability private key must be %d bytes", ed25519.PrivateKeySize)
	}
	return Signer{
		KeyID:      keyID,
		PrivateKey: ed25519.PrivateKey(privateKey),
	}, nil
}

func (signer Signer) Sign(claims Claims) (string, error) {
	keyID := strings.TrimSpace(signer.KeyID)
	if keyID == "" {
		return "", fmt.Errorf("capability signing key id is required")
	}
	if len(signer.PrivateKey) != ed25519.PrivateKeySize {
		return "", fmt.Errorf("capability signing key is invalid")
	}

	headerRaw, err := json.Marshal(map[string]string{
		"alg": "EdDSA",
		"typ": "JWT",
		"kid": keyID,
	})
	if err != nil {
		return "", err
	}
	payloadRaw, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}

	signingInput := base64.RawURLEncoding.EncodeToString(headerRaw) + "." + base64.RawURLEncoding.EncodeToString(payloadRaw)
	signature := ed25519.Sign(signer.PrivateKey, []byte(signingInput))
	return signingInput + "." + base64.RawURLEncoding.EncodeToString(signature), nil
}
