// Recipe "create-document": upload a file and create a single-file document from it.
//
// Usage: go run ./recipes/create_document [--keep]
// With --keep the document stays in the vault; otherwise it is deleted at the end.
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/M-Files/mfiles-grpc-api-samples/mfilesclient"
	"github.com/M-Files/mfiles-grpc-api-samples/recipes/internal/recipe"
	"github.com/M-Files/mfiles-grpc-api/go/mfiles"
)

// blockSize is the size of one UploadTempFile block: a multiple of 4096, well below the message
// limit.
const blockSize = 4 * 1024 * 1024

func main() {
	recipe.Main(run)
}

func run(ctx context.Context) error {
	client, err := recipe.Connect(ctx)
	if err != nil {
		return err
	}
	defer client.Close()
	objects := mfiles.NewIRPCObjectOperationsClient(client.Conn)

	title := fmt.Sprintf("%s Created by the Go sample %s", recipe.TestPrefix,
		time.Now().UTC().Format(time.DateTime))
	content := []byte(title + "\r\nHello from the M-Files gRPC API.\r\n")

	uploadID, err := upload(ctx, objects, content)
	if err != nil {
		return err
	}

	// [START create-document]
	response, err := objects.AddObjectWithFiles(ctx, &mfiles.AddObjectWithFilesRequest{
		ObjectTypeId: 0, // Document
		Properties: []*mfiles.PropertyValue{
			mfilesclient.PropertyValue(0, mfilesclient.Text(title)),    // Name or title
			mfilesclient.PropertyValue(100, mfilesclient.Lookup(1, 1)), // Class: "Other Document"
			mfilesclient.PropertyValue(22, mfilesclient.Boolean(true)), // Single file
		},
		// Always send an ACL: without one, the new document has no permissions and not even its
		// creator can see it. Named ACL -1 is "Full control for all internal users".
		Acl: mfilesclient.NamedACL(mfilesclient.NamedACLFullControlForAllInternalUsers),
		Files: &mfiles.ObjFileSource{
			Type: mfiles.ObjFileSourceType_OBJ_FILE_SOURCE_TYPE_UPLOADED_FILE,
			Data: &mfiles.ObjFileSourceUnion{Data: &mfiles.ObjFileSourceUnion_UploadedFile{
				UploadedFile: &mfiles.ObjFileSourceUnion_UploadedFileData{
					UploadId:  uploadID,
					Title:     title, // a single-file document's file takes the document's title
					Extension: "txt",
					Size:      int64(len(content)),
				},
			}},
		},
		CheckIn: true,
	})
	if err != nil {
		return err
	}
	created := response.GetCreatedObject().GetObjectVersion()
	fmt.Println("Created", recipe.Describe(created))
	// [END create-document]

	for _, file := range created.GetVersionInfo().GetFiles() {
		fmt.Printf("  File: %s.%s, %d bytes\n", file.GetTitle(), file.GetExtension(), file.GetSize())
	}

	if !recipe.Keep() {
		return recipe.Delete(ctx, client, created.GetObjectInfo().GetObjId())
	}
	return nil
}

// [START upload-file]

// upload uploads content to a temporary file on the server, in blocks, and returns its upload
// ID. The first call (upload_id 0) starts the upload; the next ones continue it.
func upload(ctx context.Context, objects mfiles.IRPCObjectOperationsClient,
	content []byte) (int32, error) {
	var uploadID int32
	for offset := 0; offset == 0 || offset < len(content); offset += blockSize {
		block, err := objects.UploadTempFile(ctx, &mfiles.UploadTempFileRequest{
			UploadId:         uploadID,
			TotalSizeInBytes: int64(len(content)),
			Offset:           int64(offset),
			Data:             content[offset:min(offset+blockSize, len(content))],
		})
		if err != nil {
			return 0, err
		}
		uploadID = block.GetUploadId()
	}
	_, err := objects.UploadTempFileCommit(ctx, &mfiles.UploadTempFileCommitRequest{
		UploadId:    uploadID,
		LogicalSize: int64(len(content)),
	})
	return uploadID, err
}

// [END upload-file]
