package rpc

import (
	"context"
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"time"

	"github.com/Timothylock/go-signin-with-apple/apple"
	"github.com/golang-jwt/jwt/v5"
)

type AppleClient struct {
	*apple.Client
	teamID   string
	clientID string
	keyID    string
	secret   string
}

// GenerateClientSecret generates a new client secret, all fields are required, no http request, fully local logic
func (a *AppleClient) GenerateClientSecret() (string, error) {
	token := &jwt.Token{
		Header: map[string]interface{}{
			"alg": "ES256",
			"kid": a.keyID,
		},
		Claims: jwt.MapClaims{
			"iss": a.teamID,
			"iat": time.Now().Unix(),
			// constraint: exp - iat <= 180 days
			"exp": time.Now().Add(24 * time.Hour).Unix(),
			"aud": "https://appleid.apple.com",
			"sub": a.clientID,
		},
		Method: jwt.SigningMethodES256,
	}

	ecdsaKey, _ := authKeyFromBytes([]byte(a.secret))
	return token.SignedString(ecdsaKey)
}

// authKeyFromBytes create private key for jwt sign
func authKeyFromBytes(key []byte) (*ecdsa.PrivateKey, error) {
	var err error

	var block *pem.Block
	if block, _ = pem.Decode(key); block == nil {
		return nil, errors.New("token: AuthKey must be a valid .p8 PEM file")
	}

	var parsedKey interface{}
	if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
		return nil, err
	}

	var pkey *ecdsa.PrivateKey
	var ok bool
	if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
		return nil, errors.New("token: AuthKey must be of type ecdsa.PrivateKey")
	}

	return pkey, nil
}

func (a *AppleClient) VerifyAppToken(ctx context.Context, code string) (apple.ValidationResponse, error) {
	var resp apple.ValidationResponse
	clientSecret, err := a.GenerateClientSecret()
	if err != nil {
		return resp, err
	}

	reqBody := apple.AppValidationTokenRequest{
		ClientID:     a.clientID,
		ClientSecret: clientSecret,
		Code:         code,
	}
	if err := a.Client.VerifyAppToken(ctx, reqBody, &resp); err != nil {
		return resp, err
	}

	return resp, nil
}