// Recipe "update-properties": change properties of a document two ways: in one call with an
// explicit version, and with check out, set, check in.
//
// Usage: go run ./recipes/update_properties [--keep]
// With --keep the test 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"
)

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

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

	created, err := recipe.CreateTestDocument(ctx, client, "update-properties")
	if err != nil {
		return err
	}
	objID := created.GetObjectInfo().GetObjId()
	id := objID.GetItemId().GetInternalId()
	version := created.GetVersionInfo().GetVersion().GetInternalVersion()
	fmt.Println("Created", recipe.Describe(created))
	if !recipe.Keep() {
		defer func() {
			if deleteErr := recipe.Delete(ctx, client, objID); err == nil {
				err = deleteErr
			}
		}()
	}

	// [START update-properties]
	// One call: the server checks the object out, sets the values and checks it in.
	// Name the version you read; with fail_if_newer_version_exists the call fails instead of
	// overwriting a change someone else made after that version.
	updated, err := objects.SetProperties(ctx, &mfiles.SetPropertiesRequest{
		ObjVer:                        mfilesclient.ObjVer(0, id, version),
		AllowModifyingCheckedInObject: true,
		FailIfNewerVersionExists:      true,
		// Other properties stay as they are (remove_unspecified_properties is false).
		Properties: []*mfiles.PropertyValue{
			mfilesclient.PropertyValue(1004, mfilesclient.Text("grpc, sample")), // Keywords
		},
	})
	if err != nil {
		return err
	}
	version = updated.GetObjectInfo().GetLatestVersion().GetObjectVersion().GetVersionInfo().
		GetVersion().GetInternalVersion()
	fmt.Printf("Set Keywords: now version %d\n", version)
	// [END update-properties]

	if err := checkOutSetCheckIn(ctx, objects, objID); err != nil {
		return err
	}

	// Show the result.
	result, err := objects.GetObjectInfoAndProperties(ctx, &mfiles.GetObjectInfoAndPropertiesRequest{
		ObjId:         objID,
		LatestVersion: true,
	})
	if err != nil {
		return err
	}
	for _, property := range result.GetPropertyArray() {
		if def := property.GetPropertyDef(); def == 1004 || def == 1002 {
			fmt.Printf("  Property %d: %s\n", def, recipe.FormatValue(property.GetValue()))
		}
	}
	return nil
}

// [START check-out-in]

// checkOutSetCheckIn makes several changes into one new version; nobody else can change the
// object meanwhile.
func checkOutSetCheckIn(ctx context.Context, objects mfiles.IRPCObjectOperationsClient,
	objID *mfiles.ObjID) error {
	checkedOut, err := objects.CheckOut(ctx, &mfiles.CheckOutRequest{ObjId: objID})
	if err != nil {
		return err
	}
	workingVersion := checkedOut.GetObjectInfo().GetObjectVersion().GetVersionInfo().GetVersion()
	fmt.Printf("Checked out: working version %d\n", workingVersion.GetInternalVersion())
	working := mfilesclient.ObjVer(objID.GetType(), objID.GetItemId().GetInternalId(),
		workingVersion.GetInternalVersion())

	today := time.Now().UTC()
	_, err = objects.SetProperties(ctx, &mfiles.SetPropertiesRequest{
		ObjVer: working,
		Properties: []*mfiles.PropertyValue{
			mfilesclient.PropertyValue(1004, mfilesclient.Text("grpc, sample, checked out")), // Keywords
			mfilesclient.PropertyValue(1002, // Document date
				mfilesclient.Date(today.Year(), today.Month(), today.Day())),
		},
	})
	var checkedIn *mfiles.CheckInResponse
	if err == nil {
		checkedIn, err = objects.CheckIn(ctx, &mfiles.CheckInRequest{ObjVer: working})
	}
	if err != nil {
		// Do not leave the object checked out.
		_, _ = objects.UndoCheckout(context.WithoutCancel(ctx),
			&mfiles.UndoCheckoutRequest{ObjVer: working})
		return err
	}
	fmt.Printf("Checked in: now version %d\n", checkedIn.GetObjectInfo().GetLatestVersion().
		GetObjectVersion().GetVersionInfo().GetVersion().GetInternalVersion())
	return nil
}

// [END check-out-in]
