// Approval-path adapter (design.md §3.7).
//
// Approval-process business actions (SUBMIT / APPROVE / REJECT / RECALL)
// emit one event per action with Entrypoint=APPROVAL. Field-level changes
// on the underlying record (Status transitions, lock state) are NOT this
// adapter's job — those are emitted by whatever mutates the record.
//
// Pure builder — no DB writes.

package audit

import (
	"errors"
	"fmt"
)

// ApprovalKind enumerates the approval-process business verbs this adapter
// recognizes. Maps to AuditAction per the table in the docstring on
// BuildApprovalEvents. RECALL is modeled as UPDATE (user-initiated state
// change that is neither approve nor reject).
type ApprovalKind string

const (
	ApprovalKindSubmit  ApprovalKind = "SUBMIT"
	ApprovalKindApprove ApprovalKind = "APPROVE"
	ApprovalKindReject  ApprovalKind = "REJECT"
	ApprovalKindRecall  ApprovalKind = "RECALL"
)

// ApprovalAuthContext carries actor + correlation ids for an approval action.
type ApprovalAuthContext struct {
	ChangedById string
	RequestId   string
	TraceId     string
	SessionId   string
	ClientIp    string
	UserAgent   string
}

// ApprovalAction is the adapter-facing shape of a single approval business
// action. ProcessDeveloperName is the generic L0 field carrying whichever
// ApprovalProcess DeveloperName the admin configured (L2 metadata) — the
// adapter treats it as an opaque string, never branching on its value.
type ApprovalAction struct {
	Kind                 ApprovalKind
	ObjectApiName        string
	RecordId             string
	ProcessDeveloperName string
	InstanceId           string // optional; approval-process instance id
	Comment              string // optional approver comment
}

// ErrUnknownApprovalKind is returned when ApprovalAction.Kind is not one of
// the four modeled verbs. Fail-loud.
var ErrUnknownApprovalKind = errors.New("audit: unknown ApprovalKind")

// BuildApprovalEvents returns a single-element []AuditEvent representing the
// approval action itself. Mapping:
//
//	SUBMIT  -> ActionSubmit
//	APPROVE -> ActionApprove
//	REJECT  -> ActionReject
//	RECALL  -> ActionUpdate
//
// Every event carries Entrypoint=APPROVAL and
// EntrypointDetail=ProcessDeveloperName.
func BuildApprovalEvents(auth ApprovalAuthContext, action ApprovalAction) ([]AuditEvent, error) {
	if auth.ChangedById == "" {
		return nil, fmt.Errorf("%w: ChangedById", ErrMissingRequired)
	}
	act, err := approvalKindToAction(action.Kind)
	if err != nil {
		return nil, err
	}

	ev := AuditEvent{
		ObjectApiName:    action.ObjectApiName,
		RecordId:         action.RecordId,
		Action:           act,
		ChangedById:      auth.ChangedById,
		Entrypoint:       EntrypointApproval,
		EntrypointDetail: action.ProcessDeveloperName,
		Outcome:          OutcomeSuccess,
		RequestId:        auth.RequestId,
		TraceId:          auth.TraceId,
		SessionId:        auth.SessionId,
		ClientIp:         auth.ClientIp,
		UserAgent:        auth.UserAgent,
	}
	if err := ev.Validate(); err != nil {
		return nil, err
	}
	return []AuditEvent{ev}, nil
}

func approvalKindToAction(k ApprovalKind) (AuditAction, error) {
	switch k {
	case ApprovalKindSubmit:
		return ActionSubmit, nil
	case ApprovalKindApprove:
		return ActionApprove, nil
	case ApprovalKindReject:
		return ActionReject, nil
	case ApprovalKindRecall:
		return ActionUpdate, nil
	default:
		return "", fmt.Errorf("%w: %q", ErrUnknownApprovalKind, k)
	}
}
