package handler

import (
	"context"
	"strings"
	"time"

	"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
	"git.frostfs.info/TrueCloudLab/frostfs-http-gw/response"
	"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
	"github.com/valyala/fasthttp"
	"go.uber.org/zap"
)

type request struct {
	*fasthttp.RequestCtx
	log *zap.Logger
}

func (r *request) handleFrostFSErr(err error, start time.Time) {
	logFields := []zap.Field{
		zap.Stringer("elapsed", time.Since(start)),
		zap.Error(err),
	}
	statusCode, msg, additionalFields := response.FormErrorResponse("could not receive object", err)
	logFields = append(logFields, additionalFields...)

	r.log.Error(logs.CouldNotReceiveObject, logFields...)
	response.Error(r.RequestCtx, msg, statusCode)
}

func bearerToken(ctx context.Context) *bearer.Token {
	if tkn, err := tokens.LoadBearerToken(ctx); err == nil {
		return tkn
	}
	return nil
}

func isDir(name string) bool {
	return name == "" || strings.HasSuffix(name, "/")
}

func loadAttributes(attrs []object.Attribute) map[string]string {
	result := make(map[string]string)
	for _, attr := range attrs {
		result[attr.Key()] = attr.Value()
	}
	return result
}

func isValidToken(s string) bool {
	for _, c := range s {
		if c <= ' ' || c > 127 {
			return false
		}
		if strings.ContainsRune("()<>@,;:\\\"/[]?={}", c) {
			return false
		}
	}
	return true
}

func isValidValue(s string) bool {
	for _, c := range s {
		// HTTP specification allows for more technically, but we don't want to escape things.
		if c < ' ' || c > 127 || c == '"' {
			return false
		}
	}
	return true
}

func logAndSendBucketError(c *fasthttp.RequestCtx, log *zap.Logger, err error) {
	log.Error(logs.CouldntGetBucket, zap.Error(err))

	if client.IsErrContainerNotFound(err) {
		response.Error(c, "Not Found", fasthttp.StatusNotFound)
		return
	}
	response.Error(c, "could not get bucket: "+err.Error(), fasthttp.StatusBadRequest)
}

func newAddress(cnr cid.ID, obj oid.ID) oid.Address {
	var addr oid.Address
	addr.SetContainer(cnr)
	addr.SetObject(obj)
	return addr
}