package approvalapp

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/service/agentapproval"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/approval"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

// AgentManagementConsumer is the subset of the agentmanagement service the
// adapter calls. Keeps the handler testable with a fake.
type AgentManagementConsumer interface {
	Submit(context.Context, agent.AuthenticatedAgent, agentapproval.SubmitInput) (agentapproval.Result, error)
	Decide(context.Context, approval.DecideParams) (agentapproval.DecisionResult, error)
}

type agentManagementReplayConsumer interface {
	LookupReplay(context.Context, agent.AuthenticatedAgent, agentapproval.SubmitInput) (agentapproval.Result, bool, error)
}

type agentManagementSubjectHandler struct {
	approvals *approval.Store
	service   AgentManagementConsumer
	projector *Projector
}

const agentManagementCardStatusProcessing = "processing"

// NewAgentManagementSubjectHandler wires the agent_management domain service into
// the Approval subject registry. PresentPending projects one owner-Human DM
// pending actionable card per approval.
func NewAgentManagementSubjectHandler(approvals *approval.Store, service AgentManagementConsumer, projector *Projector) SubjectHandler {
	return &agentManagementSubjectHandler{approvals: approvals, service: service, projector: projector}
}

func (*agentManagementSubjectHandler) SubjectType() string { return agentapproval.SubjectType }
func (*agentManagementSubjectHandler) RequestType() string { return agentapproval.RequestType }
func (*agentManagementSubjectHandler) CancelPolicy() SubjectCancelPolicy {
	return SubjectCancelNotSupported
}
func (*agentManagementSubjectHandler) ReconcilePolicy() SubjectReconcilePolicy {
	return SubjectReconcileNone
}

func (handler *agentManagementSubjectHandler) Validate(_ context.Context, instance approval.Instance) error {
	if handler == nil || handler.approvals == nil || handler.service == nil || handler.projector == nil {
		return fmt.Errorf("agent management Approval subject handler is not configured")
	}
	if instance.SubjectType != agentapproval.SubjectType || len(instance.Payload) == 0 {
		return fmt.Errorf("agent management Approval subject is invalid")
	}
	var payload agentapproval.Payload
	if err := json.Unmarshal(instance.Payload, &payload); err != nil {
		return fmt.Errorf("decode agent management payload: %w", err)
	}
	if payload.SchemaVersion != agentapproval.PayloadSchemaVersion {
		return fmt.Errorf("agent management payload schema version is not supported")
	}
	return nil
}

func (handler *agentManagementSubjectHandler) Submit(ctx context.Context, command SubmitCommand) (SubmitResult, error) {
	payload, ok := command.Payload.(agentapproval.SubmitInput)
	if !ok {
		return SubmitResult{}, storeerr.BadRequest("agent management payload is invalid")
	}
	payload.ClientRequestID = command.ClientRequestID
	payload.CorrelationID = command.CorrelationID
	payload.Now = command.Now
	result, err := handler.service.Submit(ctx, authenticatedAgent(command.Principal), payload)
	if err != nil {
		return SubmitResult{}, err
	}
	return SubmitResult{Instance: result.Approval, Assignments: result.Assignments, Created: result.Created, Value: result}, nil
}

func (handler *agentManagementSubjectHandler) LookupReplay(ctx context.Context, command SubmitCommand) (SubmitResult, bool, error) {
	replays, ok := handler.service.(agentManagementReplayConsumer)
	if !ok {
		return SubmitResult{}, false, nil
	}
	payload, ok := command.Payload.(agentapproval.SubmitInput)
	if !ok {
		return SubmitResult{}, false, storeerr.BadRequest("agent management payload is invalid")
	}
	payload.ClientRequestID = command.ClientRequestID
	payload.CorrelationID = command.CorrelationID
	payload.Now = command.Now
	result, found, err := replays.LookupReplay(ctx, authenticatedAgent(command.Principal), payload)
	if err != nil || !found {
		return SubmitResult{}, found, err
	}
	return SubmitResult{
		Instance: result.Approval, Assignments: result.Assignments,
		Created: false, Value: result,
	}, true, nil
}

func (*agentManagementSubjectHandler) DecodeSubmitPayload(value any) (any, error) {
	switch value := value.(type) {
	case agentapproval.SubmitInput:
		return value, nil
	case json.RawMessage:
		var request struct {
			Action    string                           `json:"action"`
			Create    *agentapproval.CreateProposal    `json:"create,omitempty"`
			Update    *agentapproval.UpdateProposal    `json:"update,omitempty"`
			Delete    *agentapproval.DeleteProposal    `json:"delete,omitempty"`
			Duplicate *agentapproval.DuplicateProposal `json:"duplicate,omitempty"`
		}
		decoder := json.NewDecoder(strings.NewReader(string(value)))
		decoder.DisallowUnknownFields()
		if err := decoder.Decode(&request); err != nil {
			return nil, storeerr.BadRequest("agent management payload is invalid")
		}
		var trailing any
		if err := decoder.Decode(&trailing); err != io.EOF {
			return nil, storeerr.BadRequest("agent management payload is invalid")
		}
		return agentapproval.SubmitInput{Payload: agentapproval.Payload{
			Action: request.Action, Create: request.Create, Update: request.Update,
			Delete: request.Delete, Duplicate: request.Duplicate,
		}}, nil
	default:
		return nil, storeerr.BadRequest("agent management payload is invalid")
	}
}

func (handler *agentManagementSubjectHandler) Decide(ctx context.Context, command DecisionCommand) (DecisionResult, error) {
	decided, err := handler.service.Decide(ctx, approval.DecideParams{
		WorkspaceID:      command.WorkspaceID,
		ApprovalID:       command.ApprovalID,
		AssignmentID:     command.AssignmentID,
		ActorMemberID:    command.ActorMemberID,
		ExpectedVersion:  command.ExpectedVersion,
		ClientDecisionID: command.ClientDecisionID,
		Decision:         command.Decision,
		Reason:           command.Reason,
		Now:              command.Now,
	})
	if err != nil {
		return DecisionResult{}, err
	}
	result := map[string]any(nil)
	if decided.Instance.State == approval.StateApproved {
		if decided.OperationID <= 0 {
			return DecisionResult{}, fmt.Errorf(
				"approved agent management decision has no operation",
			)
		}
		result = map[string]any{"operationId": decided.OperationID}
	}
	return DecisionResult{
		Instance: decided.Instance, Replay: decided.Replay, Result: result,
	}, nil
}

// PresentPending projects one owner-Human DM pending actionable card. The owner
// (sole approver) receives an approve/reject card addressed via the approval
// assignment; the recipient basis lets the projector resolve delivery without a
// separate channel binding. ClientNotificationID is stable for idempotent re-projection.
func (handler *agentManagementSubjectHandler) PresentPending(ctx context.Context, result SubmitResult) (app.AppNotificationResult, error) {
	if result.Instance.SubjectType != agentapproval.SubjectType {
		return app.AppNotificationResult{}, fmt.Errorf("agent management PresentPending subject mismatch")
	}
	if result.Instance.State != approval.StatePending || result.Instance.Version <= 0 {
		return app.AppNotificationResult{}, fmt.Errorf("agent management approval is not pending")
	}
	if len(result.Assignments) != 1 {
		return app.AppNotificationResult{}, fmt.Errorf("agent management approval must have exactly one owner assignment")
	}
	var payload agentapproval.Payload
	if err := json.Unmarshal(result.Instance.Payload, &payload); err != nil {
		return app.AppNotificationResult{}, fmt.Errorf("decode agent management payload for card: %w", err)
	}
	assignment := result.Assignments[0]
	if assignment.ApprovalInstanceID != result.Instance.ID {
		return app.AppNotificationResult{}, fmt.Errorf("agent management assignment does not belong to the approval")
	}
	if assignment.Status != approval.AssignmentPending {
		return app.AppNotificationResult{}, fmt.Errorf("agent management assignment is not pending")
	}
	if assignment.ApproverMemberID == "" || assignment.ApproverMemberID != payload.Owner.MemberID {
		return app.AppNotificationResult{}, fmt.Errorf("agent management assignment approver does not match the frozen owner")
	}
	if _, err := handler.projector.provisioner.EnsureWorkspace(ctx, result.Instance.WorkspaceID, time.Now().UTC()); err != nil {
		return app.AppNotificationResult{}, err
	}
	currentProcessorName, err := handler.projector.memberDisplayName(
		ctx, result.Instance.WorkspaceID, assignment.ApproverMemberID, "",
	)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	content, err := pendingAgentManagementCardContent(
		result.Instance, assignment, payload, currentProcessorName,
	)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	return handler.projector.notifications.CreateAppNotificationForBuiltin(ctx, IdentityID, result.Instance.WorkspaceID, app.AppNotificationCommand{
		ClientNotificationID: agentManagementReviewClientNotificationID(result.Instance.ID),
		Target:               app.AppNotificationTarget{Kind: app.AppNotificationTargetHumanDM, MemberID: assignment.ApproverMemberID},
		Content:              content,
		BusinessObject: app.AppNotificationBusinessObject{
			Namespace: BusinessNamespace, Type: BusinessTypeInstance,
			ID: result.Instance.ID, Version: result.Instance.Version,
		},
		RecipientBasis: app.AppNotificationRecipientBasis{Kind: RecipientBasisAssignment, Ref: assignment.ID},
		CausedByActor:  app.AppNotificationActorRef{Type: "agent", ID: payload.Applicant.AgentID},
		CorrelationID:  result.Instance.CorrelationID,
	})
}

type agentManagementResolvedView struct {
	payload      agentapproval.Payload
	notification app.AppNotificationRecord
	card         app.AppNotificationCardRecord
	snapshot     agentManagementReviewCard
}

func (handler *agentManagementSubjectHandler) PresentResolved(
	ctx context.Context,
	result DecisionResult,
	presentation PresentationContext,
) (app.AppNotificationResult, error) {
	instance := result.Instance
	terminal, err := handler.loadAgentManagementResolvedView(ctx, instance, presentation.NotificationID)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	targetStatus := approval.StateRejected
	mutationKind := app.AppNotificationMutationResolve
	if instance.State == approval.StateApproved {
		targetStatus = agentManagementCardStatusProcessing
		mutationKind = app.AppNotificationMutationUpdate
	}
	if agentManagementProjectionAlreadyApplied(terminal.notification, terminal.card, terminal.snapshot, instance, targetStatus) {
		return handler.projector.notifications.GetAppNotificationForBuiltin(
			ctx, IdentityID, instance.WorkspaceID, terminal.notification.ID,
		)
	}
	if terminal.notification.Status != app.AppNotificationStatusActive ||
		terminal.card.State != app.AppNotificationCardStateActive ||
		terminal.snapshot.Status != approval.StatePending || len(terminal.snapshot.Actions) == 0 {
		return app.AppNotificationResult{}, fmt.Errorf(
			"agent management review card cannot converge from notification %q, card %q, status %q",
			terminal.notification.Status, terminal.card.State, terminal.snapshot.Status,
		)
	}
	processorName, err := handler.projector.memberDisplayName(
		ctx, instance.WorkspaceID, instance.DecidedByMemberID, "",
	)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	content, err := agentManagementDecisionCardContent(
		instance, terminal.snapshot.Approval.AssignmentID, terminal.payload, targetStatus,
		processorName,
	)
	if err != nil {
		return app.AppNotificationResult{}, err
	}
	mutationID := strings.TrimSpace(presentation.MutationID)
	if mutationID == "" {
		return app.AppNotificationResult{}, fmt.Errorf("agent management action mutation id is required")
	}
	version := instance.Version
	command := app.AppNotificationMutationCommand{
		NotificationID:        terminal.notification.ID,
		Kind:                  mutationKind,
		ExpectedRevision:      terminal.notification.Revision,
		ClientMutationID:      "agent-management-action:" + mutationID + ":" + terminal.notification.ID,
		Content:               &content,
		BusinessObjectVersion: &version,
		CausedByActor: app.AppNotificationActorRef{
			Type: "human", ID: instance.DecidedByMemberID,
		},
		CorrelationID: instance.CorrelationID,
	}
	return handler.mutateAgentManagementResolved(ctx, instance.WorkspaceID, presentation, command)
}

func (handler *agentManagementSubjectHandler) loadAgentManagementResolvedView(
	ctx context.Context,
	instance approval.Instance,
	notificationID string,
) (agentManagementResolvedView, error) {
	if instance.SubjectType != agentapproval.SubjectType ||
		(instance.State != approval.StateApproved && instance.State != approval.StateRejected) || instance.Version <= 1 {
		return agentManagementResolvedView{}, fmt.Errorf("agent management resolved approval is invalid")
	}
	if handler == nil || handler.projector == nil || handler.projector.notifications == nil ||
		handler.projector.provisioner == nil || handler.approvals == nil {
		return agentManagementResolvedView{}, fmt.Errorf("agent management Approval projector is not configured")
	}
	notificationID = strings.TrimSpace(notificationID)
	if notificationID == "" {
		return agentManagementResolvedView{}, fmt.Errorf("agent management action notification is required")
	}
	var payload agentapproval.Payload
	if err := json.Unmarshal(instance.Payload, &payload); err != nil {
		return agentManagementResolvedView{}, fmt.Errorf("decode agent management payload for decision card: %w", err)
	}
	provisioned, err := handler.projector.provisioner.EnsureWorkspace(ctx, instance.WorkspaceID, time.Now().UTC())
	if err != nil {
		return agentManagementResolvedView{}, err
	}
	expectedClientID := agentManagementReviewClientNotificationID(instance.ID)
	stable, found, err := handler.projector.notifications.FindAppNotificationByClientID(
		ctx, instance.WorkspaceID, provisioned.Installation.ID, expectedClientID,
	)
	if err != nil {
		return agentManagementResolvedView{}, err
	}
	if !found || stable.ID != notificationID {
		return agentManagementResolvedView{}, fmt.Errorf("agent management action notification does not match its stable review card")
	}
	notification, err := handler.projector.notifications.LoadAppNotification(ctx, instance.WorkspaceID, notificationID)
	if err != nil {
		return agentManagementResolvedView{}, err
	}
	if err := validateAgentManagementReviewNotification(notification, instance, expectedClientID); err != nil {
		return agentManagementResolvedView{}, err
	}
	card, err := handler.projector.notifications.LoadAppNotificationCardInTx(
		ctx, handler.projector.provisioner.db, notification.MessageID, false,
	)
	if err != nil {
		return agentManagementResolvedView{}, err
	}
	snapshot, err := decodeAgentManagementReviewCard(card.RenderPayloadJSON)
	if err != nil {
		return agentManagementResolvedView{}, err
	}
	if card.AppNotificationID != notification.ID || card.MessageID != notification.MessageID {
		return agentManagementResolvedView{}, fmt.Errorf("agent management action card identity does not match")
	}
	if err := validateAgentManagementReviewCard(card, snapshot, instance); err != nil {
		return agentManagementResolvedView{}, err
	}
	assignment, authorityInstance, err := handler.approvals.LoadAssignment(
		ctx, instance.WorkspaceID, snapshot.Approval.AssignmentID,
	)
	if err != nil {
		return agentManagementResolvedView{}, fmt.Errorf("load agent management review assignment authority: %w", err)
	}
	expectedAssignmentStatus := approval.AssignmentRejected
	if instance.State == approval.StateApproved {
		expectedAssignmentStatus = approval.AssignmentApproved
	}
	if assignment.ApprovalInstanceID != instance.ID ||
		assignment.ApproverMemberID != strings.TrimSpace(payload.Owner.MemberID) ||
		assignment.Status != expectedAssignmentStatus || authorityInstance.ID != instance.ID ||
		authorityInstance.WorkspaceID != instance.WorkspaceID || authorityInstance.SubjectType != agentapproval.SubjectType ||
		authorityInstance.Version != instance.Version || authorityInstance.State != instance.State ||
		notification.RecipientMemberID == nil || *notification.RecipientMemberID != assignment.ApproverMemberID ||
		notification.RecipientBasisRef == nil || *notification.RecipientBasisRef != assignment.ID {
		return agentManagementResolvedView{}, fmt.Errorf("agent management review assignment authority does not match")
	}
	return agentManagementResolvedView{payload: payload, notification: notification, card: card, snapshot: snapshot}, nil
}

func (handler *agentManagementSubjectHandler) mutateAgentManagementResolved(
	ctx context.Context,
	workspaceID string,
	presentation PresentationContext,
	command app.AppNotificationMutationCommand,
) (app.AppNotificationResult, error) {
	deliveryID := strings.TrimSpace(presentation.DeliveryID)
	attemptID := strings.TrimSpace(presentation.AttemptID)
	if deliveryID != "" || attemptID != "" {
		if deliveryID == "" || attemptID == "" {
			return app.AppNotificationResult{}, fmt.Errorf("agent management action delivery and attempt must be provided together")
		}
		return handler.projector.notifications.MutateAppNotificationForBuiltinDelivery(
			ctx, IdentityID, workspaceID, deliveryID, attemptID, command,
		)
	}
	return handler.projector.notifications.MutateAppNotificationForBuiltin(
		ctx, IdentityID, workspaceID, command,
	)
}

type agentManagementReviewCard struct {
	Template string `json:"template"`
	Status   string `json:"status"`
	Approval struct {
		ID           string `json:"id"`
		Version      int64  `json:"version"`
		AssignmentID string `json:"assignmentId"`
	} `json:"approval"`
	Actions []json.RawMessage `json:"actions"`
}

func decodeAgentManagementReviewCard(raw json.RawMessage) (agentManagementReviewCard, error) {
	var card agentManagementReviewCard
	if err := json.Unmarshal(raw, &card); err != nil {
		return agentManagementReviewCard{}, fmt.Errorf(
			"decode agent management review card: %w", err,
		)
	}
	if card.Template != "approval_action" || strings.TrimSpace(card.Approval.ID) == "" ||
		strings.TrimSpace(card.Approval.AssignmentID) == "" ||
		card.Approval.Version <= 0 {
		return agentManagementReviewCard{}, fmt.Errorf(
			"agent management review card is invalid",
		)
	}
	return card, nil
}

func validateAgentManagementReviewNotification(
	notification app.AppNotificationRecord,
	instance approval.Instance,
	clientNotificationID string,
) error {
	if notification.WorkspaceID != instance.WorkspaceID ||
		notification.ClientNotificationID != clientNotificationID ||
		notification.TargetKind != app.AppNotificationTargetHumanDM ||
		notification.Status != app.AppNotificationStatusActive &&
			notification.Status != app.AppNotificationStatusResolved ||
		notification.RecipientBasisKind == nil ||
		*notification.RecipientBasisKind != RecipientBasisAssignment ||
		notification.ContentKind != app.AppNotificationContentInteractiveCard ||
		notification.BusinessObjectNamespace == nil ||
		*notification.BusinessObjectNamespace != BusinessNamespace ||
		notification.BusinessObjectType == nil ||
		*notification.BusinessObjectType != BusinessTypeInstance ||
		notification.BusinessObjectID == nil ||
		*notification.BusinessObjectID != instance.ID ||
		notification.BusinessObjectVersion == nil ||
		(*notification.BusinessObjectVersion != instance.Version-1 &&
			*notification.BusinessObjectVersion != instance.Version) {
		return fmt.Errorf(
			"agent management action notification authority does not match the approval",
		)
	}
	return nil
}

func validateAgentManagementReviewCard(
	card app.AppNotificationCardRecord,
	snapshot agentManagementReviewCard,
	instance approval.Instance,
) error {
	if snapshot.Approval.ID != instance.ID ||
		(snapshot.Approval.Version != instance.Version-1 &&
			snapshot.Approval.Version != instance.Version) {
		return fmt.Errorf(
			"agent management action card does not match the approval",
		)
	}
	switch card.State {
	case app.AppNotificationCardStateActive:
		if snapshot.Status != approval.StatePending &&
			snapshot.Status != agentManagementCardStatusProcessing {
			return fmt.Errorf("agent management active review card has invalid status")
		}
	case app.AppNotificationCardStateResolved:
		if snapshot.Status != approval.StateRejected {
			return fmt.Errorf("agent management resolved review card has invalid status")
		}
	default:
		return fmt.Errorf(
			"agent management review card has invalid state %q", card.State,
		)
	}
	return nil
}

func agentManagementProjectionAlreadyApplied(
	notification app.AppNotificationRecord,
	card app.AppNotificationCardRecord,
	snapshot agentManagementReviewCard,
	instance approval.Instance,
	targetStatus string,
) bool {
	expectedNotificationStatus := app.AppNotificationStatusResolved
	expectedCardState := app.AppNotificationCardStateResolved
	if targetStatus == agentManagementCardStatusProcessing {
		expectedNotificationStatus = app.AppNotificationStatusActive
		expectedCardState = app.AppNotificationCardStateActive
	}
	return notification.Status == expectedNotificationStatus &&
		card.State == expectedCardState &&
		snapshot.Status == targetStatus &&
		len(snapshot.Actions) == 0 &&
		snapshot.Approval.Version == instance.Version &&
		notification.BusinessObjectVersion != nil &&
		*notification.BusinessObjectVersion == instance.Version
}

func pendingAgentManagementCardContent(instance approval.Instance, assignment approval.Assignment, payload agentapproval.Payload, currentProcessorName string) (app.AppNotificationContent, error) {
	return agentManagementCardContent(
		instance, assignment.ID, assignment.ApproverMemberID, payload,
		approval.StatePending, currentProcessorName,
	)
}

func agentManagementDecisionCardContent(
	instance approval.Instance,
	assignmentID string,
	payload agentapproval.Payload,
	status string,
	processorName string,
) (app.AppNotificationContent, error) {
	return agentManagementCardContent(
		instance, assignmentID, "", payload, status, processorName,
	)
}

func agentManagementCardContent(
	instance approval.Instance,
	assignmentID string,
	approverMemberID string,
	payload agentapproval.Payload,
	status string,
	processorName string,
) (app.AppNotificationContent, error) {
	assignmentID = strings.TrimSpace(assignmentID)
	approverMemberID = strings.TrimSpace(approverMemberID)
	if strings.TrimSpace(instance.ID) == "" || instance.Version <= 0 ||
		assignmentID == "" {
		return app.AppNotificationContent{}, fmt.Errorf(
			"agent management card approval identity is required",
		)
	}
	if status != approval.StatePending &&
		status != agentManagementCardStatusProcessing &&
		status != approval.StateRejected {
		return app.AppNotificationContent{}, fmt.Errorf(
			"agent management card status %q is invalid", status,
		)
	}
	if status == approval.StatePending && approverMemberID == "" {
		return app.AppNotificationContent{}, fmt.Errorf(
			"agent management pending card approver is required",
		)
	}
	actorPolicy := map[string]any{
		"kind":      "members",
		"memberIds": []string{approverMemberID},
	}
	card := map[string]any{
		"schemaVersion": "1",
		"template":      "approval_action",
		"title":         "审批 Agent 管理请求",
		"titleKey":      "agent_management_review",
		"status":        status,
		"proposal": map[string]any{
			"action":  payload.Action,
			"summary": agentManagementProposalSummary(payload),
		},
		"approval": map[string]any{
			"id":           instance.ID,
			"version":      instance.Version,
			"assignmentId": assignmentID,
		},
		"actions": []map[string]any{},
	}
	if status == approval.StatePending {
		card["approval"].(map[string]any)["currentProcessorNames"] = []string{
			strings.TrimSpace(processorName),
		}
	} else {
		card["approval"].(map[string]any)["processorName"] = strings.TrimSpace(processorName)
	}
	if status == approval.StatePending {
		card["actions"] = []map[string]any{
			{
				"actionKey": ActionApprove, "type": "callback", "label": "通过",
				"semantics": ActionSemanticsCommit, "actorPolicy": actorPolicy,
			},
			{
				"actionKey": ActionReject, "type": "callback", "label": "暂不通过",
				"semantics": ActionSemanticsRevise, "actorPolicy": actorPolicy,
				"inputSchema": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"reason": map[string]any{"type": "string", "maxLength": 1000},
						"locale": map[string]any{"type": "string", "enum": []string{"en", "zh-CN", "ja"}},
					},
					"additionalProperties": false,
				},
			},
		}
	}
	encoded, err := json.Marshal(card)
	if err != nil {
		return app.AppNotificationContent{}, fmt.Errorf("marshal agent management card: %w", err)
	}
	fallbackText := "待审批：Agent 管理请求"
	if status == agentManagementCardStatusProcessing {
		fallbackText = "处理中：Agent 管理请求"
	} else if status == approval.StateRejected {
		fallbackText = "已拒绝：Agent 管理请求"
	}
	return app.AppNotificationContent{
		Kind:         app.AppNotificationContentInteractiveCard,
		FallbackText: fallbackText,
		CardJSON:     encoded,
	}, nil
}

func agentManagementProposalSummary(payload agentapproval.Payload) map[string]any {
	summary := map[string]any{}
	if len(payload.Presentation.Agents) > 0 {
		summary["agents"] = payload.Presentation.Agents
	}
	switch payload.Action {
	case agentapproval.ActionCreate:
		if payload.Create != nil {
			summary["name"], summary["machineId"], summary["runtime"] = payload.Create.Name, payload.Create.MachineID, payload.Create.Runtime
		}
	case agentapproval.ActionUpdate:
		if payload.Update != nil {
			summary["agentId"] = payload.Update.AgentID
		}
	case agentapproval.ActionDelete:
		if payload.Delete != nil {
			summary["agentIds"] = payload.Delete.AgentIDs
		}
	case agentapproval.ActionDuplicate:
		if payload.Duplicate != nil {
			summary["sourceAgentId"], summary["count"], summary["mode"] = payload.Duplicate.SourceAgentID, payload.Duplicate.Count, payload.Duplicate.Mode
		}
	}
	return summary
}
