package mapping

import (
	"context"

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

func ToWireCreateFileChunk(chunk semantic.CreateChunk) (*runtimev1.CreateFileRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateCreateChunk(chunk); err != nil {
		return nil, err
	}
	if chunk.Header != nil {
		return &runtimev1.CreateFileRequest{Value: &runtimev1.CreateFileRequest_Header{
			Header: &runtimev1.CreateFileHeader{
				IdempotencyKey: chunk.Header.IdempotencyKey, ParentObjectId: chunk.Header.ParentObjectID,
				ParentRecordId: chunk.Header.ParentRecordID, FileName: chunk.Header.FileName,
				ContentType: chunk.Header.ContentType, ContentLength: chunk.Header.ContentLength,
				AttachmentKind: chunk.Header.AttachmentKind, MinCatalogRevision: chunk.Header.MinCatalogRevision,
			},
		}}, nil
	}
	return &runtimev1.CreateFileRequest{
		Value: &runtimev1.CreateFileRequest_Data{Data: append([]byte(nil), chunk.Data...)},
	}, nil
}

func FromWireCreateFileChunk(chunk *runtimev1.CreateFileRequest) (semantic.CreateChunk, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.CreateChunk{}, err
	}
	if chunk == nil {
		return semantic.CreateChunk{}, invalidPlatformWire()
	}
	var result semantic.CreateChunk
	switch value := chunk.GetValue().(type) {
	case *runtimev1.CreateFileRequest_Header:
		if value.Header == nil {
			return semantic.CreateChunk{}, invalidPlatformWire()
		}
		result.Header = &semantic.CreateHeader{
			IdempotencyKey: value.Header.GetIdempotencyKey(), ParentObjectID: value.Header.GetParentObjectId(),
			ParentRecordID: value.Header.GetParentRecordId(), FileName: value.Header.GetFileName(),
			ContentType: value.Header.GetContentType(), ContentLength: value.Header.GetContentLength(),
			AttachmentKind: value.Header.GetAttachmentKind(), MinCatalogRevision: value.Header.MinCatalogRevision,
		}
	case *runtimev1.CreateFileRequest_Data:
		result.Data = append([]byte(nil), value.Data...)
	default:
		return semantic.CreateChunk{}, invalidPlatformWire()
	}
	if err := semantic.ValidateCreateChunk(result); err != nil {
		return semantic.CreateChunk{}, err
	}
	return result, nil
}

func ToWireCreateFileResponse(response semantic.CreateResponse) (*runtimev1.CreateFileResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateCreateResponse(response); err != nil {
		return nil, err
	}
	return &runtimev1.CreateFileResponse{
		FileId: response.FileID, FileName: response.FileName, Size: response.Size, ContentType: response.ContentType,
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireCreateFileResponse(response *runtimev1.CreateFileResponse) (semantic.CreateResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.CreateResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.CreateResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.CreateResponse{}, err
	}
	result := semantic.CreateResponse{
		FileID: response.GetFileId(), FileName: response.GetFileName(), Size: response.GetSize(),
		ContentType: response.GetContentType(), Receipts: receipts,
	}
	if err := semantic.ValidateCreateResponse(result); err != nil {
		return semantic.CreateResponse{}, err
	}
	return result, nil
}

func ToWireOpenFileRequest(ctx context.Context, request semantic.OpenRequest) (*runtimev1.OpenFileRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateOpenRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.OpenFileRequest{FileId: request.FileID, MinCatalogRevision: request.MinCatalogRevision}, nil
}

func FromWireOpenFileRequest(ctx context.Context, request *runtimev1.OpenFileRequest) (semantic.OpenRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.OpenRequest{}, err
	}
	if request == nil {
		return semantic.OpenRequest{}, invalidPlatformWire()
	}
	result := semantic.OpenRequest{FileID: request.GetFileId(), MinCatalogRevision: request.MinCatalogRevision}
	if err := semantic.ValidateOpenRequest(ctx, result); err != nil {
		return semantic.OpenRequest{}, err
	}
	return result, nil
}

func ToWireOpenFileChunk(chunk semantic.OpenChunk) (*runtimev1.OpenFileResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateOpenChunk(chunk); err != nil {
		return nil, err
	}
	result := &runtimev1.OpenFileResponse{
		FileName: chunk.FileName, ContentType: chunk.ContentType, TotalSize: chunk.TotalSize,
		Data: append([]byte(nil), chunk.Data...),
	}
	if chunk.Receipts != nil {
		result.Receipts = toWireReceipts(*chunk.Receipts, platformCapability(semantic.CapabilityName))
	}
	return result, nil
}

func FromWireOpenFileChunk(chunk *runtimev1.OpenFileResponse) (semantic.OpenChunk, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.OpenChunk{}, err
	}
	if chunk == nil {
		return semantic.OpenChunk{}, internalPlatformWire()
	}
	result := semantic.OpenChunk{
		FileName: chunk.GetFileName(), ContentType: chunk.GetContentType(), TotalSize: chunk.GetTotalSize(),
		Data: append([]byte(nil), chunk.GetData()...),
	}
	if chunk.GetReceipts() != nil {
		receipts, err := fromWireReceipts(chunk.GetReceipts(), platformCapability(semantic.CapabilityName))
		if err != nil {
			return semantic.OpenChunk{}, err
		}
		result.Receipts = &receipts
	}
	if err := semantic.ValidateOpenChunk(result); err != nil {
		return semantic.OpenChunk{}, err
	}
	return result, nil
}

func ToWireDeleteFileRequest(ctx context.Context, request semantic.DeleteRequest) (*runtimev1.DeleteFileRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateDeleteRequest(ctx, request); err != nil {
		return nil, err
	}
	return &runtimev1.DeleteFileRequest{FileId: request.FileID, MinCatalogRevision: request.MinCatalogRevision}, nil
}

func FromWireDeleteFileRequest(ctx context.Context, request *runtimev1.DeleteFileRequest) (semantic.DeleteRequest, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.DeleteRequest{}, err
	}
	if request == nil {
		return semantic.DeleteRequest{}, invalidPlatformWire()
	}
	result := semantic.DeleteRequest{FileID: request.GetFileId(), MinCatalogRevision: request.MinCatalogRevision}
	if err := semantic.ValidateDeleteRequest(ctx, result); err != nil {
		return semantic.DeleteRequest{}, err
	}
	return result, nil
}

func ToWireDeleteFileResponse(response semantic.DeleteResponse) (*runtimev1.DeleteFileResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return nil, err
	}
	if err := semantic.ValidateDeleteResponse(response); err != nil {
		return nil, err
	}
	return &runtimev1.DeleteFileResponse{
		Receipts: toWireReceipts(response.Receipts, platformCapability(semantic.CapabilityName)),
	}, nil
}

func FromWireDeleteFileResponse(response *runtimev1.DeleteFileResponse) (semantic.DeleteResponse, error) {
	if err := requirePlatformSemanticShape(semantic.CapabilityName); err != nil {
		return semantic.DeleteResponse{}, err
	}
	if response == nil || response.GetReceipts() == nil {
		return semantic.DeleteResponse{}, incompatibleFailureFor(platformCapability(semantic.CapabilityName))
	}
	receipts, err := fromWireReceipts(response.GetReceipts(), platformCapability(semantic.CapabilityName))
	if err != nil {
		return semantic.DeleteResponse{}, err
	}
	result := semantic.DeleteResponse{Receipts: receipts}
	if err := semantic.ValidateDeleteResponse(result); err != nil {
		return semantic.DeleteResponse{}, err
	}
	return result, nil
}
