package mapping

import (
	"context"

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

func FromWireReadAuditLogRequest(ctx context.Context, request *runtimev1.ReadAuditLogRequest) (semantic.Request, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.Request{}, err
	}
	if request == nil {
		return semantic.Request{}, invalidPlatformWire()
	}
	mapped := semantic.Request{
		ObjectID: request.GetObjectId(), RecordID: request.GetRecordId(),
		Limit: request.GetLimit(), Offset: request.GetOffset(),
		MinCatalogRevision: request.MinCatalogRevision,
	}
	if err := semantic.ValidateRequest(ctx, mapped); err != nil {
		return semantic.Request{}, err
	}
	return mapped, nil
}

func ToWireReadAuditLogRequest(ctx context.Context, request semantic.Request) (*runtimev1.ReadAuditLogRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.ReadAuditLogRequest{
		ObjectId: request.ObjectID, RecordId: request.RecordID,
		Limit: request.Limit, Offset: request.Offset,
		MinCatalogRevision: request.MinCatalogRevision,
	}, nil
}

func ToWireReadAuditLogResponse(request semantic.Request, response semantic.Response) (*runtimev1.ReadAuditLogResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateResponseForRequest(request, response); err != nil {
		return nil, err
	}
	wireRows := make([]*runtimev1.AuditLogRow, 0, len(response.Rows))
	for _, row := range response.Rows {
		wireRows = append(wireRows, &runtimev1.AuditLogRow{
			Id: row.ID, ObjectId: row.ObjectID, RecordId: row.RecordID, FieldId: row.FieldID,
			OldValue: cloneOptionalString(row.OldValue), NewValue: cloneOptionalString(row.NewValue),
			ChangedById: row.ChangedByID, ChangedAt: row.ChangedAt, Action: row.Action,
			ChangesJson: row.ChangesJSON, Entrypoint: row.Entrypoint,
			EntrypointDetail: row.EntrypointDetail, RequestId: row.RequestID, TraceId: row.TraceID,
			SessionId: row.SessionID, ClientIp: row.ClientIP, ActualUserId: row.ActualUserID,
			Outcome: row.Outcome, ErrorCode: row.ErrorCode,
		})
	}
	return &runtimev1.ReadAuditLogResponse{
		Rows:     wireRows,
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireReadAuditLogResponse(request semantic.Request, response *runtimev1.ReadAuditLogResponse) (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
	}
	mapped := semantic.Response{Rows: make([]semantic.Row, 0, len(response.GetRows())), Receipts: receipts}
	for _, row := range response.GetRows() {
		if row == nil {
			return semantic.Response{}, &semantic.Failure{Category: semantic.FailureInternal}
		}
		mapped.Rows = append(mapped.Rows, semantic.Row{
			ID: row.GetId(), ObjectID: row.GetObjectId(), RecordID: row.GetRecordId(), FieldID: row.GetFieldId(),
			OldValue: cloneOptionalString(row.OldValue), NewValue: cloneOptionalString(row.NewValue),
			ChangedByID: row.GetChangedById(), ChangedAt: row.GetChangedAt(), Action: row.GetAction(),
			ChangesJSON: row.GetChangesJson(), Entrypoint: row.GetEntrypoint(),
			EntrypointDetail: row.GetEntrypointDetail(), RequestID: row.GetRequestId(), TraceID: row.GetTraceId(),
			SessionID: row.GetSessionId(), ClientIP: row.GetClientIp(), ActualUserID: row.GetActualUserId(),
			Outcome: row.GetOutcome(), ErrorCode: row.GetErrorCode(),
		})
	}
	if err := semantic.ValidateResponseForRequest(request, mapped); err != nil {
		return semantic.Response{}, err
	}
	return mapped, nil
}

func cloneOptionalString(value *string) *string {
	if value == nil {
		return nil
	}
	copy := *value
	return &copy
}
