package automation_test

import (
	"context"
	"strings"
	"testing"

	"github.com/nexuscrm/automation"
	"github.com/nexuscrm/kernel/pkg/logging"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/permissions"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

type clonePermissionRepo struct {
	ports.PermissionRepository
}

func (r *clonePermissionRepo) GetEffectiveSystemPermissions(context.Context, string, string) (map[string]bool, error) {
	return map[string]bool{
		string(constants.SuperUserCapability): true,
		string(constants.PermModifyMetadata):  true,
	}, nil
}

func (r *clonePermissionRepo) GetAllRoles(context.Context) ([]*models.UserRole, error) {
	return nil, nil
}

func TestApprovalService_CloneRejectsLegacyAlertActionsBeforePersistence(t *testing.T) {
	persistence := &submitPersistenceStub{}
	query := &submitQueryStub{
		onQuery: func(ctx context.Context, objectName, filter string, user *models.UserSession, sortField, sortDir string, limit int) (*models.QueryResult, error) {
			switch objectName {
			case constants.TableProcessDefinition:
				if strings.Contains(filter, "DeveloperName == 'Cloned_Process'") {
					return &models.QueryResult{}, nil
				}
				if strings.Contains(filter, "Id == 'pd-source'") {
					return &models.QueryResult{
						TotalSize: 1,
						Records: []models.SObject{{
							constants.FieldID: "pd-source",
							"Name":            "Source Process",
							"DeveloperName":   "Source_Process",
							"TableEnumOrId":   "GenericRecord",
						}},
					}, nil
				}
			case constants.TableProcessAction:
				if strings.Contains(filter, "ProcessDefinitionId == 'pd-source'") {
					return &models.QueryResult{
						TotalSize: 1,
						Records: []models.SObject{{
							constants.FieldID:                       "pa-alert-1",
							constants.FieldProcessAction_Name:       "Notify Owner",
							constants.FieldProcessAction_ActionType: constants.ProcessActionTypeAlert,
						}},
					}, nil
				}
			}
			return &models.QueryResult{}, nil
		},
	}
	svc := automation.NewApprovalService(
		persistence,
		query,
		permissions.NewPermissionService(&clonePermissionRepo{}, nil, nil),
		nil,
		nil,
		nil,
		nil,
		logging.Default,
	)

	_, err := svc.Clone(context.Background(), "pd-source", "Cloned_Process", "Cloned Process", submitTestUser())
	if err == nil {
		t.Fatal("Clone() err = nil, want retired ProcessAction.Alert validation error")
	}
	for _, want := range []string{"ProcessAction.Alert", "Notify Owner", "Flow sendEmail", "remove it"} {
		if !strings.Contains(err.Error(), want) {
			t.Fatalf("Clone() err = %q, want substring %q", err.Error(), want)
		}
	}
	if persistence.runTxCalls != 0 {
		t.Fatalf("RunInTransaction calls = %d, want 0 before rejecting legacy Alert actions", persistence.runTxCalls)
	}
	if len(persistence.inserts) != 0 {
		t.Fatalf("Insert calls = %d, want 0 before rejecting legacy Alert actions", len(persistence.inserts))
	}
}

func TestApprovalService_CloneKeepsOwnerlessLiveChildProjection(t *testing.T) {
	const sourceMetadata = `{"approvalStep":[{"name":"Metadata_Only_Step","label":"Metadata Only Step","assignedApprover":{"approver":{"type":"user","name":"005-metadata-only"},"whenMultipleApprovers":"FirstResponse"}}]}`

	parsedMetadata, err := automation.ParseApprovalProcessMetadata(sourceMetadata)
	if err != nil {
		t.Fatalf("ParseApprovalProcessMetadata() err = %v, want valid divergent metadata", err)
	}
	if len(parsedMetadata.Steps) != 1 || parsedMetadata.Steps[0].Name != "Metadata_Only_Step" {
		t.Fatalf("parsed metadata steps = %#v, want the metadata-only step", parsedMetadata.Steps)
	}

	persistence := &submitPersistenceStub{}
	records := map[string][]models.SObject{
		constants.TableProcessDefinition: {{
			constants.FieldID: "pd-source",
			"Name":            "Source Process",
			"DeveloperName":   "Source_Process",
			"TableEnumOrId":   "GenericRecord",
			constants.FieldProcessDefinition_Metadata:     sourceMetadata,
			constants.FieldProcessDefinition_OwnerPack:    "source-managed-pack",
			constants.FieldProcessDefinition_ProcessOrder: 8,
		}},
		constants.TableProcessNode: {{
			constants.FieldID:     "pn-live",
			"ProcessDefinitionId": "pd-source",
			"DeveloperName":       "Live_Child_Step",
		}},
		constants.TableProcessAction: {{
			constants.FieldID:                       "pa-live",
			constants.FieldProcessAction_Name:       "Live Child Action",
			constants.FieldProcessAction_ActionType: constants.ProcessActionTypeFieldUpdate,
			"ProcessDefinitionId":                   "pd-source",
			"ProcessNodeId":                         "pn-live",
		}},
	}
	query := &submitQueryStub{
		onQuery: func(ctx context.Context, objectName, filter string, user *models.UserSession, sortField, sortDir string, limit int) (*models.QueryResult, error) {
			if objectName == constants.TableProcessDefinition && strings.Contains(filter, "DeveloperName ==") {
				return &models.QueryResult{}, nil
			}
			return &models.QueryResult{TotalSize: len(records[objectName]), Records: records[objectName]}, nil
		},
	}
	svc := automation.NewApprovalService(
		persistence,
		query,
		permissions.NewPermissionService(&clonePermissionRepo{}, nil, nil),
		nil,
		nil,
		nil,
		nil,
		logging.Default,
	)

	_, err = svc.Clone(context.Background(), "pd-source", "Cloned_Process", "Cloned Process", submitTestUser())
	if err != nil {
		t.Fatalf("Clone() err = %v", err)
	}
	if len(persistence.inserts) != 3 {
		t.Fatalf("Insert calls = %d, want ProcessDefinition + live ProcessNode + live ProcessAction", len(persistence.inserts))
	}

	processInsert, nodeInsert, actionInsert := persistence.inserts[0], persistence.inserts[1], persistence.inserts[2]
	if got := processInsert.record[constants.FieldProcessDefinition_OwnerPack]; got != "" {
		t.Fatalf("cloned ProcessDefinition OwnerPack = %#v, want empty owner", got)
	}
	if got := processInsert.record[constants.FieldProcessDefinition_Metadata]; got != sourceMetadata {
		t.Fatalf("cloned ProcessDefinition Metadata = %#v, want raw source metadata preserved", got)
	}
	if got := nodeInsert.record["DeveloperName"]; got != "Live_Child_Step" {
		t.Fatalf("cloned ProcessNode DeveloperName = %#v, want live child projection", got)
	}
	clonedNodeID, _ := nodeInsert.record[constants.FieldID].(string)
	if clonedNodeID == "" || clonedNodeID == "pn-live" {
		t.Fatalf("cloned ProcessNode ID = %q, want a fresh ID", clonedNodeID)
	}
	if got := actionInsert.record[constants.FieldProcessAction_Name]; got != "Live Child Action" {
		t.Fatalf("cloned ProcessAction Name = %#v, want live child projection", got)
	}
	if got := actionInsert.record["ProcessNodeId"]; got != clonedNodeID {
		t.Fatalf("cloned ProcessAction ProcessNodeId = %#v, want remapped live child ID %q", got, clonedNodeID)
	}
}
