package mapping

import (
	"context"

	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	historysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"

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

func ToWireLayoutAuthoringV2ReadWorkspaceRequest(ctx context.Context, request semantic.WorkspaceRequest) (*runtimev1.LayoutAuthoringV2ServiceReadWorkspaceRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateWorkspaceRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.LayoutAuthoringV2ServiceReadWorkspaceRequest{ObjectId: request.ObjectID}, nil
}

func FromWireLayoutAuthoringV2ReadWorkspaceRequest(ctx context.Context, request *runtimev1.LayoutAuthoringV2ServiceReadWorkspaceRequest) (semantic.WorkspaceRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.WorkspaceRequest{}, err
	}
	if request == nil {
		return semantic.WorkspaceRequest{}, invalidPlatformWire()
	}
	result := semantic.WorkspaceRequest{ObjectID: request.GetObjectId()}
	if err := semantic.ValidateWorkspaceRequest(ctx, result); err != nil {
		return semantic.WorkspaceRequest{}, err
	}
	return result, nil
}

func ToWireLayoutAuthoringV2ReadWorkspaceResponse(request semantic.WorkspaceRequest, response semantic.WorkspaceResponse) (*runtimev1.LayoutAuthoringV2ServiceReadWorkspaceResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateWorkspaceResponse(request, response); err != nil {
		return nil, err
	}
	result := &runtimev1.LayoutAuthoringV2ServiceReadWorkspaceResponse{ObjectId: response.ObjectID, LayoutRevision: response.LayoutRevision, Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName))}
	for _, value := range response.Layouts {
		result.Layouts = append(result.Layouts, &runtimev1.StoredLayoutSummary{LayoutId: value.LayoutID, ObjectId: value.ObjectID, Name: value.Name, Type: value.Type, IsDefault: value.IsDefault, CreatedAt: value.CreatedAt, LastModifiedAt: value.LastModifiedAt})
	}
	for _, value := range response.Assignments {
		assignment := &runtimev1.StoredLayoutAssignment{AssignmentId: value.AssignmentID, ObjectId: value.ObjectID, LayoutId: value.LayoutID, Specificity: value.Specificity, CreatedAt: value.CreatedAt, LastModifiedAt: value.LastModifiedAt, CreatedById: cloneOptionalString(value.CreatedByID)}
		for _, dimension := range value.Dimensions {
			assignment.Dimensions = append(assignment.Dimensions, &runtimev1.StoredLayoutAssignmentDimension{Key: dimension.Key, Value: dimension.Value})
		}
		result.Assignments = append(result.Assignments, assignment)
	}
	return result, nil
}

func FromWireLayoutAuthoringV2ReadWorkspaceResponse(request semantic.WorkspaceRequest, response *runtimev1.LayoutAuthoringV2ServiceReadWorkspaceResponse) (semantic.WorkspaceResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.WorkspaceResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.WorkspaceResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	if err := preflightWireLayoutAuthoringV2Workspace(response); err != nil {
		return semantic.WorkspaceResponse{}, err
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.WorkspaceResponse{}, err
	}
	result := semantic.WorkspaceResponse{ObjectID: response.GetObjectId(), LayoutRevision: response.GetLayoutRevision(), Receipts: receipts}
	for _, value := range response.GetLayouts() {
		if value == nil {
			return semantic.WorkspaceResponse{}, internalPlatformWire()
		}
		result.Layouts = append(result.Layouts, semantic.LayoutSummary{LayoutID: value.GetLayoutId(), ObjectID: value.GetObjectId(), Name: value.GetName(), Type: value.GetType(), IsDefault: value.GetIsDefault(), CreatedAt: value.GetCreatedAt(), LastModifiedAt: value.GetLastModifiedAt()})
	}
	for _, value := range response.GetAssignments() {
		if value == nil {
			return semantic.WorkspaceResponse{}, internalPlatformWire()
		}
		assignment := semantic.LayoutAssignment{AssignmentID: value.GetAssignmentId(), ObjectID: value.GetObjectId(), LayoutID: value.GetLayoutId(), Specificity: value.GetSpecificity(), CreatedAt: value.GetCreatedAt(), LastModifiedAt: value.GetLastModifiedAt(), CreatedByID: cloneOptionalString(value.CreatedById)}
		for _, dimension := range value.GetDimensions() {
			if dimension == nil {
				return semantic.WorkspaceResponse{}, internalPlatformWire()
			}
			assignment.Dimensions = append(assignment.Dimensions, adminread.LayoutAssignmentDimension{Key: dimension.GetKey(), Value: dimension.GetValue()})
		}
		result.Assignments = append(result.Assignments, assignment)
	}
	if err := semantic.ValidateWorkspaceResponse(request, result); err != nil {
		return semantic.WorkspaceResponse{}, err
	}
	return result, nil
}

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

func FromWireLayoutAuthoringV2ReadStoredLayoutRequest(ctx context.Context, request *runtimev1.LayoutAuthoringV2ServiceReadStoredLayoutRequest) (semantic.StoredLayoutRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.StoredLayoutRequest{}, err
	}
	if request == nil {
		return semantic.StoredLayoutRequest{}, invalidPlatformWire()
	}
	result := semantic.StoredLayoutRequest{ObjectID: request.GetObjectId(), LayoutID: request.GetLayoutId()}
	if err := semantic.ValidateStoredLayoutRequest(ctx, result); err != nil {
		return semantic.StoredLayoutRequest{}, err
	}
	return result, nil
}

func ToWireLayoutAuthoringV2ReadStoredLayoutResponse(request semantic.StoredLayoutRequest, response semantic.StoredLayoutResponse) (*runtimev1.LayoutAuthoringV2ServiceReadStoredLayoutResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateStoredLayoutResponse(request, response); err != nil {
		return nil, err
	}
	return &runtimev1.LayoutAuthoringV2ServiceReadStoredLayoutResponse{
		LayoutId: response.LayoutID, ObjectId: response.ObjectID, LayoutRevision: response.LayoutRevision,
		Document:             toWireLayoutAuthoringDocument(response.Document),
		OrderedListFields:    &runtimev1.LayoutAuthoringStringList{Values: cloneStrings(response.OrderedListFields)},
		RequiredCreateFields: &runtimev1.LayoutAuthoringStringList{Values: cloneStrings(response.RequiredCreateFields)},
		Receipts:             toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireLayoutAuthoringV2ReadStoredLayoutResponse(request semantic.StoredLayoutRequest, response *runtimev1.LayoutAuthoringV2ServiceReadStoredLayoutResponse) (semantic.StoredLayoutResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil || response.GetOrderedListFields() == nil || response.GetRequiredCreateFields() == nil {
		return semantic.StoredLayoutResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	if err := preflightWireLayoutAuthoringV2StoredLayout(response); err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	document, err := fromWireLayoutAuthoringDocument(response.GetDocument())
	if err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	result := semantic.StoredLayoutResponse{
		LayoutID: response.GetLayoutId(), ObjectID: response.GetObjectId(), LayoutRevision: response.GetLayoutRevision(),
		Document: document, OrderedListFields: cloneStrings(response.GetOrderedListFields().GetValues()),
		RequiredCreateFields: cloneStrings(response.GetRequiredCreateFields().GetValues()), Receipts: receipts,
	}
	if err := semantic.ValidateStoredLayoutResponse(request, result); err != nil {
		return semantic.StoredLayoutResponse{}, err
	}
	return result, nil
}

func ToWireLayoutAuthoringV2MutateRequest(ctx context.Context, request semantic.Request) (*runtimev1.LayoutAuthoringV2ServiceMutateRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateRequest(ctx, request); err != nil {
		return nil, err
	}
	result := &runtimev1.LayoutAuthoringV2ServiceMutateRequest{ObjectId: request.ObjectID, ExpectedLayoutRevision: request.ExpectedLayoutRevision}
	for _, projection := range request.HistoryRelatedLists {
		columns := make(map[string]string, len(projection.Columns))
		for name, member := range projection.Columns {
			columns[name] = string(member)
		}
		result.HistoryRelatedLists = append(result.HistoryRelatedLists, &runtimev1.LayoutAuthoringHistoryRelatedListProjection{ObjectId: projection.ObjectID, Columns: columns})
	}

	switch {
	case request.Operation.Create != nil:
		value := request.Operation.Create
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_Create{Create: &runtimev1.LayoutAuthoringV2Create{Document: toWireLayoutAuthoringDocument(value.Document), ListFields: toWireLayoutAuthoringStringListChange(value.ListFields)}}
	case request.Operation.Update != nil:
		value := request.Operation.Update
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_Update{Update: &runtimev1.LayoutAuthoringV2Update{LayoutId: value.LayoutID, Changes: toWireLayoutAuthoringChanges(value.Changes), ListFields: toWireLayoutAuthoringStringListChange(value.ListFields)}}
	case request.Operation.Clone != nil:
		value := request.Operation.Clone
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_Clone{Clone: &runtimev1.LayoutAuthoringV2Clone{SourceLayoutId: value.SourceLayoutID, TargetLayoutId: value.TargetLayoutID, TargetName: value.TargetName}}
	case request.Operation.SetDefault != nil:
		value := request.Operation.SetDefault
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_SetDefault{SetDefault: &runtimev1.LayoutAuthoringV2SetDefault{LayoutId: value.LayoutID}}
	case request.Operation.Delete != nil:
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_Delete{Delete: &runtimev1.LayoutAuthoringV2Delete{LayoutId: request.Operation.Delete.LayoutID}}
	case request.Operation.Restore != nil:
		value := request.Operation.Restore
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_Restore{Restore: &runtimev1.LayoutAuthoringV2Restore{LayoutId: value.LayoutID, HistoryVersion: value.HistoryVersion}}
	case request.Operation.AssignmentUpsert != nil:
		value := request.Operation.AssignmentUpsert
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_AssignmentUpsert{AssignmentUpsert: &runtimev1.LayoutAuthoringV2UpsertAssignment{LayoutId: value.LayoutID, Selectors: toWireLayoutAuthoringSelectors(value.Selectors)}}
	case request.Operation.AssignmentDelete != nil:
		result.Operation = &runtimev1.LayoutAuthoringV2ServiceMutateRequest_AssignmentDelete{AssignmentDelete: &runtimev1.LayoutAuthoringV2DeleteAssignment{AssignmentId: request.Operation.AssignmentDelete.AssignmentID}}
	}
	return result, nil
}

func FromWireLayoutAuthoringV2MutateRequest(ctx context.Context, request *runtimev1.LayoutAuthoringV2ServiceMutateRequest) (semantic.Request, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.Request{}, err
	}
	if request == nil {
		return semantic.Request{}, invalidPlatformWire()
	}
	// Classify request lifecycle before any payload admission or conversion.
	if err := commonsemantic.ValidateRequestContext(ctx); err != nil {
		return semantic.Request{}, err
	}
	if err := preflightWireLayoutAuthoringV2MutateRequest(request); err != nil {
		return semantic.Request{}, err
	}
	result := semantic.Request{ObjectID: request.GetObjectId(), ExpectedLayoutRevision: request.GetExpectedLayoutRevision()}
	for _, projection := range request.GetHistoryRelatedLists() {
		if projection == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		columns := make(map[string]historysemantic.HistoryColumn, len(projection.GetColumns()))
		for name, member := range projection.GetColumns() {
			columns[name] = historysemantic.HistoryColumn(member)
		}
		result.HistoryRelatedLists = append(result.HistoryRelatedLists, semantic.HistoryRelatedListProjection{ObjectID: projection.GetObjectId(), Columns: columns})
	}

	switch operation := request.Operation.(type) {
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_Create:
		if operation.Create == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		document, err := fromWireLayoutAuthoringDocument(operation.Create.GetDocument())
		if err != nil {
			return semantic.Request{}, err
		}
		listFields, err := fromWireLayoutAuthoringStringListChange(operation.Create.GetListFields())
		if err != nil {
			return semantic.Request{}, err
		}
		result.Operation.Create = &semantic.CreateLayout{Document: document, ListFields: listFields}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_Update:
		if operation.Update == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		changes, err := fromWireLayoutAuthoringChanges(operation.Update.GetChanges())
		if err != nil {
			return semantic.Request{}, err
		}
		listFields, err := fromWireLayoutAuthoringStringListChange(operation.Update.GetListFields())
		if err != nil {
			return semantic.Request{}, err
		}
		result.Operation.Update = &semantic.UpdateLayout{LayoutID: operation.Update.GetLayoutId(), Changes: changes, ListFields: listFields}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_Clone:
		if operation.Clone == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		result.Operation.Clone = &semantic.CloneLayout{SourceLayoutID: operation.Clone.GetSourceLayoutId(), TargetLayoutID: operation.Clone.GetTargetLayoutId(), TargetName: operation.Clone.GetTargetName()}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_SetDefault:
		if operation.SetDefault == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		result.Operation.SetDefault = &semantic.SetDefaultLayout{LayoutID: operation.SetDefault.GetLayoutId()}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_Delete:
		if operation.Delete == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		result.Operation.Delete = &semantic.DeleteLayout{LayoutID: operation.Delete.GetLayoutId()}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_Restore:
		if operation.Restore == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		result.Operation.Restore = &semantic.RestoreLayout{LayoutID: operation.Restore.GetLayoutId(), HistoryVersion: operation.Restore.GetHistoryVersion()}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_AssignmentUpsert:
		if operation.AssignmentUpsert == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		selectors, err := fromWireLayoutAuthoringSelectors(operation.AssignmentUpsert.GetSelectors())
		if err != nil {
			return semantic.Request{}, err
		}
		result.Operation.AssignmentUpsert = &semantic.UpsertLayoutAssignment{LayoutID: operation.AssignmentUpsert.GetLayoutId(), Selectors: selectors}
	case *runtimev1.LayoutAuthoringV2ServiceMutateRequest_AssignmentDelete:
		if operation.AssignmentDelete == nil {
			return semantic.Request{}, invalidPlatformWire()
		}
		result.Operation.AssignmentDelete = &semantic.DeleteLayoutAssignment{AssignmentID: operation.AssignmentDelete.GetAssignmentId()}
	default:
		return semantic.Request{}, invalidPlatformWire()
	}
	if err := semantic.ValidateRequest(ctx, result); err != nil {
		return semantic.Request{}, err
	}
	return result, nil
}

func ToWireLayoutAuthoringV2MutateResponse(request semantic.Request, response semantic.Response) (*runtimev1.LayoutAuthoringV2ServiceMutateResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateResponse(request, response); err != nil {
		return nil, err
	}
	return &runtimev1.LayoutAuthoringV2ServiceMutateResponse{Changed: response.Changed, LayoutRevision: response.LayoutRevision, LayoutId: cloneOptionalString(response.LayoutID), AssignmentId: cloneOptionalString(response.AssignmentID), Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName))}, nil
}

func FromWireLayoutAuthoringV2MutateResponse(request semantic.Request, response *runtimev1.LayoutAuthoringV2ServiceMutateResponse) (semantic.Response, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.Response{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.Response{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.Response{}, err
	}
	result := semantic.Response{Changed: response.GetChanged(), LayoutRevision: response.GetLayoutRevision(), LayoutID: cloneOptionalString(response.LayoutId), AssignmentID: cloneOptionalString(response.AssignmentId), Receipts: receipts}
	if err := semantic.ValidateResponse(request, result); err != nil {
		return semantic.Response{}, err
	}
	return result, nil
}
