package automation

import (
	"context"
	"fmt"
	"strings"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type approvalActionContext struct {
	instanceID              string
	workItemActorID         string
	workItemOriginalActorID string
	processDefinitionID     string
	targetObjectID          string
	submittedByID           string
}

// ApprovalWorkItemBinding is the immutable runtime-state identity needed to
// bind an action request to its Catalog definition before durable acceptance.
// Operation-specific authorization remains in Approve/Reject/Recall/Reassign.
type ApprovalWorkItemBinding struct {
	ProcessInstanceID   string
	ProcessDefinitionID string
	TargetRecordID      string
}

// ResolveWorkItemBinding resolves only durable runtime identities. It does not
// authorize or mutate the action, and callers must execute the requested
// operation in the same transaction in which this binding was read.
func (s *ApprovalService) ResolveWorkItemBinding(
	ctx context.Context,
	workItemID string,
	user *models.UserSession,
) (ApprovalWorkItemBinding, error) {
	ctx = constants.WithSecurityContext(ctx, constants.SystemContext, "ApprovalService: resolve Runtime work item binding")
	workItemFilter := fmt.Sprintf("Id == '%s'", escapeApprovalFilterValue(workItemID))
	wiResult, err := s.query.QueryWithFilter(ctx, constants.TableProcessInstanceWorkitem, workItemFilter, user, "", "", 1)
	if err != nil {
		return ApprovalWorkItemBinding{}, apperrors.NewInternalError("failed to resolve approval work item binding", err)
	}
	if wiResult == nil || len(wiResult.Records) == 0 {
		return ApprovalWorkItemBinding{}, ErrWorkItemNotFound
	}
	instanceID := strings.TrimSpace(approvalStringValue(wiResult.Records[0]["ProcessInstanceId"]))
	if instanceID == "" {
		return ApprovalWorkItemBinding{}, ErrWorkItemNotFound
	}
	instanceFilter := fmt.Sprintf("Id == '%s'", escapeApprovalFilterValue(instanceID))
	instanceResult, err := s.query.QueryWithFilter(ctx, constants.TableProcessInstance, instanceFilter, user, "", "", 1)
	if err != nil {
		return ApprovalWorkItemBinding{}, apperrors.NewInternalError("failed to resolve approval work item instance", err)
	}
	if instanceResult == nil || len(instanceResult.Records) != 1 {
		return ApprovalWorkItemBinding{}, apperrors.NewInternalError("approval work item instance is missing or ambiguous", nil)
	}
	instance := instanceResult.Records[0]
	binding := ApprovalWorkItemBinding{
		ProcessInstanceID:   instanceID,
		ProcessDefinitionID: strings.TrimSpace(approvalStringValue(instance["ProcessDefinitionId"])),
		TargetRecordID:      strings.TrimSpace(approvalStringValue(instance["TargetObjectId"])),
	}
	if binding.ProcessDefinitionID == "" || binding.TargetRecordID == "" {
		return ApprovalWorkItemBinding{}, apperrors.NewInternalError("approval work item binding is incomplete", nil)
	}
	return binding, nil
}

func (s *ApprovalService) loadApprovalActionContext(ctx context.Context, workItemID string, user *models.UserSession) (approvalActionContext, error) {
	workItemFilter := fmt.Sprintf("Id == '%s'", escapeApprovalFilterValue(workItemID))
	wiResult, err := s.query.QueryWithFilter(ctx, constants.TableProcessInstanceWorkitem, workItemFilter, user, "", "", 1)
	if err != nil {
		return approvalActionContext{}, apperrors.NewInternalError("failed to resolve approval work item", err)
	}
	if wiResult == nil || wiResult.TotalSize == 0 || len(wiResult.Records) == 0 {
		return approvalActionContext{}, ErrWorkItemNotFound
	}
	workItem := wiResult.Records[0]
	instanceID, _ := workItem["ProcessInstanceId"].(string)
	if instanceID == "" {
		return approvalActionContext{}, ErrWorkItemNotFound
	}

	workItemActorID, _ := workItem["ActorId"].(string)
	if workItemActorID != user.ID && !s.hasSuperUserCapability(ctx, user) {
		return approvalActionContext{}, ErrNotAuthorized
	}
	workItemOriginalActorID := approvalStringValue(workItem["OriginalActorId"])
	if workItemOriginalActorID == "" {
		workItemOriginalActorID = workItemActorID
	}

	instanceFilter := fmt.Sprintf("Id == '%s'", escapeApprovalFilterValue(instanceID))
	instResult, err := s.query.QueryWithFilter(ctx, constants.TableProcessInstance, instanceFilter, user, "", "", 1)
	if err != nil {
		return approvalActionContext{}, apperrors.NewInternalError("failed to look up ProcessInstance "+instanceID, err)
	}
	if instResult == nil || instResult.TotalSize == 0 || len(instResult.Records) == 0 {
		return approvalActionContext{}, apperrors.NewInternalError("failed to look up ProcessInstance "+instanceID, nil)
	}
	instance := instResult.Records[0]
	processDefinitionID, _ := instance["ProcessDefinitionId"].(string)
	targetObjectID, _ := instance["TargetObjectId"].(string)
	submittedByID, _ := instance["SubmittedById"].(string)
	if strings.TrimSpace(processDefinitionID) == "" {
		return approvalActionContext{}, apperrors.NewInternalError("approval instance missing ProcessDefinitionId", nil)
	}
	if strings.TrimSpace(targetObjectID) == "" {
		return approvalActionContext{}, apperrors.NewInternalError("approval instance missing TargetObjectId", nil)
	}

	return approvalActionContext{
		instanceID:              instanceID,
		workItemActorID:         workItemActorID,
		workItemOriginalActorID: workItemOriginalActorID,
		processDefinitionID:     processDefinitionID,
		targetObjectID:          targetObjectID,
		submittedByID:           submittedByID,
	}, nil
}
