package invocationmanage

import (
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/workflow/invocation"
)

const (
	defaultPageLimit       = 50
	maxPageLimit           = 100
	maxIdempotencyKeyBytes = 128
)

func requiredID(name string, value string) (string, error) {
	value = strings.TrimSpace(value)
	if value == "" {
		return "", invalidRequest(name + " is required")
	}
	return value, nil
}

func requiredScope(workspaceID string, resourceID string, resourceName string) (string, string, error) {
	workspaceID, err := requiredID("workspace id", workspaceID)
	if err != nil {
		return "", "", err
	}
	resourceID, err = requiredID(resourceName+" id", resourceID)
	if err != nil {
		return "", "", err
	}
	return workspaceID, resourceID, nil
}

func normalizePrincipal(value Principal) (Principal, error) {
	value.UserID = strings.TrimSpace(value.UserID)
	value.MemberID = strings.TrimSpace(value.MemberID)
	value.Role = strings.TrimSpace(value.Role)
	if value.UserID == "" || value.MemberID == "" {
		return Principal{}, invalidRequest("principal user id and member id are required")
	}
	switch domain.WorkspaceRole(value.Role) {
	case domain.WorkspaceRoleOwner, domain.WorkspaceRoleAdmin, domain.WorkspaceRoleMember:
		return value, nil
	default:
		return Principal{}, invalidRequest("principal role must be owner, admin, or member")
	}
}

func normalizeActor(value invocation.Actor, principal Principal) (invocation.Actor, error) {
	value, err := normalizeReplayActor(value)
	if err != nil {
		return invocation.Actor{}, err
	}
	if value.Type == invocation.ActorTypeHuman && (value.ID != principal.UserID || value.MemberID != principal.MemberID) {
		return invocation.Actor{}, RequestError{
			Kind: storeerr.ErrForbidden, Code: ErrorCodeForbidden,
			Message: "human invokedBy identity must match the authenticated principal",
		}
	}
	return value, nil
}

func normalizeReplayActor(value invocation.Actor) (invocation.Actor, error) {
	value.Type = strings.TrimSpace(value.Type)
	value.ID = strings.TrimSpace(value.ID)
	value.MemberID = strings.TrimSpace(value.MemberID)
	if value.Type != invocation.ActorTypeHuman && value.Type != invocation.ActorTypeAgent {
		return invocation.Actor{}, invalidRequest("invokedBy type must be human or agent")
	}
	if value.ID == "" || value.MemberID == "" {
		return invocation.Actor{}, invalidRequest("invokedBy id and member id are required")
	}
	return value, nil
}

func normalizeIdempotencyKey(value string) (string, error) {
	value = strings.TrimSpace(value)
	if value == "" {
		return "", invalidRequest("idempotency key is required")
	}
	if len([]byte(value)) > maxIdempotencyKeyBytes {
		return "", invalidRequest(fmt.Sprintf("idempotency key must not exceed %d bytes", maxIdempotencyKeyBytes))
	}
	for _, character := range value {
		if character < 0x21 || character > 0x7e {
			return "", invalidRequest("idempotency key must contain only visible ASCII characters")
		}
	}
	return value, nil
}

func normalizeList(input ListInput) (ListInput, error) {
	var err error
	input.WorkspaceID, err = requiredID("workspace id", input.WorkspaceID)
	if err != nil {
		return ListInput{}, err
	}
	input.PlanID, err = requiredID("Plan id", input.PlanID)
	if err != nil {
		return ListInput{}, err
	}
	input.Cursor = strings.TrimSpace(input.Cursor)
	if input.Limit == 0 {
		input.Limit = defaultPageLimit
	}
	if input.Limit < 1 || input.Limit > maxPageLimit {
		return ListInput{}, invalidRequest(fmt.Sprintf("limit must be between 1 and %d", maxPageLimit))
	}
	return input, nil
}
