package run

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

const maxWorkflowRunCancelRequestBytes = int64(8 << 10)

func decodeWorkflowRunCancelJSON(ctx *gin.Context) (dto.WorkflowRunCancelActionRequest, error) {
	if ctx == nil || ctx.Request == nil || ctx.Request.Body == nil {
		return dto.WorkflowRunCancelActionRequest{}, nil
	}
	ctx.Request.Body = http.MaxBytesReader(ctx.Writer, ctx.Request.Body, maxWorkflowRunCancelRequestBytes)
	payload, err := io.ReadAll(ctx.Request.Body)
	if err != nil {
		return dto.WorkflowRunCancelActionRequest{}, workflowRunJSONError(err)
	}
	if len(bytes.TrimSpace(payload)) == 0 {
		return dto.WorkflowRunCancelActionRequest{}, nil
	}
	return decodeWorkflowRunCancelJSONBytes(payload)
}

func decodeWorkflowRunCancelJSONBytes(payload []byte) (dto.WorkflowRunCancelActionRequest, error) {
	var body dto.WorkflowRunCancelActionRequest
	decoder := json.NewDecoder(bytes.NewReader(payload))
	opening, err := decoder.Token()
	if err != nil || opening != json.Delim('{') {
		return body, workflowRunJSONError(fmt.Errorf("request body must be a JSON object"))
	}
	seenReason := false
	for decoder.More() {
		token, err := decoder.Token()
		if err != nil {
			return body, workflowRunJSONError(err)
		}
		name, ok := token.(string)
		if !ok {
			return body, workflowRunJSONError(fmt.Errorf("JSON object key must be a string"))
		}
		if name != "reason" {
			return body, workflowRunJSONError(fmt.Errorf("unknown JSON field %q", name))
		}
		if seenReason {
			return body, workflowRunJSONError(fmt.Errorf("duplicate JSON field %q", name))
		}
		seenReason = true
		if err := decoder.Decode(&body.Reason); err != nil {
			return body, workflowRunJSONError(err)
		}
	}
	closing, err := decoder.Token()
	if err != nil || closing != json.Delim('}') {
		return body, workflowRunJSONError(fmt.Errorf("request body is incomplete"))
	}
	if token, err := decoder.Token(); err != io.EOF {
		if err != nil {
			return body, workflowRunJSONError(err)
		}
		return body, workflowRunJSONError(fmt.Errorf("trailing JSON value %v", token))
	}
	return body, nil
}

func workflowRunJSONError(err error) error {
	var maxBytesError *http.MaxBytesError
	if errors.As(err, &maxBytesError) {
		return publicError(http.StatusRequestEntityTooLarge, "run_request_too_large", "invalid request body")
	}
	return publicError(http.StatusBadRequest, "run_invalid_json", "invalid request body")
}
