package xkeypairs

import (
	"crypto/ecdsa"
	"crypto/rsa"
	"encoding/base64"
	"errors"
	"fmt"
	"math/big"

	"git.rootprojects.org/root/keypairs"
)

type JWK interface {
	marshalJWK() ([]byte, error)
}

type ECJWK struct {
	KeyID string   `json:"kid,omitempty"`
	Curve string   `json:"crv"`
	X     string   `json:"x"`
	Y     string   `json:"y"`
	Use   []string `json:"use,omitempty"`
	Seed  string   `json:"_seed,omitempty"`
}

func (k *ECJWK) marshalJWK() ([]byte, error) {
	return []byte(fmt.Sprintf(`{"crv":%q,"kty":"EC","x":%q,"y":%q}`, k.Curve, k.X, k.Y)), nil
}

type RSAJWK struct {
	KeyID string   `json:"kid,omitempty"`
	Exp   string   `json:"e"`
	N     string   `json"n"`
	Use   []string `json:"use,omitempty"`
	Seed  string   `json:"_seed,omitempty"`
}

func (k *RSAJWK) marshalJWK() ([]byte, error) {
	return []byte(fmt.Sprintf(`{"e":%q,"kty":"RSA","n":%q}`, k.Exp, k.N)), nil
}

func ToPublicJWK(pubkey keypairs.PublicKey) JWK {
	switch k := pubkey.Key().(type) {
	case *ecdsa.PublicKey:
		return ECToPublicJWK(k)
	case *rsa.PublicKey:
		return RSAToPublicJWK(k)
	default:
		panic(errors.New("impossible key type"))
		return nil
	}
}

// ECToPublicJWK will output the most minimal version of an EC JWK (no key id, no "use" flag, nada)
func ECToPublicJWK(k *ecdsa.PublicKey) *ECJWK {
	return &ECJWK{
		Curve: k.Curve.Params().Name,
		X:     base64.RawURLEncoding.EncodeToString(k.X.Bytes()),
		Y:     base64.RawURLEncoding.EncodeToString(k.Y.Bytes()),
	}
}

// RSAToPublicJWK will output the most minimal version of an RSA JWK (no key id, no "use" flag, nada)
func RSAToPublicJWK(p *rsa.PublicKey) *RSAJWK {
	return &RSAJWK{
		Exp: base64.RawURLEncoding.EncodeToString(big.NewInt(int64(p.E)).Bytes()),
		N:   base64.RawURLEncoding.EncodeToString(p.N.Bytes()),
	}
}