package run

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

func validRunStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case RunStatusQueued, RunStatusRunning, RunStatusWaiting, RunStatusSucceeded, RunStatusFailed, RunStatusCanceled:
		return true
	default:
		return false
	}
}

func validActiveRunStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case RunStatusQueued, RunStatusRunning, RunStatusWaiting:
		return true
	default:
		return false
	}
}

func validActivationStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case ActivationStatusReady, ActivationStatusWaiting, ActivationStatusSucceeded, ActivationStatusFailed, ActivationStatusCanceled:
		return true
	default:
		return false
	}
}

func validEffectStatus(status string) bool {
	switch strings.TrimSpace(status) {
	case EffectStatusPending, EffectStatusDispatching, EffectStatusDelivered, EffectStatusAwaiting, EffectStatusQueuedForAgent, EffectStatusWaitingForResponder, EffectStatusCompleted, EffectStatusFailed, EffectStatusCanceled:
		return true
	default:
		return false
	}
}

func normalizeEventDraft(value EventDraft) (EventDraft, error) {
	value.Type = strings.TrimSpace(value.Type)
	if value.Type == "" || len(value.Type) > 64 {
		return EventDraft{}, storeerr.BadRequest("workflow Run event type must be between 1 and 64 characters")
	}
	payload, err := deterministicObject("workflow Run event payload", value.PayloadJSON, true)
	if err != nil {
		return EventDraft{}, storeerr.BadRequest(err.Error())
	}
	value.PayloadJSON = payload
	return value, nil
}

func normalizeActivation(value Activation, nowRequired bool) (Activation, error) {
	value.ID = strings.TrimSpace(value.ID)
	value.WorkspaceID = strings.TrimSpace(value.WorkspaceID)
	value.RunID = strings.TrimSpace(value.RunID)
	value.LogicalKey = strings.TrimSpace(value.LogicalKey)
	value.ParentActivationID = strings.TrimSpace(value.ParentActivationID)
	value.NodeID = strings.TrimSpace(value.NodeID)
	value.Opcode = strings.TrimSpace(value.Opcode)
	value.Status = strings.TrimSpace(value.Status)
	value.SelectedPort = strings.TrimSpace(value.SelectedPort)
	value.ErrorCode = strings.TrimSpace(value.ErrorCode)
	if value.ID == "" || value.WorkspaceID == "" || value.RunID == "" || value.LogicalKey == "" || len(value.LogicalKey) > 255 ||
		value.NodeID == "" || len(value.NodeID) > 128 || value.Opcode == "" || len(value.Opcode) > 64 {
		return Activation{}, storeerr.BadRequest("complete bounded workflow Run activation identity is required")
	}
	if !validActivationStatus(value.Status) || value.AttemptCount < 0 || len(value.SelectedPort) > 128 || len(value.ErrorCode) > 128 {
		return Activation{}, storeerr.BadRequest("workflow Run activation status, attempt count, port, or error code is invalid")
	}
	var err error
	value.InputJSON, err = deterministicObject("workflow Run activation input", value.InputJSON, true)
	if err != nil {
		return Activation{}, storeerr.BadRequest(err.Error())
	}
	value.ScopeContextJSON, err = deterministicObject("workflow Run activation scope context", value.ScopeContextJSON, true)
	if err != nil {
		return Activation{}, storeerr.BadRequest(err.Error())
	}
	value.OutputJSON, err = deterministicObject("workflow Run activation output", value.OutputJSON, false)
	if err != nil {
		return Activation{}, storeerr.BadRequest(err.Error())
	}
	value.ErrorJSON, err = deterministicObject("workflow Run activation error", value.ErrorJSON, false)
	if err != nil {
		return Activation{}, storeerr.BadRequest(err.Error())
	}
	value.RetryAt, value.WakeAt, value.DeadlineAt = utcPtr(value.RetryAt), utcPtr(value.WakeAt), utcPtr(value.DeadlineAt)
	value.StartedAt, value.CompletedAt = utcPtr(value.StartedAt), utcPtr(value.CompletedAt)
	if nowRequired && (value.CreatedAt.IsZero() || value.UpdatedAt.IsZero()) {
		return Activation{}, storeerr.BadRequest("workflow Run activation timestamps are required")
	}
	if !value.CreatedAt.IsZero() {
		value.CreatedAt = utc(value.CreatedAt)
	}
	if !value.UpdatedAt.IsZero() {
		value.UpdatedAt = utc(value.UpdatedAt)
	}
	return value, nil
}

func normalizeEffect(value Effect, nowRequired bool) (Effect, error) {
	value.ID = strings.TrimSpace(value.ID)
	value.WorkspaceID = strings.TrimSpace(value.WorkspaceID)
	value.RunID = strings.TrimSpace(value.RunID)
	value.ActivationID = strings.TrimSpace(value.ActivationID)
	value.ApprovalAssigneeMemberID = strings.TrimSpace(value.ApprovalAssigneeMemberID)
	value.CallbackResponderAgentID = strings.TrimSpace(value.CallbackResponderAgentID)
	value.MessageID = strings.TrimSpace(value.MessageID)
	value.Kind = strings.TrimSpace(value.Kind)
	value.IdempotencyKey = strings.TrimSpace(value.IdempotencyKey)
	value.Status = strings.TrimSpace(value.Status)
	value.ErrorCode = strings.TrimSpace(value.ErrorCode)
	if value.ID == "" || value.WorkspaceID == "" || value.RunID == "" || value.ActivationID == "" ||
		value.Kind == "" || len(value.Kind) > 64 || value.IdempotencyKey == "" || len(value.IdempotencyKey) > 255 ||
		len(value.ApprovalAssigneeMemberID) > 64 || len(value.CallbackResponderAgentID) > 255 || len(value.MessageID) > 255 {
		return Effect{}, storeerr.BadRequest("complete bounded workflow Run effect identity is required")
	}
	if !validEffectStatus(value.Status) || value.AttemptCount < 0 || len(value.ErrorCode) > 128 {
		return Effect{}, storeerr.BadRequest("workflow Run effect status, attempt count, or error code is invalid")
	}
	if value.MessageID != "" && value.Kind != EffectKindMessage {
		return Effect{}, storeerr.BadRequest("only a message effect may carry a message binding")
	}
	if value.CallbackResponderAgentID != "" && value.Kind != EffectKindMessage {
		return Effect{}, storeerr.BadRequest("only a message effect may carry a callback responder")
	}
	var err error
	value.RequestJSON, err = deterministicObject("workflow Run effect request", value.RequestJSON, true)
	if err != nil {
		return Effect{}, storeerr.BadRequest(err.Error())
	}
	value.ResultJSON, err = deterministicObject("workflow Run effect result", value.ResultJSON, false)
	if err != nil {
		return Effect{}, storeerr.BadRequest(err.Error())
	}
	value.ErrorJSON, err = deterministicObject("workflow Run effect error", value.ErrorJSON, false)
	if err != nil {
		return Effect{}, storeerr.BadRequest(err.Error())
	}
	value.RetryAt, value.DeadlineAt, value.LeaseExpiresAt = utcPtr(value.RetryAt), utcPtr(value.DeadlineAt), utcPtr(value.LeaseExpiresAt)
	value.DeliveredAt, value.CompletedAt, value.QueuedAt = utcPtr(value.DeliveredAt), utcPtr(value.CompletedAt), utcPtr(value.QueuedAt)
	if (value.LeaseOwner == "") != (value.LeaseToken == "") || (value.LeaseToken == "") != (value.LeaseExpiresAt == nil) {
		return Effect{}, storeerr.BadRequest("workflow Run effect lease fields must be set or cleared together")
	}
	if nowRequired && (value.CreatedAt.IsZero() || value.UpdatedAt.IsZero()) {
		return Effect{}, storeerr.BadRequest("workflow Run effect timestamps are required")
	}
	if !value.CreatedAt.IsZero() {
		value.CreatedAt = utc(value.CreatedAt)
	}
	if !value.UpdatedAt.IsZero() {
		value.UpdatedAt = utc(value.UpdatedAt)
	}
	return value, nil
}

func mustNormalizedObject(label string, value json.RawMessage) (json.RawMessage, error) {
	canonical, err := deterministicObject(label, value, true)
	if err != nil {
		return nil, fmt.Errorf("%s: %w", label, err)
	}
	return canonical, nil
}
