package connector_workqueue

import (
	"context"
	"fmt"
	"strings"
	"time"

	contractmodels "github.com/pingcap-inc/nexuscrm-contract/pkg/models"
)

// WorkItemAttemptOutcome is the message-free terminal evidence for one claimed
// connector Flow attempt. The caller-facing Flow error remains elsewhere; this
// shape carries only stable codes and the underlying typed cause.
type WorkItemAttemptOutcome struct {
	FlowExecutionID    string
	PresentedErrorCode string
	Cause              *contractmodels.FlowExecutionCause
}

type workItemAttemptOutcomeContextKey struct{}

// WithWorkItemAttemptOutcome binds terminal evidence to the finalization
// context. A blank FlowExecutionID is rejected so non-Flow finalizers cannot
// accidentally create an uncorrelatable attempt row.
func WithWorkItemAttemptOutcome(ctx context.Context, outcome WorkItemAttemptOutcome) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}
	outcome.FlowExecutionID = strings.TrimSpace(outcome.FlowExecutionID)
	outcome.PresentedErrorCode = strings.TrimSpace(outcome.PresentedErrorCode)
	if outcome.FlowExecutionID == "" {
		return ctx
	}
	if outcome.Cause != nil {
		cause := *outcome.Cause
		cause.Code = strings.TrimSpace(cause.Code)
		cause.ElementType = strings.TrimSpace(cause.ElementType)
		cause.ElementName = strings.TrimSpace(cause.ElementName)
		outcome.Cause = &cause
	}
	return context.WithValue(ctx, workItemAttemptOutcomeContextKey{}, outcome)
}

// WorkItemAttemptOutcomeFromContext returns a defensive copy of the evidence.
func WorkItemAttemptOutcomeFromContext(ctx context.Context) (WorkItemAttemptOutcome, bool) {
	if ctx == nil {
		return WorkItemAttemptOutcome{}, false
	}
	outcome, ok := ctx.Value(workItemAttemptOutcomeContextKey{}).(WorkItemAttemptOutcome)
	if !ok || outcome.FlowExecutionID == "" {
		return WorkItemAttemptOutcome{}, false
	}
	if outcome.Cause != nil {
		cause := *outcome.Cause
		outcome.Cause = &cause
	}
	return outcome, true
}

func appendWorkItemAttempt(
	ctx context.Context,
	q queryExecer,
	workID string,
	attempt int,
	eventID string,
	state string,
	now time.Time,
) error {
	outcome, ok := WorkItemAttemptOutcomeFromContext(ctx)
	if !ok {
		return nil
	}
	if strings.TrimSpace(workID) == "" || attempt <= 0 || strings.TrimSpace(eventID) == "" || strings.TrimSpace(state) == "" || now.IsZero() {
		return ErrInvalidWorkItem
	}
	var causeCode, causeCategory, causeElementType, causeElementName string
	var retriable bool
	if outcome.Cause != nil {
		causeCode = outcome.Cause.Code
		causeCategory = string(outcome.Cause.Category)
		causeElementType = outcome.Cause.ElementType
		causeElementName = outcome.Cause.ElementName
		retriable = outcome.Cause.Retriable
	}
	res, err := q.ExecContext(ctx, `
INSERT INTO _System_ConnectorWorkAttempt
       (WorkId, Attempt, EventId, FlowExecutionId, OutcomeState,
        PresentedErrorCode, CauseCode, CauseCategory, CauseElementType,
        CauseElementName, Retriable, CreatedDate, LastModifiedDate)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		workID, attempt, eventID, outcome.FlowExecutionID, state,
		outcome.PresentedErrorCode, causeCode, causeCategory, causeElementType,
		causeElementName, retriable, now, now,
	)
	if err != nil {
		return err
	}
	inserted, err := rowsAffected(res)
	if err != nil {
		return err
	}
	if !inserted {
		return fmt.Errorf("connector workqueue: attempt ledger append affected no rows")
	}
	return nil
}
