package mapping

import (
	"context"

	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionadminv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/protobuf/proto"
)

func ToWireReadPermissionAdministrationRequest(ctx context.Context, request semantic.ReadRequest) (*runtimev1.ReadPermissionAdministrationRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateReadRequest(ctx, request); err != nil {
		return nil, err
	}
	selector, ok := toWirePermissionAdministrationReadSelector(request.Selector)
	if !ok {
		return nil, invalidPlatformWire()
	}
	return &runtimev1.ReadPermissionAdministrationRequest{
		Selector: selector, PermissionSetId: request.PermissionSetID,
		MinCatalogRevision: request.MinCatalogRevision,
	}, nil
}

func FromWireReadPermissionAdministrationRequest(ctx context.Context, request *runtimev1.ReadPermissionAdministrationRequest) (semantic.ReadRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ReadRequest{}, err
	}
	if request == nil {
		return semantic.ReadRequest{}, invalidPlatformWire()
	}
	selector, ok := fromWirePermissionAdministrationReadSelector(request.GetSelector())
	if !ok {
		return semantic.ReadRequest{}, invalidPlatformWire()
	}
	if err := commonsemantic.ValidateRequestContext(ctx); err != nil {
		return semantic.ReadRequest{}, err
	}
	if err := preflightWirePermissionAdministrationReadRequest(request); err != nil {
		return semantic.ReadRequest{}, err
	}
	result := semantic.ReadRequest{
		Selector: selector, PermissionSetID: request.GetPermissionSetId(),
		MinCatalogRevision: request.MinCatalogRevision,
	}
	if err := semantic.ValidateReadRequest(ctx, result); err != nil {
		return semantic.ReadRequest{}, err
	}
	return result, nil
}

func ToWireReadPermissionAdministrationResponse(request semantic.ReadRequest, response semantic.ReadResponse) (*runtimev1.ReadPermissionAdministrationResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateReadResponse(request, response); err != nil {
		return nil, err
	}
	result := &runtimev1.ReadPermissionAdministrationResponse{
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}
	for _, value := range response.SystemCatalog {
		result.SystemCatalog = append(result.SystemCatalog, &runtimev1.PermissionAdministrationSystemPermission{
			PermissionId: value.PermissionID, Label: value.Label, Category: value.Category,
			Description: value.Description, RequiresPermissionId: value.RequiresPermissionID,
		})
	}
	result.SystemGrants = toWirePermissionAdministrationGrants(response.SystemGrants)
	for _, value := range response.RecordTypeCatalog {
		result.RecordTypeCatalog = append(result.RecordTypeCatalog, &runtimev1.PermissionAdministrationRecordType{
			RecordTypeId: value.RecordTypeID, ResourceType: value.ResourceType, StableName: value.StableName,
			Label: value.Label, Active: value.Active,
		})
	}
	result.RecordTypeGrants = toWirePermissionAdministrationGrants(response.RecordTypeGrants)
	for _, value := range response.CustomCatalog {
		result.CustomCatalog = append(result.CustomCatalog, &runtimev1.PermissionAdministrationCustomPermission{
			PermissionId: value.PermissionID, StableName: value.StableName, Label: value.Label,
		})
	}
	result.CustomGrants = toWirePermissionAdministrationGrants(response.CustomGrants)
	return result, nil
}

func FromWireReadPermissionAdministrationResponse(request semantic.ReadRequest, response *runtimev1.ReadPermissionAdministrationResponse) (semantic.ReadResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ReadResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.ReadResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.ReadResponse{}, err
	}
	if err := preflightWirePermissionAdministrationReadResponse(request.Selector, response); err != nil {
		return semantic.ReadResponse{}, err
	}
	result := semantic.ReadResponse{Receipts: receipts}
	for _, value := range response.GetSystemCatalog() {
		if value == nil {
			return semantic.ReadResponse{}, internalPlatformWire()
		}
		result.SystemCatalog = append(result.SystemCatalog, semantic.SystemPermission{
			PermissionID: value.GetPermissionId(), Label: value.GetLabel(), Category: value.GetCategory(),
			Description: value.GetDescription(), RequiresPermissionID: value.GetRequiresPermissionId(),
		})
	}
	result.SystemGrants, err = fromWirePermissionAdministrationGrants(response.GetSystemGrants())
	if err != nil {
		return semantic.ReadResponse{}, err
	}
	for _, value := range response.GetRecordTypeCatalog() {
		if value == nil {
			return semantic.ReadResponse{}, internalPlatformWire()
		}
		result.RecordTypeCatalog = append(result.RecordTypeCatalog, semantic.RecordType{
			RecordTypeID: value.GetRecordTypeId(), ResourceType: value.GetResourceType(), StableName: value.GetStableName(),
			Label: value.GetLabel(), Active: value.GetActive(),
		})
	}
	result.RecordTypeGrants, err = fromWirePermissionAdministrationGrants(response.GetRecordTypeGrants())
	if err != nil {
		return semantic.ReadResponse{}, err
	}
	for _, value := range response.GetCustomCatalog() {
		if value == nil {
			return semantic.ReadResponse{}, internalPlatformWire()
		}
		result.CustomCatalog = append(result.CustomCatalog, semantic.CustomPermission{
			PermissionID: value.GetPermissionId(), StableName: value.GetStableName(), Label: value.GetLabel(),
		})
	}
	result.CustomGrants, err = fromWirePermissionAdministrationGrants(response.GetCustomGrants())
	if err != nil {
		return semantic.ReadResponse{}, err
	}
	if err := semantic.ValidateReadResponse(request, result); err != nil {
		return semantic.ReadResponse{}, err
	}
	return result, nil
}

func ToWireMutatePermissionAdministrationRequest(ctx context.Context, request semantic.MutateRequest) (*runtimev1.MutatePermissionAdministrationRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateMutateRequest(ctx, request); err != nil {
		return nil, err
	}
	selector, ok := toWirePermissionAdministrationMutationSelector(request.Selector)
	if !ok {
		return nil, invalidPlatformWire()
	}
	return &runtimev1.MutatePermissionAdministrationRequest{
		Selector: selector, PermissionSetId: request.PermissionSetID,
		Grants:             toWirePermissionAdministrationGrants(request.Grants),
		MinCatalogRevision: request.MinCatalogRevision,
	}, nil
}

func FromWireMutatePermissionAdministrationRequest(ctx context.Context, request *runtimev1.MutatePermissionAdministrationRequest) (semantic.MutateRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.MutateRequest{}, err
	}
	if request == nil {
		return semantic.MutateRequest{}, invalidPlatformWire()
	}
	selector, ok := fromWirePermissionAdministrationMutationSelector(request.GetSelector())
	if !ok {
		return semantic.MutateRequest{}, invalidPlatformWire()
	}
	if err := commonsemantic.ValidateRequestContext(ctx); err != nil {
		return semantic.MutateRequest{}, err
	}
	if err := preflightWirePermissionAdministrationMutateRequest(request); err != nil {
		return semantic.MutateRequest{}, err
	}
	grants, err := fromWirePermissionAdministrationGrants(request.GetGrants())
	if err != nil {
		return semantic.MutateRequest{}, err
	}
	result := semantic.MutateRequest{
		Selector: selector, PermissionSetID: request.GetPermissionSetId(), Grants: grants,
		MinCatalogRevision: request.MinCatalogRevision,
	}
	if err := semantic.ValidateMutateRequest(ctx, result); err != nil {
		return semantic.MutateRequest{}, err
	}
	return result, nil
}

func ToWireMutatePermissionAdministrationResponse(request semantic.MutateRequest, response semantic.MutateResponse) (*runtimev1.MutatePermissionAdministrationResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateMutateResponse(request, response); err != nil {
		return nil, err
	}
	return &runtimev1.MutatePermissionAdministrationResponse{
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireMutatePermissionAdministrationResponse(request semantic.MutateRequest, response *runtimev1.MutatePermissionAdministrationResponse) (semantic.MutateResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.MutateResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.MutateResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.MutateResponse{}, err
	}
	if err := preflightWirePermissionAdministrationMutateResponse(response); err != nil {
		return semantic.MutateResponse{}, err
	}
	result := semantic.MutateResponse{Receipts: receipts}
	if err := semantic.ValidateMutateResponse(request, result); err != nil {
		return semantic.MutateResponse{}, err
	}
	return result, nil
}

func preflightWirePermissionAdministrationReadRequest(request *runtimev1.ReadPermissionAdministrationRequest) error {
	budget := newWireStringBudget(semantic.MaxSemanticRequestBytes)
	if !budget.take(request.GetPermissionSetId()) {
		return platformLimitFailure(commonsemantic.LimitSemanticRequest, semantic.MaxSemanticRequestBytes)
	}
	if proto.Size(request) > envelope.MaxRequestWireBytes {
		return platformLimitFailure(commonsemantic.LimitWireFrame, envelope.MaxRequestWireBytes)
	}
	return nil
}

func preflightWirePermissionAdministrationReadResponse(selector semantic.ReadSelector, response *runtimev1.ReadPermissionAdministrationResponse) error {
	if !wirePermissionAdministrationResponseSelectsOneFamily(selector, response) {
		return internalPlatformWire()
	}
	budget := newWireStringBudget(semantic.MaxSemanticResponseBytes)
	if !budget.takeValidatedReceiptBytes(response.GetReceipts()) {
		return internalPlatformWire()
	}
	for _, value := range response.GetSystemCatalog() {
		if value == nil || !budget.take(value.GetPermissionId(), value.GetLabel(), value.GetCategory(), value.GetDescription(), value.GetRequiresPermissionId()) {
			return internalPlatformWire()
		}
	}
	for _, grants := range [][]*runtimev1.PermissionAdministrationGrant{
		response.GetSystemGrants(), response.GetRecordTypeGrants(), response.GetCustomGrants(),
	} {
		for _, value := range grants {
			if value == nil || !budget.take(value.GetPermissionId()) {
				return internalPlatformWire()
			}
		}
	}
	for _, value := range response.GetRecordTypeCatalog() {
		if value == nil || !budget.take(value.GetRecordTypeId(), value.GetResourceType(), value.GetStableName(), value.GetLabel()) {
			return internalPlatformWire()
		}
	}
	for _, value := range response.GetCustomCatalog() {
		if value == nil || !budget.take(value.GetPermissionId(), value.GetStableName(), value.GetLabel()) {
			return internalPlatformWire()
		}
	}
	if proto.Size(response) > envelope.MaxResponseWireBytes {
		return internalPlatformWire()
	}
	return nil
}

func wirePermissionAdministrationResponseSelectsOneFamily(selector semantic.ReadSelector, response *runtimev1.ReadPermissionAdministrationResponse) bool {
	systemCatalog, systemGrants := len(response.GetSystemCatalog()), len(response.GetSystemGrants())
	recordTypeCatalog, recordTypeGrants := len(response.GetRecordTypeCatalog()), len(response.GetRecordTypeGrants())
	customCatalog, customGrants := len(response.GetCustomCatalog()), len(response.GetCustomGrants())
	switch selector {
	case semantic.ReadSystemCatalog:
		return systemCatalog <= semantic.MaxCatalogEntries && systemGrants == 0 && recordTypeCatalog == 0 && recordTypeGrants == 0 && customCatalog == 0 && customGrants == 0
	case semantic.ReadSystemGrants:
		return systemCatalog == 0 && systemGrants <= semantic.MaxGrants && recordTypeCatalog == 0 && recordTypeGrants == 0 && customCatalog == 0 && customGrants == 0
	case semantic.ReadRecordTypeCatalog:
		return systemCatalog == 0 && systemGrants == 0 && recordTypeCatalog <= semantic.MaxCatalogEntries && recordTypeGrants == 0 && customCatalog == 0 && customGrants == 0
	case semantic.ReadRecordTypeGrants:
		return systemCatalog == 0 && systemGrants == 0 && recordTypeCatalog == 0 && recordTypeGrants <= semantic.MaxGrants && customCatalog == 0 && customGrants == 0
	case semantic.ReadCustomCatalog:
		return systemCatalog == 0 && systemGrants == 0 && recordTypeCatalog == 0 && recordTypeGrants == 0 && customCatalog <= semantic.MaxCatalogEntries && customGrants == 0
	case semantic.ReadCustomGrants:
		return systemCatalog == 0 && systemGrants == 0 && recordTypeCatalog == 0 && recordTypeGrants == 0 && customCatalog == 0 && customGrants <= semantic.MaxGrants
	default:
		return false
	}
}

func preflightWirePermissionAdministrationMutateRequest(request *runtimev1.MutatePermissionAdministrationRequest) error {
	if len(request.GetGrants()) > semantic.MaxGrants {
		return platformLimitFailure(commonsemantic.LimitOperationCount, semantic.MaxGrants)
	}
	budget := newWireStringBudget(semantic.MaxSemanticRequestBytes)
	if !budget.take(request.GetPermissionSetId()) {
		return platformLimitFailure(commonsemantic.LimitSemanticRequest, semantic.MaxSemanticRequestBytes)
	}
	for _, value := range request.GetGrants() {
		if value == nil {
			return invalidPlatformWire()
		}
		if !budget.take(value.GetPermissionId()) {
			return platformLimitFailure(commonsemantic.LimitSemanticRequest, semantic.MaxSemanticRequestBytes)
		}
	}
	if proto.Size(request) > envelope.MaxRequestWireBytes {
		return platformLimitFailure(commonsemantic.LimitWireFrame, envelope.MaxRequestWireBytes)
	}
	return nil
}

func preflightWirePermissionAdministrationMutateResponse(response *runtimev1.MutatePermissionAdministrationResponse) error {
	budget := newWireStringBudget(semantic.MaxSemanticResponseBytes)
	if !budget.takeValidatedReceiptBytes(response.GetReceipts()) || proto.Size(response) > envelope.MaxResponseWireBytes {
		return internalPlatformWire()
	}
	return nil
}

func toWirePermissionAdministrationGrants(values []semantic.Grant) []*runtimev1.PermissionAdministrationGrant {
	if len(values) == 0 {
		return nil
	}
	result := make([]*runtimev1.PermissionAdministrationGrant, 0, len(values))
	for _, value := range values {
		result = append(result, &runtimev1.PermissionAdministrationGrant{PermissionId: value.PermissionID, Enabled: value.Enabled})
	}
	return result
}

func fromWirePermissionAdministrationGrants(values []*runtimev1.PermissionAdministrationGrant) ([]semantic.Grant, error) {
	if len(values) == 0 {
		return nil, nil
	}
	result := make([]semantic.Grant, 0, len(values))
	for _, value := range values {
		if value == nil {
			return nil, internalPlatformWire()
		}
		result = append(result, semantic.Grant{PermissionID: value.GetPermissionId(), Enabled: value.GetEnabled()})
	}
	return result, nil
}

func toWirePermissionAdministrationReadSelector(value semantic.ReadSelector) (runtimev1.PermissionAdministrationReadSelector, bool) {
	switch value {
	case semantic.ReadSystemCatalog:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_SYSTEM_CATALOG, true
	case semantic.ReadSystemGrants:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_SYSTEM_GRANTS, true
	case semantic.ReadRecordTypeCatalog:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_RECORD_TYPE_CATALOG, true
	case semantic.ReadRecordTypeGrants:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_RECORD_TYPE_GRANTS, true
	case semantic.ReadCustomCatalog:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_CUSTOM_CATALOG, true
	case semantic.ReadCustomGrants:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_CUSTOM_GRANTS, true
	default:
		return runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_UNSPECIFIED, false
	}
}

func fromWirePermissionAdministrationReadSelector(value runtimev1.PermissionAdministrationReadSelector) (semantic.ReadSelector, bool) {
	switch value {
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_SYSTEM_CATALOG:
		return semantic.ReadSystemCatalog, true
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_SYSTEM_GRANTS:
		return semantic.ReadSystemGrants, true
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_RECORD_TYPE_CATALOG:
		return semantic.ReadRecordTypeCatalog, true
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_RECORD_TYPE_GRANTS:
		return semantic.ReadRecordTypeGrants, true
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_CUSTOM_CATALOG:
		return semantic.ReadCustomCatalog, true
	case runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_CUSTOM_GRANTS:
		return semantic.ReadCustomGrants, true
	default:
		return "", false
	}
}

func toWirePermissionAdministrationMutationSelector(value semantic.MutationSelector) (runtimev1.PermissionAdministrationMutationSelector, bool) {
	switch value {
	case semantic.MutateSystemGrants:
		return runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_SYSTEM_GRANTS, true
	case semantic.MutateRecordTypeGrants:
		return runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_RECORD_TYPE_GRANTS, true
	case semantic.MutateCustomGrants:
		return runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_CUSTOM_GRANTS, true
	default:
		return runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_UNSPECIFIED, false
	}
}

func fromWirePermissionAdministrationMutationSelector(value runtimev1.PermissionAdministrationMutationSelector) (semantic.MutationSelector, bool) {
	switch value {
	case runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_SYSTEM_GRANTS:
		return semantic.MutateSystemGrants, true
	case runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_RECORD_TYPE_GRANTS:
		return semantic.MutateRecordTypeGrants, true
	case runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_CUSTOM_GRANTS:
		return semantic.MutateCustomGrants, true
	default:
		return "", false
	}
}
