package invocation

import (
	"bytes"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	invocationlang "github.com/tidbcloud/AgentTeam/internal/workflow/invocation"
)

const (
	idempotencyHeader                = "Idempotency-Key"
	maxInvocationIdempotencyKeyBytes = 128
)

func requestPrincipal(request api.WorkspaceScopedRequest) Principal {
	return Principal{
		UserID: strings.TrimSpace(request.UserID()), MemberID: strings.TrimSpace(request.Workspace.MemberID),
		Role: strings.TrimSpace(request.Workspace.WorkspaceRole),
	}
}

func requestActor(request api.WorkspaceScopedRequest) Actor {
	return Actor{
		Type: invocationlang.ActorTypeHuman,
		ID:   strings.TrimSpace(request.UserID()), MemberID: strings.TrimSpace(request.Workspace.MemberID),
	}
}

func planID(request api.WorkspaceScopedRequest) (string, error) {
	value := strings.TrimSpace(request.Gin.Param("planId"))
	if value == "" {
		return "", publicError(http.StatusBadRequest, "invocation_invalid_request", "planId is required")
	}
	return value, nil
}

func listPlanID(request api.WorkspaceScopedRequest) (string, error) {
	value := strings.TrimSpace(request.Gin.Query("planId"))
	if value == "" {
		return "", publicError(http.StatusBadRequest, "invocation_invalid_request", "planId is required")
	}
	return value, nil
}

func invocationID(request api.WorkspaceScopedRequest) (string, error) {
	value := strings.TrimSpace(request.Gin.Param("invocationId"))
	if value == "" {
		return "", publicError(http.StatusBadRequest, "invocation_invalid_request", "invocationId is required")
	}
	return value, nil
}

func pageLimit(request api.WorkspaceScopedRequest) (int, error) {
	raw := strings.TrimSpace(request.Gin.Query("limit"))
	if raw == "" {
		return 0, nil
	}
	value, err := strconv.Atoi(raw)
	if err != nil || value < 1 || value > 100 {
		return 0, publicError(http.StatusBadRequest, "invocation_invalid_request", "limit must be between 1 and 100")
	}
	return value, nil
}

func requireIdempotencyKey(raw string) (string, error) {
	key := strings.TrimSpace(raw)
	if key == "" {
		return "", publicError(http.StatusBadRequest, "invocation_idempotency_key_required", "Idempotency-Key is required")
	}
	if len([]byte(key)) > maxInvocationIdempotencyKeyBytes {
		return "", publicError(http.StatusBadRequest, "invocation_invalid_idempotency_key", fmt.Sprintf("Idempotency-Key must not exceed %d bytes", maxInvocationIdempotencyKeyBytes))
	}
	for _, character := range key {
		if character < 0x21 || character > 0x7e {
			return "", publicError(http.StatusBadRequest, "invocation_invalid_idempotency_key", "Idempotency-Key must contain only visible ASCII characters")
		}
	}
	return key, nil
}

func requireInvocationInput(value []byte) ([]byte, error) {
	trimmed := bytes.TrimSpace(value)
	if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) || trimmed[0] != '{' {
		return nil, publicError(http.StatusBadRequest, "invocation_invalid_request", "input must be a JSON object")
	}
	if len(value) > invocationlang.MaxInputBytes {
		return nil, publicError(http.StatusRequestEntityTooLarge, "invocation_request_too_large", fmt.Sprintf("input must not exceed %d bytes", invocationlang.MaxInputBytes))
	}
	return value, nil
}
