package automation

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

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

type approvalHistoryRow struct {
	ID                string
	ProcessInstanceID string
	StepNodeID        string
	ProcessNodeID     string
	TargetObjectID    string
	TargetObjectType  string
	StepStatus        string
	ActorID           string
	OriginalActorID   string
	Comments          string
	IsPending         bool
	CreatedDate       string
}

func (s *ApprovalService) appendApprovalHistoryRow(ctx context.Context, row approvalHistoryRow, user *models.UserSession) error {
	if strings.TrimSpace(row.ProcessInstanceID) == "" {
		return apperrors.NewInternalError("approval history missing ProcessInstanceId", nil)
	}
	if strings.TrimSpace(row.StepStatus) == "" {
		return apperrors.NewInternalError("approval history missing StepStatus", nil)
	}
	if strings.TrimSpace(row.ActorID) == "" {
		return apperrors.NewInternalError("approval history missing ActorId", nil)
	}
	now := strings.TrimSpace(row.CreatedDate)
	if now == "" {
		now = time.Now().UTC().Format(time.RFC3339)
	}

	record := models.SObject{
		"ProcessInstanceId": row.ProcessInstanceID,
		"StepStatus":        row.StepStatus,
		"ActorId":           row.ActorID,
		"Comments":          row.Comments,
		"IsPending":         row.IsPending,
		"CreatedDate":       now,
		"LastModifiedDate":  now,
	}
	if row.ID != "" {
		record[constants.FieldID] = row.ID
	}
	if row.StepNodeID != "" {
		record["StepNodeId"] = row.StepNodeID
	}
	if row.ProcessNodeID != "" {
		record["ProcessNodeId"] = row.ProcessNodeID
	}
	if row.TargetObjectID != "" {
		record[constants.FieldProcessInstanceHistory_TargetObjectID] = row.TargetObjectID
	}
	if row.TargetObjectType != "" {
		record[constants.FieldProcessInstanceHistory_TargetObjectType] = row.TargetObjectType
	}
	originalActorID := strings.TrimSpace(row.OriginalActorID)
	if originalActorID == "" {
		originalActorID = row.ActorID
	}
	if originalActorID != "" {
		record["OriginalActorId"] = originalActorID
	}

	if _, err := s.persistence.Insert(processTableWriteContext(ctx, "ApprovalService: append ProcessInstanceHistory"), constants.TableProcessInstanceHistory, record, user); err != nil {
		return fmt.Errorf("failed to append ProcessInstanceHistory: %w", err)
	}
	return nil
}

func (s *ApprovalService) updatePendingApprovalHistoryActor(ctx context.Context, workItemID, instanceID, targetObjectID, targetObjectType, previousActorID, newActorID, now string, user *models.UserSession) error {
	if strings.TrimSpace(workItemID) == "" || strings.TrimSpace(instanceID) == "" || strings.TrimSpace(previousActorID) == "" || strings.TrimSpace(newActorID) == "" {
		return nil
	}
	filter := fmt.Sprintf(
		"Id == '%s' && ProcessInstanceId == '%s' && StepStatus == '%s'",
		escapeApprovalFilterValue(workItemID),
		escapeApprovalFilterValue(instanceID),
		constants.ApprovalStatusPending,
	)
	readCtx := constants.WithSecurityContext(ctx, constants.SystemContext, "ApprovalService: query pending ProcessInstanceHistory for reassignment")
	result, err := s.query.QueryWithFilter(readCtx, constants.TableProcessInstanceHistory, filter, user, "", "", 200)
	if err != nil {
		return fmt.Errorf("failed to query pending ProcessInstanceHistory for reassignment: %w", err)
	}

	updated := false
	if result != nil {
		for _, row := range result.Records {
			if !approvalBoolValue(row[constants.FieldProcessInstanceHistory_IsPending]) {
				continue
			}
			historyID := approvalStringValue(row[constants.FieldID])
			if historyID == "" {
				continue
			}
			updates := models.SObject{
				constants.FieldProcessInstanceHistory_ActorID: newActorID,
			}
			if err := s.persistence.Update(processTableWriteContext(ctx, "ApprovalService: reassign ProcessInstanceHistory"), constants.TableProcessInstanceHistory, historyID, updates, user); err != nil {
				return fmt.Errorf("failed to update ProcessInstanceHistory reassignment: %w", err)
			}
			updated = true
		}
	}
	if updated {
		return nil
	}

	return s.appendApprovalHistoryRow(ctx, approvalHistoryRow{
		ID:                workItemID,
		ProcessInstanceID: instanceID,
		TargetObjectID:    targetObjectID,
		TargetObjectType:  targetObjectType,
		StepStatus:        constants.ApprovalStatusPending,
		ActorID:           newActorID,
		OriginalActorID:   previousActorID,
		Comments:          "",
		IsPending:         true,
		CreatedDate:       now,
	}, user)
}

func (s *ApprovalService) deletePendingApprovalHistoryRows(ctx context.Context, workItemIDs []string, user *models.UserSession) error {
	idLiterals := make([]string, 0, len(workItemIDs))
	for _, workItemID := range workItemIDs {
		workItemID = strings.TrimSpace(workItemID)
		if workItemID == "" {
			continue
		}
		idLiterals = append(idLiterals, fmt.Sprintf("'%s'", escapeApprovalFilterValue(workItemID)))
	}
	if len(idLiterals) == 0 {
		return nil
	}
	filter := fmt.Sprintf(
		"Id in [%s] && StepStatus == '%s'",
		strings.Join(idLiterals, ", "),
		constants.ApprovalStatusPending,
	)
	readCtx := constants.WithSecurityContext(ctx, constants.SystemContext, "ApprovalService: query pending ProcessInstanceHistory for retirement")
	result, err := s.query.QueryWithFilter(readCtx, constants.TableProcessInstanceHistory, filter, user, "", "", 500)
	if err != nil {
		return fmt.Errorf("failed to query pending ProcessInstanceHistory for retirement: %w", err)
	}
	if result == nil || len(result.Records) == 0 {
		return nil
	}

	for _, row := range result.Records {
		if !approvalBoolValue(row[constants.FieldProcessInstanceHistory_IsPending]) {
			continue
		}
		historyID := approvalStringValue(row[constants.FieldID])
		if historyID == "" {
			continue
		}
		if err := s.persistence.Delete(processTableWriteContext(ctx, "ApprovalService: retire pending ProcessInstanceHistory"), constants.TableProcessInstanceHistory, historyID, user); err != nil {
			return fmt.Errorf("failed to retire pending ProcessInstanceHistory %s: %w", historyID, err)
		}
	}
	return nil
}
