package mapping

import (
	"context"

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

func ToWireReadObjectAdminIndexRequest(ctx context.Context, request semantic.ObjectAdminIndexRequest) (*runtimev1.ReadObjectAdminIndexRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateObjectAdminIndexRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.ReadObjectAdminIndexRequest{MinCatalogRevision: request.MinCatalogRevision}, nil
}

func FromWireReadObjectAdminIndexRequest(ctx context.Context, request *runtimev1.ReadObjectAdminIndexRequest) (semantic.ObjectAdminIndexRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ObjectAdminIndexRequest{}, err
	}
	if request == nil {
		return semantic.ObjectAdminIndexRequest{}, invalidPlatformWire()
	}
	mapped := semantic.ObjectAdminIndexRequest{MinCatalogRevision: request.MinCatalogRevision}
	if err := semantic.ValidateObjectAdminIndexRequest(ctx, mapped); err != nil {
		return semantic.ObjectAdminIndexRequest{}, err
	}
	return mapped, nil
}

func ToWireReadObjectAdminIndexResponse(request semantic.ObjectAdminIndexRequest, response semantic.ObjectAdminIndexResponse) (*runtimev1.ReadObjectAdminIndexResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateObjectAdminIndexResponse(request, response); err != nil {
		return nil, err
	}
	objects := make([]*runtimev1.ObjectAdminMetadata, 0, len(response.Objects))
	for _, object := range response.Objects {
		objects = append(objects, &runtimev1.ObjectAdminMetadata{
			ObjectId: object.ObjectID, Description: cloneOptionalString(object.Description),
			DeleteMode: object.DeleteMode,
		})
	}
	return &runtimev1.ReadObjectAdminIndexResponse{
		Objects: objects, Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireReadObjectAdminIndexResponse(request semantic.ObjectAdminIndexRequest, response *runtimev1.ReadObjectAdminIndexResponse) (semantic.ObjectAdminIndexResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ObjectAdminIndexResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.ObjectAdminIndexResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.ObjectAdminIndexResponse{}, err
	}
	mapped := semantic.ObjectAdminIndexResponse{Objects: make([]semantic.ObjectAdminMetadata, 0, len(response.GetObjects())), Receipts: receipts}
	for _, object := range response.GetObjects() {
		if object == nil {
			return semantic.ObjectAdminIndexResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
		}
		mapped.Objects = append(mapped.Objects, semantic.ObjectAdminMetadata{
			ObjectID: object.GetObjectId(), Description: cloneOptionalString(object.Description), DeleteMode: object.GetDeleteMode(),
		})
	}
	if err := semantic.ValidateObjectAdminIndexResponse(request, mapped); err != nil {
		return semantic.ObjectAdminIndexResponse{}, err
	}
	return mapped, nil
}

func ToWireReadLayoutAdminWorkspaceRequest(ctx context.Context, request semantic.LayoutAdminWorkspaceRequest) (*runtimev1.ReadLayoutAdminWorkspaceRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateLayoutAdminWorkspaceRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.ReadLayoutAdminWorkspaceRequest{ObjectId: request.ObjectID, MinCatalogRevision: request.MinCatalogRevision}, nil
}

func FromWireReadLayoutAdminWorkspaceRequest(ctx context.Context, request *runtimev1.ReadLayoutAdminWorkspaceRequest) (semantic.LayoutAdminWorkspaceRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.LayoutAdminWorkspaceRequest{}, err
	}
	if request == nil {
		return semantic.LayoutAdminWorkspaceRequest{}, invalidPlatformWire()
	}
	mapped := semantic.LayoutAdminWorkspaceRequest{ObjectID: request.GetObjectId(), MinCatalogRevision: request.MinCatalogRevision}
	if err := semantic.ValidateLayoutAdminWorkspaceRequest(ctx, mapped); err != nil {
		return semantic.LayoutAdminWorkspaceRequest{}, err
	}
	return mapped, nil
}

func ToWireReadLayoutAdminWorkspaceResponse(request semantic.LayoutAdminWorkspaceRequest, response semantic.LayoutAdminWorkspaceResponse) (*runtimev1.ReadLayoutAdminWorkspaceResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateLayoutAdminWorkspaceResponse(request, response); err != nil {
		return nil, err
	}
	layouts := make([]*runtimev1.StoredLayoutSummary, 0, len(response.Layouts))
	for _, layout := range response.Layouts {
		layouts = append(layouts, &runtimev1.StoredLayoutSummary{
			LayoutId: layout.LayoutID, ObjectId: layout.ObjectID, Name: layout.Name, Type: layout.Type,
			IsDefault: layout.IsDefault, CreatedAt: layout.CreatedAt, LastModifiedAt: layout.LastModifiedAt,
		})
	}
	assignments := make([]*runtimev1.StoredLayoutAssignment, 0, len(response.Assignments))
	for _, assignment := range response.Assignments {
		dimensions := make([]*runtimev1.StoredLayoutAssignmentDimension, 0, len(assignment.Dimensions))
		for _, dimension := range assignment.Dimensions {
			dimensions = append(dimensions, &runtimev1.StoredLayoutAssignmentDimension{
				Key: dimension.Key, Value: dimension.Value,
			})
		}
		assignments = append(assignments, &runtimev1.StoredLayoutAssignment{
			AssignmentId: assignment.AssignmentID, ObjectId: assignment.ObjectID, LayoutId: assignment.LayoutID,
			Dimensions: dimensions, Specificity: assignment.Specificity,
			CreatedAt: assignment.CreatedAt, LastModifiedAt: assignment.LastModifiedAt,
			CreatedById: cloneOptionalString(assignment.CreatedByID),
		})
	}
	return &runtimev1.ReadLayoutAdminWorkspaceResponse{
		ObjectId: response.ObjectID, Layouts: layouts, Assignments: assignments,
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireReadLayoutAdminWorkspaceResponse(request semantic.LayoutAdminWorkspaceRequest, response *runtimev1.ReadLayoutAdminWorkspaceResponse) (semantic.LayoutAdminWorkspaceResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.LayoutAdminWorkspaceResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.LayoutAdminWorkspaceResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.LayoutAdminWorkspaceResponse{}, err
	}
	mapped := semantic.LayoutAdminWorkspaceResponse{
		ObjectID: response.GetObjectId(), Layouts: make([]semantic.LayoutSummary, 0, len(response.GetLayouts())),
		Assignments: make([]semantic.LayoutAssignment, 0, len(response.GetAssignments())), Receipts: receipts,
	}
	for _, layout := range response.GetLayouts() {
		if layout == nil {
			return semantic.LayoutAdminWorkspaceResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
		}
		mapped.Layouts = append(mapped.Layouts, semantic.LayoutSummary{
			LayoutID: layout.GetLayoutId(), ObjectID: layout.GetObjectId(), Name: layout.GetName(), Type: layout.GetType(),
			IsDefault: layout.GetIsDefault(), CreatedAt: layout.GetCreatedAt(), LastModifiedAt: layout.GetLastModifiedAt(),
		})
	}
	for _, assignment := range response.GetAssignments() {
		if assignment == nil {
			return semantic.LayoutAdminWorkspaceResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
		}
		dimensions := make([]semantic.LayoutAssignmentDimension, 0, len(assignment.GetDimensions()))
		for _, dimension := range assignment.GetDimensions() {
			if dimension == nil {
				return semantic.LayoutAdminWorkspaceResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
			}
			dimensions = append(dimensions, semantic.LayoutAssignmentDimension{
				Key: dimension.GetKey(), Value: dimension.GetValue(),
			})
		}
		mapped.Assignments = append(mapped.Assignments, semantic.LayoutAssignment{
			AssignmentID: assignment.GetAssignmentId(), ObjectID: assignment.GetObjectId(), LayoutID: assignment.GetLayoutId(),
			Dimensions: dimensions, Specificity: assignment.GetSpecificity(),
			CreatedAt: assignment.GetCreatedAt(), LastModifiedAt: assignment.GetLastModifiedAt(),
			CreatedByID: cloneOptionalString(assignment.CreatedById),
		})
	}
	if err := semantic.ValidateLayoutAdminWorkspaceResponse(request, mapped); err != nil {
		return semantic.LayoutAdminWorkspaceResponse{}, err
	}
	return mapped, nil
}

func ToWireReadStoredLayoutRequest(ctx context.Context, request semantic.StoredLayoutRequest) (*runtimev1.ReadStoredLayoutRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateStoredLayoutRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.ReadStoredLayoutRequest{LayoutId: request.LayoutID, MinCatalogRevision: request.MinCatalogRevision}, nil
}

func FromWireReadStoredLayoutRequest(ctx context.Context, request *runtimev1.ReadStoredLayoutRequest) (semantic.StoredLayoutRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.StoredLayoutRequest{}, err
	}
	if request == nil {
		return semantic.StoredLayoutRequest{}, invalidPlatformWire()
	}
	mapped := semantic.StoredLayoutRequest{LayoutID: request.GetLayoutId(), MinCatalogRevision: request.MinCatalogRevision}
	if err := semantic.ValidateStoredLayoutRequest(ctx, mapped); err != nil {
		return semantic.StoredLayoutRequest{}, err
	}
	return mapped, nil
}

func ToWireReadStoredLayoutResponse(request semantic.StoredLayoutRequest, response semantic.StoredLayoutResponse) (*runtimev1.ReadStoredLayoutResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateStoredLayoutResponse(request, response); err != nil {
		return nil, err
	}
	return &runtimev1.ReadStoredLayoutResponse{
		LayoutId: response.LayoutID, ObjectId: response.ObjectID,
		StoredDocument: append([]byte(nil), response.StoredDocument...),
		Receipts:       toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireReadStoredLayoutResponse(request semantic.StoredLayoutRequest, response *runtimev1.ReadStoredLayoutResponse) (semantic.StoredLayoutResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.StoredLayoutResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	mapped := semantic.StoredLayoutResponse{
		LayoutID: response.GetLayoutId(), ObjectID: response.GetObjectId(),
		StoredDocument: append([]byte(nil), response.GetStoredDocument()...), Receipts: receipts,
	}
	if err := semantic.ValidateStoredLayoutResponse(request, mapped); err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	return mapped, nil
}

func ToWireReadValidationRuleAdminIndexRequest(
	ctx context.Context,
	request semantic.ValidationRuleAdminIndexRequest,
) (*runtimev1.ReadValidationRuleAdminIndexRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateValidationRuleAdminIndexRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.ReadValidationRuleAdminIndexRequest{
		ObjectId: request.ObjectID, MinCatalogRevision: request.MinCatalogRevision,
	}, nil
}

func FromWireReadValidationRuleAdminIndexRequest(
	ctx context.Context,
	request *runtimev1.ReadValidationRuleAdminIndexRequest,
) (semantic.ValidationRuleAdminIndexRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ValidationRuleAdminIndexRequest{}, err
	}
	if request == nil {
		return semantic.ValidationRuleAdminIndexRequest{}, invalidPlatformWire()
	}
	mapped := semantic.ValidationRuleAdminIndexRequest{
		ObjectID: request.GetObjectId(), MinCatalogRevision: request.MinCatalogRevision,
	}
	if err := semantic.ValidateValidationRuleAdminIndexRequest(ctx, mapped); err != nil {
		return semantic.ValidationRuleAdminIndexRequest{}, err
	}
	return mapped, nil
}

func ToWireReadValidationRuleAdminIndexResponse(
	request semantic.ValidationRuleAdminIndexRequest,
	response semantic.ValidationRuleAdminIndexResponse,
) (*runtimev1.ReadValidationRuleAdminIndexResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateValidationRuleAdminIndexResponse(request, response); err != nil {
		return nil, err
	}
	rules := make([]*runtimev1.ValidationRuleAdminMetadata, 0, len(response.Rules))
	for _, rule := range response.Rules {
		bindings := make([]*runtimev1.ValidationRuleRecordSetBinding, 0, len(rule.RecordSetBindings))
		for _, binding := range rule.RecordSetBindings {
			filters := make([]*runtimev1.ValidationRuleRecordSetFilter, 0, len(binding.Filters))
			for _, filter := range binding.Filters {
				filters = append(filters, &runtimev1.ValidationRuleRecordSetFilter{
					Field: filter.Field, Operator: filter.Operator, ValueSource: filter.ValueSource,
					ValueJson: append([]byte(nil), filter.Value...),
				})
			}
			bindings = append(bindings, &runtimev1.ValidationRuleRecordSetBinding{
				Name: binding.Name, Mode: binding.Mode, ObjectId: binding.ObjectID,
				FilterLogic: binding.FilterLogic, Filters: filters,
			})
		}
		rules = append(rules, &runtimev1.ValidationRuleAdminMetadata{
			RuleId: rule.RuleID, ObjectId: rule.ObjectID, Name: rule.Name,
			Description: cloneOptionalString(rule.Description), Active: rule.Active,
			Condition: rule.Condition, ErrorMessage: rule.ErrorMessage,
			ErrorDisplayField: cloneOptionalString(rule.ErrorDisplayField), RecordSetBindings: bindings,
			IsCustomized: rule.IsCustomized, OwnerPack: cloneOptionalString(rule.OwnerPack),
		})
	}
	return &runtimev1.ReadValidationRuleAdminIndexResponse{
		ObjectId: response.ObjectID, Rules: rules,
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireReadValidationRuleAdminIndexResponse(
	request semantic.ValidationRuleAdminIndexRequest,
	response *runtimev1.ReadValidationRuleAdminIndexResponse,
) (semantic.ValidationRuleAdminIndexResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.ValidationRuleAdminIndexResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.ValidationRuleAdminIndexResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	if err := preflightWireValidationRuleAdminIndexResponse(response); err != nil {
		return semantic.ValidationRuleAdminIndexResponse{}, err
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.ValidationRuleAdminIndexResponse{}, err
	}
	mapped := semantic.ValidationRuleAdminIndexResponse{
		ObjectID: response.GetObjectId(),
		Rules:    make([]semantic.ValidationRuleMetadata, 0, len(response.GetRules())),
		Receipts: receipts,
	}
	for _, rule := range response.GetRules() {
		if rule == nil {
			return semantic.ValidationRuleAdminIndexResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
		}
		bindings := make([]semantic.ValidationRuleRecordSetBinding, 0, len(rule.GetRecordSetBindings()))
		for _, binding := range rule.GetRecordSetBindings() {
			if binding == nil {
				return semantic.ValidationRuleAdminIndexResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
			}
			filters := make([]semantic.ValidationRuleRecordSetFilter, 0, len(binding.GetFilters()))
			for _, filter := range binding.GetFilters() {
				if filter == nil {
					return semantic.ValidationRuleAdminIndexResponse{}, &semantic.Failure{Category: semantic.FailureInternal}
				}
				filters = append(filters, semantic.ValidationRuleRecordSetFilter{
					Field: filter.GetField(), Operator: filter.GetOperator(), ValueSource: filter.GetValueSource(),
					Value: append([]byte(nil), filter.GetValueJson()...),
				})
			}
			bindings = append(bindings, semantic.ValidationRuleRecordSetBinding{
				Name: binding.GetName(), Mode: binding.GetMode(), ObjectID: binding.GetObjectId(),
				FilterLogic: binding.GetFilterLogic(), Filters: filters,
			})
		}
		mapped.Rules = append(mapped.Rules, semantic.ValidationRuleMetadata{
			RuleID: rule.GetRuleId(), ObjectID: rule.GetObjectId(), Name: rule.GetName(),
			Description: cloneOptionalString(rule.Description), Active: rule.GetActive(),
			Condition: rule.GetCondition(), ErrorMessage: rule.GetErrorMessage(),
			ErrorDisplayField: cloneOptionalString(rule.ErrorDisplayField), RecordSetBindings: bindings,
			IsCustomized: rule.GetIsCustomized(), OwnerPack: cloneOptionalString(rule.OwnerPack),
		})
	}
	if err := semantic.ValidateValidationRuleAdminIndexResponse(request, mapped); err != nil {
		return semantic.ValidationRuleAdminIndexResponse{}, err
	}
	return mapped, nil
}

func preflightWireValidationRuleAdminIndexResponse(
	response *runtimev1.ReadValidationRuleAdminIndexResponse,
) error {
	if len(response.GetRules()) > semantic.MaxValidationRulesPerObject {
		return &semantic.Failure{
			Category:  semantic.FailureResourceExhausted,
			LimitKind: semantic.LimitRecordCount,
			Maximum:   semantic.MaxValidationRulesPerObject,
		}
	}
	remainingProjectionEntries := semantic.MaxValidationRuleProjectionEntries
	if len(response.GetRules()) > remainingProjectionEntries {
		return &semantic.Failure{
			Category:  semantic.FailureResourceExhausted,
			LimitKind: semantic.LimitRecordCount,
			Maximum:   semantic.MaxValidationRuleProjectionEntries,
		}
	}
	remainingProjectionEntries -= len(response.GetRules())
	for _, rule := range response.GetRules() {
		if rule == nil {
			return &semantic.Failure{Category: semantic.FailureInternal}
		}
		if len(rule.GetRecordSetBindings()) > semantic.MaxRecordSetBindingsPerRule {
			return &semantic.Failure{
				Category:  semantic.FailureResourceExhausted,
				LimitKind: semantic.LimitRecordCount,
				Maximum:   semantic.MaxRecordSetBindingsPerRule,
			}
		}
		if len(rule.GetRecordSetBindings()) > remainingProjectionEntries {
			return &semantic.Failure{
				Category:  semantic.FailureResourceExhausted,
				LimitKind: semantic.LimitRecordCount,
				Maximum:   semantic.MaxValidationRuleProjectionEntries,
			}
		}
		remainingProjectionEntries -= len(rule.GetRecordSetBindings())
		for _, binding := range rule.GetRecordSetBindings() {
			if binding == nil {
				return &semantic.Failure{Category: semantic.FailureInternal}
			}
			if len(binding.GetFilters()) > semantic.MaxRecordSetFiltersPerBinding {
				return &semantic.Failure{
					Category:  semantic.FailureResourceExhausted,
					LimitKind: semantic.LimitRecordCount,
					Maximum:   semantic.MaxRecordSetFiltersPerBinding,
				}
			}
			if len(binding.GetFilters()) > remainingProjectionEntries {
				return &semantic.Failure{
					Category:  semantic.FailureResourceExhausted,
					LimitKind: semantic.LimitRecordCount,
					Maximum:   semantic.MaxValidationRuleProjectionEntries,
				}
			}
			remainingProjectionEntries -= len(binding.GetFilters())
			for _, filter := range binding.GetFilters() {
				if filter == nil {
					return &semantic.Failure{Category: semantic.FailureInternal}
				}
				if len(filter.GetValueJson()) > semantic.MaxFilterValueBytes {
					return &semantic.Failure{
						Category:  semantic.FailureResourceExhausted,
						LimitKind: semantic.LimitSemanticResponse,
						Maximum:   semantic.MaxFilterValueBytes,
					}
				}
			}
		}
	}
	return nil
}
