package storage

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"io"
)

// composeStreams concatenates objects into destination while calculating the
// MD5 of the exact bytes written. Provider-specific storage clients stay out
// of this package; callbacks keep this helper independent from biz and SDKs.
func composeStreams(
	ctx context.Context,
	names []string,
	open func(context.Context, string) (io.ReadCloser, error),
	put func(context.Context, string, io.Reader) error,
	remove func(context.Context, string) error,
	destination string,
) (string, error) {
	reader, writer := io.Pipe()
	hash := md5.New()
	errCh := make(chan error, 1)
	go func() {
		defer writer.Close()
		for _, name := range names {
			if err := ctx.Err(); err != nil {
				errCh <- err
				return
			}
			file, err := open(ctx, name)
			if err != nil {
				errCh <- err
				return
			}
			_, copyErr := io.Copy(io.MultiWriter(writer, hash), file)
			_ = file.Close()
			if copyErr != nil {
				errCh <- copyErr
				return
			}
		}
		errCh <- nil
	}()
	putErr := put(ctx, destination, reader)
	composeErr := <-errCh
	if putErr != nil {
		_ = remove(ctx, destination)
		return "", putErr
	}
	if composeErr != nil {
		_ = remove(ctx, destination)
		return "", composeErr
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}
