package mapping

import (
	"context"

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

func ToWireBeginAuthorizationRequest(ctx context.Context, request semantic.BeginRequest) (*runtimev1.BeginAuthorizationRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateBeginRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.BeginAuthorizationRequest{
		AppInstallationId: request.AppInstallationID, ClientId: request.ClientID,
		RedirectUri: request.RedirectURI, ResponseType: request.ResponseType, Scope: request.Scope,
		State: request.State, CodeChallenge: request.CodeChallenge,
		CodeChallengeMethod: request.CodeChallengeMethod, Issuer: request.Issuer,
		Resource: request.Resource, ResourcePresent: request.ResourcePresent,
	}, nil
}

func FromWireBeginAuthorizationRequest(ctx context.Context, request *runtimev1.BeginAuthorizationRequest) (semantic.BeginRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.BeginRequest{}, err
	}
	if request == nil {
		return semantic.BeginRequest{}, &semantic.Failure{Category: semantic.FailureInvalidRequest, InvalidComponent: semantic.InvalidComponentOperation}
	}
	value := semantic.BeginRequest{
		AppInstallationID: request.GetAppInstallationId(), ClientID: request.GetClientId(),
		RedirectURI: request.GetRedirectUri(), ResponseType: request.GetResponseType(), Scope: request.GetScope(),
		State: request.GetState(), CodeChallenge: request.GetCodeChallenge(),
		CodeChallengeMethod: request.GetCodeChallengeMethod(), Issuer: request.GetIssuer(),
		Resource: request.GetResource(), ResourcePresent: request.GetResourcePresent(),
	}
	return value, semantic.ValidateBeginRequest(ctx, value)
}

func ToWireBeginAuthorizationResponse(response semantic.BeginResponse) (*runtimev1.BeginAuthorizationResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateBeginResponse(response); err != nil {
		return nil, err
	}
	return &runtimev1.BeginAuthorizationResponse{RequestHandle: response.RequestHandle,
		ExpiresAtUnixSecond: response.ExpiresAtUnixSecond, ProtocolError: toWireAuthorizationProtocolError(response.ProtocolError)}, nil
}

func FromWireBeginAuthorizationResponse(response *runtimev1.BeginAuthorizationResponse) (semantic.BeginResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.BeginResponse{}, err
	}
	if response == nil {
		return semantic.BeginResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
	}
	value := semantic.BeginResponse{RequestHandle: response.GetRequestHandle(), ExpiresAtUnixSecond: response.GetExpiresAtUnixSecond(),
		ProtocolError: fromWireAuthorizationProtocolError(response.GetProtocolError())}
	return value, semantic.ValidateBeginResponse(value)
}

func ToWireCompleteAuthorizationRequest(ctx context.Context, request semantic.CompleteRequest) (*runtimev1.CompleteAuthorizationRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateCompleteRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.CompleteAuthorizationRequest{AppInstallationId: request.AppInstallationID, RequestHandle: request.RequestHandle}, nil
}

func FromWireCompleteAuthorizationRequest(ctx context.Context, request *runtimev1.CompleteAuthorizationRequest) (semantic.CompleteRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.CompleteRequest{}, err
	}
	if request == nil {
		return semantic.CompleteRequest{}, &semantic.Failure{Category: semantic.FailureInvalidRequest, InvalidComponent: semantic.InvalidComponentOperation}
	}
	value := semantic.CompleteRequest{AppInstallationID: request.GetAppInstallationId(), RequestHandle: request.GetRequestHandle()}
	return value, semantic.ValidateCompleteRequest(ctx, value)
}

func ToWireCompleteAuthorizationResponse(response semantic.CompleteResponse) (*runtimev1.CompleteAuthorizationResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateCompleteResponse(response); err != nil {
		return nil, err
	}
	return &runtimev1.CompleteAuthorizationResponse{AuthorizationCodeIssued: response.AuthorizationCodeIssued,
		RedirectUri: response.RedirectURI, State: response.State,
		ProtocolError: toWireAuthorizationProtocolError(response.ProtocolError)}, nil
}

func FromWireCompleteAuthorizationResponse(response *runtimev1.CompleteAuthorizationResponse) (semantic.CompleteResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.CompleteResponse{}, err
	}
	if response == nil {
		return semantic.CompleteResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
	}
	value := semantic.CompleteResponse{AuthorizationCodeIssued: response.GetAuthorizationCodeIssued(), RedirectURI: response.GetRedirectUri(), State: response.GetState(),
		ProtocolError: fromWireAuthorizationProtocolError(response.GetProtocolError())}
	return value, semantic.ValidateCompleteResponse(value)
}

func ToWireExchangeAuthorizationTokenRequest(ctx context.Context, request semantic.ExchangeRequest) (*runtimev1.ExchangeAuthorizationTokenRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateExchangeRequest(ctx, request); err != nil {
		return nil, err
	}
	grant := runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_UNSPECIFIED
	switch request.GrantType {
	case semantic.GrantTypeAuthorizationCode:
		grant = runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_AUTHORIZATION_CODE
	case semantic.GrantTypeRefreshToken:
		grant = runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_REFRESH_TOKEN
	case semantic.GrantTypeClientCredentials:
		grant = runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_CLIENT_CREDENTIALS
	}
	// Semantic grants and the nested wire module publish independently. Never
	// collapse a newly valid semantic grant into the wire UNSPECIFIED value
	// while its enum and mapping are still awaiting wire publication.
	if grant == runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_UNSPECIFIED {
		return nil, &semantic.Failure{Category: semantic.FailureInvalidRequest, InvalidComponent: semantic.InvalidComponentOperation}
	}
	return &runtimev1.ExchangeAuthorizationTokenRequest{AppInstallationId: request.AppInstallationID, GrantType: grant,
		ClientId: request.ClientID, RedirectUri: request.RedirectURI, RequestedScope: request.RequestedScope,
		Issuer: request.Issuer, Resource: request.Resource, ResourcePresent: request.ResourcePresent}, nil
}

func FromWireExchangeAuthorizationTokenRequest(ctx context.Context, request *runtimev1.ExchangeAuthorizationTokenRequest) (semantic.ExchangeRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ExchangeRequest{}, err
	}
	if request == nil {
		return semantic.ExchangeRequest{}, &semantic.Failure{Category: semantic.FailureInvalidRequest, InvalidComponent: semantic.InvalidComponentOperation}
	}
	grant := semantic.GrantType("")
	switch request.GetGrantType() {
	case runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_AUTHORIZATION_CODE:
		grant = semantic.GrantTypeAuthorizationCode
	case runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_REFRESH_TOKEN:
		grant = semantic.GrantTypeRefreshToken
	case runtimev1.AuthorizationGrantType_AUTHORIZATION_GRANT_TYPE_CLIENT_CREDENTIALS:
		grant = semantic.GrantTypeClientCredentials
	}
	value := semantic.ExchangeRequest{AppInstallationID: request.GetAppInstallationId(), GrantType: grant,
		ClientID: request.GetClientId(), RedirectURI: request.GetRedirectUri(), RequestedScope: request.GetRequestedScope(),
		Issuer: request.GetIssuer(), Resource: request.GetResource(), ResourcePresent: request.GetResourcePresent()}
	return value, semantic.ValidateExchangeRequest(ctx, value)
}

func ToWireExchangeAuthorizationTokenResponse(response semantic.ExchangeResponse) (*runtimev1.ExchangeAuthorizationTokenResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateExchangeResponse(response); err != nil {
		return nil, err
	}
	value := &runtimev1.ExchangeAuthorizationTokenResponse{ProtocolError: toWireAuthorizationProtocolError(response.ProtocolError)}
	if response.Token != nil {
		value.Token = &runtimev1.AuthorizationTokenView{TokenType: response.Token.TokenType, ExpiresInSecond: response.Token.ExpiresInSecond,
			Scope: response.Token.Scope, AccessTokenIssued: response.Token.AccessTokenIssued,
			RefreshTokenIssued: response.Token.RefreshTokenIssued}
	}
	return value, nil
}

func FromWireExchangeAuthorizationTokenResponse(response *runtimev1.ExchangeAuthorizationTokenResponse) (semantic.ExchangeResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ExchangeResponse{}, err
	}
	if response == nil {
		return semantic.ExchangeResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
	}
	value := semantic.ExchangeResponse{ProtocolError: fromWireAuthorizationProtocolError(response.GetProtocolError())}
	if token := response.GetToken(); token != nil {
		value.Token = &semantic.Token{TokenType: token.GetTokenType(), ExpiresInSecond: token.GetExpiresInSecond(),
			Scope: token.GetScope(), AccessTokenIssued: token.GetAccessTokenIssued(),
			RefreshTokenIssued: token.GetRefreshTokenIssued()}
	}
	return value, semantic.ValidateExchangeResponse(value)
}

func toWireAuthorizationProtocolError(value *semantic.ProtocolError) *runtimev1.AuthorizationProtocolError {
	if value == nil {
		return nil
	}
	return &runtimev1.AuthorizationProtocolError{Code: value.Code, SafeDescription: value.SafeDescription,
		TrustedRedirectUri: value.TrustedRedirectURI, State: value.State}
}

func fromWireAuthorizationProtocolError(value *runtimev1.AuthorizationProtocolError) *semantic.ProtocolError {
	if value == nil {
		return nil
	}
	return &semantic.ProtocolError{Code: value.GetCode(), SafeDescription: value.GetSafeDescription(),
		TrustedRedirectURI: value.GetTrustedRedirectUri(), State: value.GetState()}
}
