package handler

import (
	"errors"
	"io"
	"strconv"

	"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/multipart"
	"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
	"go.uber.org/zap"
)

const attributeMultipartObjectSize = "S3-Multipart-Object-Size"

// MultipartFile provides standard ReadCloser interface and also allows one to
// get file name, it's used for multipart uploads.
type MultipartFile interface {
	io.ReadCloser
	FileName() string
}

func fetchMultipartFile(l *zap.Logger, r io.Reader, boundary string) (MultipartFile, error) {
	// To have a custom buffer (3mb) the custom multipart reader is used.
	// Default reader uses 4KiB chunks, which slow down upload speed up to 400%
	// https://github.com/golang/go/blob/91b9915d3f6f8cd2e9e9fda63f67772803adfa03/src/mime/multipart/multipart.go#L32
	reader := multipart.NewReader(r, boundary)

	for {
		part, err := reader.NextPart()
		if err != nil {
			return nil, err
		}

		name := part.FormName()
		if name == "" {
			l.Debug(logs.IgnorePartEmptyFormName)
			continue
		}

		filename := part.FileName()

		// ignore multipart/form-data values
		if filename == "" {
			l.Debug(logs.IgnorePartEmptyFilename, zap.String("form", name))

			continue
		}

		return part, nil
	}
}

// getPayload returns initial payload if object is not multipart else composes new reader with parts data.
func (h *Handler) getPayload(p getMultiobjectBodyParams) (io.ReadCloser, uint64, error) {
	cid, ok := p.obj.Header.ContainerID()
	if !ok {
		return nil, 0, errors.New("no container id set")
	}
	oid, ok := p.obj.Header.ID()
	if !ok {
		return nil, 0, errors.New("no object id set")
	}
	size, err := strconv.ParseUint(p.strSize, 10, 64)
	if err != nil {
		return nil, 0, err
	}
	ctx := p.req.RequestCtx
	params := PrmInitMultiObjectReader{
		Addr:   newAddress(cid, oid),
		Bearer: bearerToken(ctx),
	}
	payload, err := h.frostfs.InitMultiObjectReader(ctx, params)
	if err != nil {
		return nil, 0, err
	}

	return io.NopCloser(payload), size, nil
}