// This file contains the implementation of the sync batcher for uploads
//
// Dropbox rules say you can start as many batches as you want, but
// you may only have one batch being committed and must wait for the
// batch to be finished before committing another.

package dropbox

import (
	"context"
	"fmt"

	"github.com/dropbox/dropbox-sdk-go-unofficial/v6/dropbox/files"
	"github.com/rclone/rclone/fs/fserrors"
)

// finishBatch commits the batch, returning a batch status to poll or maybe complete
func (f *Fs) finishBatch(ctx context.Context, items []*files.UploadSessionFinishArg) (complete *files.UploadSessionFinishBatchResult, err error) {
	var arg = &files.UploadSessionFinishBatchArg{
		Entries: items,
	}
	err = f.pacer.Call(func() (bool, error) {
		complete, err = f.srv.UploadSessionFinishBatchV2(arg)
		// If error is insufficient space then don't retry
		if e, ok := err.(files.UploadSessionFinishAPIError); ok {
			if e.EndpointError != nil && e.EndpointError.Path != nil && e.EndpointError.Path.Tag == files.WriteErrorInsufficientSpace {
				err = fserrors.NoRetryError(err)
				return false, err
			}
		}
		// after the first chunk is uploaded, we retry everything
		return err != nil, err
	})
	if err != nil {
		return nil, fmt.Errorf("batch commit failed: %w", err)
	}
	return complete, nil
}

// Called by the batcher to commit a batch
func (f *Fs) commitBatch(ctx context.Context, items []*files.UploadSessionFinishArg, results []*files.FileMetadata, errors []error) (err error) {
	// finalise the batch getting either a result or a job id to poll
	complete, err := f.finishBatch(ctx, items)
	if err != nil {
		return err
	}

	// Check we got the right number of entries
	entries := complete.Entries
	if len(entries) != len(results) {
		return fmt.Errorf("expecting %d items in batch but got %d", len(results), len(entries))
	}

	// Format results for return
	for i := range results {
		item := entries[i]
		if item.Tag == "success" {
			results[i] = item.Success
		} else {
			errorTag := item.Tag
			if item.Failure != nil {
				errorTag = item.Failure.Tag
				if item.Failure.LookupFailed != nil {
					errorTag += "/" + item.Failure.LookupFailed.Tag
				}
				if item.Failure.Path != nil {
					errorTag += "/" + item.Failure.Path.Tag
				}
				if item.Failure.PropertiesError != nil {
					errorTag += "/" + item.Failure.PropertiesError.Tag
				}
			}
			errors[i] = fmt.Errorf("upload failed: %s", errorTag)
		}
	}

	return nil
}