package mapping

import (
	"context"
	"reflect"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/authorizationv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
)

func authorizationMappingDeadline(t *testing.T) context.Context {
	t.Helper()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	t.Cleanup(cancel)
	return ctx
}

func TestAuthorizationMappingRoundTrip(t *testing.T) {
	ctx := authorizationMappingDeadline(t)
	begin := semantic.BeginRequest{AppInstallationID: "installation-1", ClientID: "public-client",
		RedirectURI: "https://client.example/callback", ResponseType: "code", Scope: "api", State: "state-1",
		CodeChallenge: "challenge", CodeChallengeMethod: "S256", Issuer: "https://crm.example", ResourcePresent: true}
	wBegin, err := ToWireBeginAuthorizationRequest(ctx, begin)
	if err != nil {
		t.Fatal(err)
	}
	decodedBegin, err := FromWireBeginAuthorizationRequest(ctx, wBegin)
	if err != nil || !reflect.DeepEqual(decodedBegin, begin) {
		t.Fatalf("begin = %#v, %v", decodedBegin, err)
	}

	beginError := semantic.BeginResponse{ProtocolError: &semantic.ProtocolError{Code: "invalid_scope",
		SafeDescription: "scope is not allowed", TrustedRedirectURI: begin.RedirectURI, State: begin.State}}
	wBeginError, err := ToWireBeginAuthorizationResponse(beginError)
	if err != nil {
		t.Fatal(err)
	}
	decodedBeginError, err := FromWireBeginAuthorizationResponse(wBeginError)
	if err != nil || !reflect.DeepEqual(decodedBeginError, beginError) {
		t.Fatalf("begin result = %#v, %v", decodedBeginError, err)
	}

	exchange := semantic.ExchangeRequest{AppInstallationID: "installation-1", GrantType: semantic.GrantTypeAuthorizationCode,
		ClientID: "public-client", RedirectURI: begin.RedirectURI, Issuer: begin.Issuer, ResourcePresent: true}
	wExchange, err := ToWireExchangeAuthorizationTokenRequest(ctx, exchange)
	if err != nil {
		t.Fatal(err)
	}
	decodedExchange, err := FromWireExchangeAuthorizationTokenRequest(ctx, wExchange)
	if err != nil || !reflect.DeepEqual(decodedExchange, exchange) {
		t.Fatalf("exchange = %#v, %v", decodedExchange, err)
	}

	token := semantic.ExchangeResponse{Token: &semantic.Token{TokenType: "Bearer", ExpiresInSecond: 3600, Scope: "api",
		AccessTokenIssued: true, RefreshTokenIssued: true}}
	wToken, err := ToWireExchangeAuthorizationTokenResponse(token)
	if err != nil {
		t.Fatal(err)
	}
	decodedToken, err := FromWireExchangeAuthorizationTokenResponse(wToken)
	if err != nil || !reflect.DeepEqual(decodedToken, token) {
		t.Fatalf("token = %#v, %v", decodedToken, err)
	}
}

func TestAuthorizationClientCredentialsMappingRoundTrip(t *testing.T) {
	request := semantic.ExchangeRequest{
		AppInstallationID: "installation-1",
		GrantType:         semantic.GrantTypeClientCredentials,
		ClientID:          "machine-client",
		RequestedScope:    "api",
		Issuer:            "https://crm.example",
	}
	wire, err := ToWireExchangeAuthorizationTokenRequest(authorizationMappingDeadline(t), request)
	if err != nil {
		t.Fatal(err)
	}
	if wire.GetGrantType() != runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_CLIENT_CREDENTIALS {
		t.Fatalf("wire grant = %v", wire.GetGrantType())
	}
	decoded, err := FromWireExchangeAuthorizationTokenRequest(authorizationMappingDeadline(t), wire)
	if err != nil || !reflect.DeepEqual(decoded, request) {
		t.Fatalf("client credentials exchange = %#v, %v", decoded, err)
	}
}
