/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 */
package fileoper

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"path"
	"strconv"

	"mstore/pkg/auxtool"
	"mstore/pkg/auxuuid"
	"mstore/pkg/descr"
)

// PutFile
type PutFileParams struct {
	ContentType string
	ContentSize string
	Filepath    string
	Source      io.ReadCloser
}
type PutFileResult struct{}

// TODO: checking catalog and file names conflict
func (oper *Operator) PutFile(ctx context.Context, operatorID string, params *PutFileParams) (int, *PutFileResult, error) {
	var err error
	res := &PutFileResult{}

	if params.ContentSize == "" {
		code := http.StatusLengthRequired
		err = fmt.Errorf("Required Content-Size header is empty")
		return code, res, err
	}
	size, err := strconv.ParseInt(params.ContentSize, 10, 64)
	if err != nil {
		code := http.StatusLengthRequired
		return code, res, err
	}
	contentType := params.ContentType
	if contentType == "" {
		contentType = defaultContentType
	}

	resName := params.Filepath
	oper.iLock.WaitAndLock(resName)
	defer oper.iLock.Done(resName)

	// TODO: convert file path to a unified and secure state
	xfilepath, err := cleanFilepath(params.Filepath)
	if err != nil {
		code := http.StatusInternalServerError
		return code, res, err
	}
	filename := path.Base(xfilepath)
	collection := path.Dir(xfilepath)

	tmpname, size, checksum, err := oper.store.WriteTempFile(params.Source)
	if err != nil {
		code := http.StatusInternalServerError
		return code, res, err
	}
	var helmHash string
	var helmMeta string
	if contentType == hcMediaType {
		helmMeta, helmHash, err = oper.store.HelmMeta(tmpname)
		if err != nil {
			code := http.StatusInternalServerError
			return code, res, err
		}
	}

	descrExists, fileDescr, err := oper.mdb.GetFileByCollectionName(ctx, collection, filename)
	if err != nil {
		code := http.StatusInternalServerError
		return code, res, err
	}

	now := auxtool.TimeNow()
	if descrExists {
		fileDescr.Size = size
		fileDescr.Checksum = checksum
		fileDescr.UpdatedAt = now
		fileDescr.Type = contentType
		fileDescr.UpdatedBy = operatorID
		fileDescr.HelmMeta = helmMeta
		fileDescr.HelmHash = helmHash
		err = oper.mdb.UpdateFileByID(ctx, fileDescr.ID, fileDescr)
		if err != nil {
			code := http.StatusInternalServerError
			return code, res, err
		}
	} else {
		fileDescr = &descr.File{
			ID:         auxuuid.NewUUID(),
			Name:       filename,
			Collection: collection,
			Size:       size,
			Type:       contentType,
			Checksum:   checksum,
			CreatedAt:  now,
			UpdatedAt:  now,
			CreatedBy:  operatorID,
			UpdatedBy:  operatorID,
			HelmMeta:   helmMeta,
			HelmHash:   helmHash,
		}
		err = oper.mdb.InsertFile(ctx, fileDescr)
		if err != nil {
			code := http.StatusInternalServerError
			return code, res, err
		}
	}

	err = oper.store.HardlinkFile(tmpname, collection, filename)
	if err != nil {
		code := http.StatusInternalServerError
		return code, res, err
	}
	code := http.StatusOK
	return code, res, err
}
