package test

import (
	authservice2 "authservice/api/authservice"
	"authservice/internal/di"
	"authservice/internal/service"
	context "context"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha512"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"google.golang.org/grpc/metadata"
	"testing"
)

func TestAppBaseScenario(t *testing.T) {

	service := di.WireAppComponentTesting()

	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		t.Error(err)
	}
	pubKeyBytes, err := x509.MarshalPKIXPublicKey(&rsaKey.PublicKey)
	if err != nil {
		t.Error(err)
	}
	readableKey := string(pubKeyBytes)
	pubKeyPem := pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PUBLIC KEY",
			Bytes: pubKeyBytes,
		},
	)
	readableKey = string(pubKeyPem)
	print(readableKey)
	encodedKey := base64.StdEncoding.EncodeToString(pubKeyPem)
	request := &authservice2.CredentialsRequest{
		Email:    "test@test.com",
		Password: "1234567",
		Key:      encodedKey,
	}
	jwt := startSignUpTest(t, service, request)
	startSigOutTest(t, service, jwt.AccessToken, &authservice2.Request{})

	startRenewTokenAfterLogOutTest(t, service, &authservice2.RenewRequest{
		RefreshToken: jwt.RefreshToken,
	})
	jwt = startSignInTest(t, service, request)
	jwt = startRenewTokenTest(t, service, &authservice2.RenewRequest{
		RefreshToken: jwt.RefreshToken,
	})

	startVerifyTest(t, service, jwt.AccessToken, rsaKey)
}

func startSignUpTest(t *testing.T, service *service.AuthService, request *authservice2.CredentialsRequest) *authservice2.JWT {
	if service == nil {
		t.Error("cant create service")
	}

	ctx := context.Background()

	response, err := service.SignUp(ctx, request)
	if err != nil {
		t.Error(err)
	}

	if response == nil {
		t.Error("response is nil")
	}
	return response.Jwt
}

func startSigOutTest(t *testing.T, service *service.AuthService, jwt string, request *authservice2.Request) {
	if service == nil {
		t.Error("cant create service")
	}

	ctx := context.Background()
	md := metadata.Pairs("Authorization", jwt)
	ctxWithMD := metadata.NewIncomingContext(ctx, md)
	response, err := service.SignOut(ctxWithMD, request)
	if err != nil {
		t.Error(err)
	}

	if response == nil {
		t.Error("response is nil")
	}
}

func startRenewTokenAfterLogOutTest(t *testing.T, service *service.AuthService, request *authservice2.RenewRequest) {
	ctx := context.Background()
	_, err := service.RenewToken(ctx, request)

	if err.Error() != "invalid provided public key" {
		t.Error(err)
	}
}

func startRenewTokenTest(t *testing.T, service *service.AuthService, request *authservice2.RenewRequest) *authservice2.JWT {
	ctx := context.Background()
	result, err := service.RenewToken(ctx, request)

	if err != nil {
		t.Error(err)
	}
	return result.Jwt
}

func startSignInTest(t *testing.T, service *service.AuthService, request *authservice2.CredentialsRequest) *authservice2.JWT {
	ctx := context.Background()
	response, err := service.SignIn(ctx, request)
	if err != nil {
		t.Error(err)
	}
	return response.Jwt
}

func startVerifyTest(t *testing.T, service *service.AuthService, jwt string, key *rsa.PrivateKey) {
	ctx := context.Background()

	message := []byte("message to be signed")
	hashed := sha512.New()
	hashed.Write(message)
	output := hashed.Sum(nil)
	fmt.Printf("%x\n", output)

	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA512, output)
	md := metadata.Pairs(
		"Authorization", jwt,
		"Signature", base64.StdEncoding.EncodeToString(signature),
		"Digest", base64.StdEncoding.EncodeToString(message))
	ctx = metadata.NewIncomingContext(ctx, md)

	response, err := service.Verify(ctx, &authservice2.Request{})
	if err != nil {
		t.Error(err)
	}

	if !response.Valid {
		t.Error(err)
	}
}
