package runtimeapi

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/federatedloginv1"
	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
)

const federatedLoginProcessTestState = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"

func (engine *authorizationProcessEngine) BeginFederatedLogin(ctx context.Context, authority RuntimeWorkloadCall, request semantic.BeginRequest) (semantic.BeginResponse, error) {
	engine.assertBoundary(ctx, authority, request.AppInstallationID)
	if request.RedirectURI != "https://app.example.test/oauth/callback" {
		engine.t.Fatalf("begin request=%#v", request)
	}
	return semantic.BeginResponse{
		State: federatedLoginProcessTestState, ExpiresAtUnixSecond: time.Now().Add(time.Minute).Unix(),
	}, nil
}

func (engine *authorizationProcessEngine) ConsumeFederatedLogin(ctx context.Context, authority RuntimeWorkloadCall, request semantic.ConsumeRequest) error {
	engine.assertBoundary(ctx, authority, request.AppInstallationID)
	if request.State != federatedLoginProcessTestState || request.RedirectURI != "https://app.example.test/oauth/callback" {
		engine.t.Fatalf("consume request=%#v", request)
	}
	return nil
}

func TestRuntimeAPIProcessServesFederatedLoginTransactionOnExistingEndpoint(t *testing.T) {
	engine := &authorizationProcessEngine{t: t}
	process, err := Assemble(ProcessConfig{
		Version: "federated-login-process-test", AdminAddress: "127.0.0.1:0", GRPCAddress: "127.0.0.1:0",
		AdmissionLimit: 2, ServingEnabled: true,
		WorkloadAuthority: func(context.Context, string) (runtimegrpc.VerifiedCaller, error) {
			return runtimegrpc.VerifiedCaller(testWorkloadID), nil
		},
		SecurityAdmission: testSecurityAdmission(t), Receipts: ReceiptSourceFunc(BootstrapReceipts),
		IdentityExchange: func(context.Context, identitysemantic.Request) (identitysemantic.Response, error) {
			return identitysemantic.Response{}, errors.New("not used")
		},
		IdentityAttemptCap: time.Second, SessionLifecycle: &sessionProcessEngine{t: t},
		AuthorizationLifecycle: engine, FederatedLoginTransactions: engine,
	}, &readerStub{})
	if err != nil {
		t.Fatal(err)
	}
	if err := process.Start(); err != nil {
		t.Fatal(err)
	}
	defer stopProcess(t, process)
	connection, err := grpc.NewClient("dns:///"+process.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		t.Fatal(err)
	}
	defer connection.Close()
	wireClient := runtimev1.NewFederatedLoginTransactionServiceClient(connection)
	client, err := runtimegrpc.NewFederatedLoginTransactionClient(wireClient.BeginFederatedLogin, wireClient.ConsumeFederatedLogin)
	if err != nil {
		t.Fatal(err)
	}
	base := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer workload-secret")
	ctx, cancel := context.WithTimeout(base, time.Second)
	defer cancel()
	begin, err := client.Begin(ctx, semantic.BeginRequest{
		AppInstallationID: testInstallationID, RedirectURI: "https://app.example.test/oauth/callback",
	})
	if err != nil || begin.State != federatedLoginProcessTestState {
		t.Fatalf("begin=%#v err=%v", begin, err)
	}
	if err := client.Consume(ctx, semantic.ConsumeRequest{
		AppInstallationID: testInstallationID, State: begin.State,
		RedirectURI: "https://app.example.test/oauth/callback",
	}); err != nil {
		t.Fatal(err)
	}
}
