package nextcloud

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
)

const defaultUploadChunkSize = 10 * 1024 * 1024

// UploadStreaming uploads large files via Nextcloud chunked DAV assembly.
func (c *Client) UploadStreaming(ctx context.Context, userID, targetPath string, content io.Reader, contentType string, totalSize int64) error {
	uploadID, err := newChunkUploadID()
	if err != nil {
		return err
	}
	buf := make([]byte, defaultUploadChunkSize)
	var uploaded int64
	chunkIndex := 0
	for {
		n, readErr := io.ReadFull(content, buf)
		if n == 0 && readErr == io.EOF {
			break
		}
		if n > 0 {
			chunkIndex++
			if err := c.UploadChunk(ctx, userID, uploadID, chunkIndexName(chunkIndex), bytes.NewReader(buf[:n]), contentType); err != nil {
				_ = c.AbortChunkUpload(ctx, userID, uploadID)
				return err
			}
			uploaded += int64(n)
		}
		if readErr == io.EOF || readErr == io.ErrUnexpectedEOF {
			break
		}
		if readErr != nil {
			_ = c.AbortChunkUpload(ctx, userID, uploadID)
			return readErr
		}
	}
	finalSize := uploaded
	if totalSize > 0 {
		finalSize = totalSize
	}
	if err := c.AssembleChunks(ctx, userID, uploadID, targetPath, finalSize); err != nil {
		_ = c.AbortChunkUpload(ctx, userID, uploadID)
		return err
	}
	return nil
}

func newChunkUploadID() (string, error) {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return "", fmt.Errorf("chunk upload id: %w", err)
	}
	return hex.EncodeToString(b), nil
}

func chunkIndexName(index int) string {
	return fmt.Sprintf("%d", index)
}
