package auth

import (
	"context"
	"github.com/Timothylock/go-signin-with-apple/apple"
	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/encryption"
	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils"
	"google.golang.org/api/idtoken"
)

type AppleAuth struct {
	Code    string `json:"code"`
	IDToken string `json:"id_token"`
	State   string `json:"state"`
}

type SocialCredentials struct {
	Google string     `json:"google"`
	Apple  *AppleAuth `json:"apple"`
}

type LoginResponse struct {
	AccessToken string `json:"access_token"`
}

func ValidateGoogleIDToken(tokenString, clientID string) (string, error) {
	payload, err := idtoken.Validate(context.Background(), tokenString, clientID)
	if err != nil {
		return "", err
	}

	email, ok := payload.Claims["email"].(string)
	if !ok {
		return "", errors.Error("email is not a string")
	}

	return email, nil
}

func ValidateAppleCode(code, redirectURI, encryptionKeySecret string, isDebug bool) (string, error) {
	teamID := "7978M5K9YV"
	clientID := "za.co.bob.auth.client"
	keyID := "6X88ZTK5S4"

	signingKey, err := encryption.GetAppleSigningKey(encryptionKeySecret, isDebug)
	if err != nil {
		return "", errors.Error("apple signing key not set up")
	}

	clientSecret, err := apple.GenerateClientSecret(signingKey, teamID, clientID, keyID)
	if err != nil {
		return "", err
	}

	client := apple.New()

	var validationResponse apple.ValidationResponse

	err = client.VerifyWebToken(context.Background(), apple.WebValidationTokenRequest{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		RedirectURI:  redirectURI,
		Code:         code,
	}, &validationResponse)

	if err != nil {
		return "", err
	}

	if validationResponse.ErrorDescription != "" {
		panic(validationResponse.ErrorDescription)
	}

	claim, _ := apple.GetClaims(validationResponse.IDToken)
	if claim == nil {
		return "", errors.Error("invalid apple token")
	}

	email := (*claim)["email"]
	emailVerified := (*claim)["email_verified"]

	if emailVerified != true {
		return "", errors.Error("email not verified")
	}

	return string_utils.InterfaceToString(email)
}

func ExtractAppleEmailFromIDToken(idToken string) (string, error) {
	claim, err := apple.GetClaims(idToken)
	if err != nil {
		return "", err
	}

	email := (*claim)["email"]
	emailVerified := (*claim)["email_verified"]

	if emailVerified != true {
		return "", errors.Error("email not verified")
	}

	return string_utils.InterfaceToString(email)
}

func HasSocialCredentials(socialCredentials SocialCredentials) bool {
	return socialCredentials.Google != "" || (socialCredentials.Apple != nil && socialCredentials.Apple.IDToken != "" && socialCredentials.Apple.Code != "")
}