package automation

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"time"

	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	outboundintentv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/outboundintentv1"
)

// commitRuntimeOutboundMailResultProjection is the Runtime-owned completion
// side of SEND_MAIL. The caller has already locked the exact ACCEPTED intent;
// both fixed completion records and the terminal receipt therefore remain in
// the same caller-owned transaction. Integration supplies only the bounded
// typed delivery result, never object, field, or record identities.
func (e *FlowExecutor) commitRuntimeOutboundMailResultProjection(
	ctx context.Context,
	view *catalogsnapshot.RuntimeMetadataView,
	authorizedWorkloadID string,
	intent outboundintentv1.Intent,
	request outboundintentv1.CommitResultRequest,
) error {
	if e == nil || e.persistence == nil {
		return fmt.Errorf("Runtime outbound mail result: completion persistence is unavailable")
	}
	payload, err := decodeRuntimeOutboundMailPayload(intent)
	if err != nil || len(payload.ToAddresses) != 1 {
		return fmt.Errorf("Runtime outbound mail result: invalid single-recipient intent payload")
	}
	result, err := outboundintentv1.DecodeMailDeliveryResultV1(request.CanonicalResult)
	if err != nil {
		return fmt.Errorf("Runtime outbound mail result: invalid typed result")
	}
	route, err := resolveRuntimeOutboundMailResultAuthority(view, authorizedWorkloadID, intent)
	if err != nil {
		return err
	}
	if result.Outcome != outboundintentv1.MailOutcomeDelivered {
		return nil
	}
	completion := route.MailCompletion
	if completion == nil {
		return fmt.Errorf("Runtime outbound mail result: completion authority is unavailable")
	}

	completedAt := time.Now().UTC()
	activityID := runtimeOutboundMailCompletionID(intent.IntentID, "activity")
	messageID := runtimeOutboundMailCompletionID(intent.IntentID, "message")
	activity := models.Record{
		constants.FieldID:                     activityID,
		completion.Activity.SourceRecordField: intent.SourceRecordID,
		completion.Activity.SubjectField:      completion.Activity.FixedSubject,
		completion.Activity.ActivityDateField: completedAt.Format(time.DateOnly),
		completion.Activity.StatusField:       completion.Activity.FixedStatus,
		completion.Activity.PriorityField:     completion.Activity.FixedPriority,
		completion.Activity.BodyField:         payload.TextBody,
		completion.Activity.OwnerField:        payload.AuthorizedActorID,
		completion.Activity.TypeField:         completion.Activity.FixedType,
		completion.Activity.SubtypeField:      completion.Activity.FixedSubtype,
	}
	message := models.Record{
		constants.FieldID:                   messageID,
		completion.Message.ActivityIDField:  activityID,
		completion.Message.FromAddressField: result.SenderAddress,
		completion.Message.ToAddressField:   payload.ToAddresses[0],
		completion.Message.SubjectField:     payload.Subject,
		completion.Message.TextBodyField:    payload.TextBody,
		completion.Message.IncomingField:    completion.Message.FixedIncoming,
		completion.Message.StatusField:      completion.Message.FixedStatus,
		completion.Message.MessageDateField: completedAt.Format(time.RFC3339Nano),
	}
	if intent.PayloadFormatVersion == outboundintentv1.MailPayloadFormatV2 ||
		intent.PayloadFormatVersion == outboundintentv1.MailPayloadFormatV3 {
		if completion.Message.CcAddressField == "" || completion.Message.BccAddressField == "" ||
			completion.Message.HTMLBodyField == "" {
			return fmt.Errorf("Runtime outbound mail result: mail v2 completion authority is unavailable")
		}
		message[completion.Message.CcAddressField] = strings.Join(payload.CcAddresses, "; ")
		message[completion.Message.BccAddressField] = strings.Join(payload.BccAddresses, "; ")
		message[completion.Message.HTMLBodyField] = payload.HTMLBody
	}
	if intent.PayloadFormatVersion == outboundintentv1.MailPayloadFormatV3 {
		if completion.Message.AttachmentIDsField == "" || len(payload.Attachments) == 0 {
			return fmt.Errorf("Runtime outbound mail result: attachment completion authority is unavailable")
		}
		attachmentIDs := make([]string, 0, len(payload.Attachments))
		for _, attachment := range payload.Attachments {
			attachmentIDs = append(attachmentIDs, attachment.FileID)
		}
		message[completion.Message.AttachmentIDsField] = strings.Join(attachmentIDs, ";")
	}

	writeCtx := metadata.WithRuntimeMetadataView(ctx, view)
	writeCtx = constants.WithoutStandardActionExecutionContext(writeCtx)
	writeCtx = constants.WithoutStandardActionInternalAccessScopes(writeCtx)
	writeCtx = constants.WithoutStandardActionProtectedFlowExecution(writeCtx)
	writeCtx = constants.WithSecurityContext(writeCtx, constants.SystemContext, "Runtime outbound mail completion")
	// The SystemContext owns fixed completion authority; the immutable intent's
	// authorized actor remains the audit identity for the records it initiated.
	actor := &models.UserSession{ID: payload.AuthorizedActorID}
	activityWriteCtx := context.WithValue(
		writeCtx,
		constants.ContextKeySuppressRecordTriggeredAutomation,
		completion.Activity.ObjectAPIName+"/"+activityID,
	)
	if _, err := e.persistence.Insert(
		activityWriteCtx, completion.Activity.ObjectAPIName, activity, actor,
	); err != nil {
		return fmt.Errorf("Runtime outbound mail result: create activity completion: %w", err)
	}
	messageWriteCtx := context.WithValue(
		writeCtx,
		constants.ContextKeySuppressRecordTriggeredAutomation,
		completion.Message.ObjectAPIName+"/"+messageID,
	)
	if _, err := e.persistence.Insert(
		messageWriteCtx, completion.Message.ObjectAPIName, message, actor,
	); err != nil {
		return fmt.Errorf("Runtime outbound mail result: create message completion: %w", err)
	}
	return nil
}

type runtimeOutboundMailPayload struct {
	AuthorizedActorID string
	ToAddresses       []string
	CcAddresses       []string
	BccAddresses      []string
	Subject           string
	TextBody          string
	HTMLBody          string
	Attachments       []outboundintentv1.MailAttachmentV1
}

func decodeRuntimeOutboundMailPayload(intent outboundintentv1.Intent) (runtimeOutboundMailPayload, error) {
	switch intent.PayloadFormatVersion {
	case outboundintentv1.MailPayloadFormatV1:
		payload, err := outboundintentv1.DecodeMailDeliveryV1(intent)
		if err != nil {
			return runtimeOutboundMailPayload{}, err
		}
		return runtimeOutboundMailPayload{
			AuthorizedActorID: payload.AuthorizedActorID,
			ToAddresses:       payload.ToAddresses, Subject: payload.Subject, TextBody: payload.TextBody,
		}, nil
	case outboundintentv1.MailPayloadFormatV2:
		payload, err := outboundintentv1.DecodeMailDeliveryV2(intent)
		if err != nil {
			return runtimeOutboundMailPayload{}, err
		}
		return runtimeOutboundMailPayload{
			AuthorizedActorID: payload.AuthorizedActorID,
			ToAddresses:       payload.ToAddresses, CcAddresses: payload.CcAddresses,
			BccAddresses: payload.BccAddresses, Subject: payload.Subject,
			TextBody: payload.TextBody, HTMLBody: payload.HTMLBody,
		}, nil
	case outboundintentv1.MailPayloadFormatV3:
		payload, err := outboundintentv1.DecodeMailDeliveryV3(intent)
		if err != nil {
			return runtimeOutboundMailPayload{}, err
		}
		return runtimeOutboundMailPayload{
			AuthorizedActorID: payload.AuthorizedActorID,
			ToAddresses:       payload.ToAddresses, CcAddresses: payload.CcAddresses,
			BccAddresses: payload.BccAddresses, Subject: payload.Subject,
			TextBody: payload.TextBody, HTMLBody: payload.HTMLBody,
			Attachments: append([]outboundintentv1.MailAttachmentV1(nil), payload.Attachments...),
		}, nil
	default:
		return runtimeOutboundMailPayload{}, fmt.Errorf("unsupported mail payload version")
	}
}

func resolveRuntimeOutboundMailResultAuthority(
	view *catalogsnapshot.RuntimeMetadataView,
	authorizedWorkloadID string,
	intent outboundintentv1.Intent,
) (catalogsnapshot.OutboundIntentRoute, error) {
	if view == nil || view.Profile() != catalogsnapshot.ProjectionRecordMutationV1 {
		return catalogsnapshot.OutboundIntentRoute{}, fmt.Errorf("Runtime outbound mail result: exact record-mutation Catalog view is required")
	}
	var routes []catalogsnapshot.OutboundIntentRoute
	for _, route := range view.OutboundIntentRoutes() {
		if route.Operation == outboundintentv1.OperationSendMail && route.MailCompletion != nil &&
			route.AuthorizedWorkloadID == strings.TrimSpace(authorizedWorkloadID) &&
			route.ConnectorOwnerKey == intent.ConnectorOwnerKey &&
			route.FlowDeveloperName == intent.FlowDeveloperName &&
			route.FlowActionName == intent.FlowActionName &&
			route.SourceObject == intent.SourceObject &&
			route.ActionSlotID == intent.ActionSlotID &&
			route.DestinationSlotID == intent.DestinationSlotID &&
			runtimeOutboundIntentBindingsEqual(route.Bindings, intent.Bindings) {
			routes = append(routes, route)
		}
	}
	if len(routes) != 1 {
		return catalogsnapshot.OutboundIntentRoute{}, fmt.Errorf(
			"Runtime outbound mail result: exact completion route count is %d, want 1", len(routes),
		)
	}
	return routes[0], nil
}

func runtimeOutboundMailCompletionID(intentID, kind string) string {
	digest := sha256.Sum256([]byte(strings.Join([]string{
		"nexuscrm", "runtime-outbound-mail", strings.TrimSpace(kind), strings.TrimSpace(intentID),
	}, "\x00")))
	value := digest[:16]
	value[6] = (value[6] & 0x0f) | 0x80
	value[8] = (value[8] & 0x3f) | 0x80
	encoded := hex.EncodeToString(value)
	return strings.Join([]string{
		encoded[0:8], encoded[8:12], encoded[12:16], encoded[16:20], encoded[20:32],
	}, "-")
}
