package triggerprojection

import (
	"errors"
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
)

func TestResolveWorkflowNilStartFromCurrentVariable(t *testing.T) {
	def := &models.FlowDefinition{ProcessType: "Workflow"}
	version := &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Variables: []models.FlowVariable{
				{Name: "myVariable_old", DataType: "SObject", ObjectType: "IgnoredObject"},
				{Name: "myVariable_current", DataType: "SObject", ObjectType: "ProbeObject"},
			},
		},
	}

	got, err := Resolve(def, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindRecord)
	}
	if got.TriggerType != "RecordAfterSave" {
		t.Fatalf("TriggerType = %q, want RecordAfterSave", got.TriggerType)
	}
	if got.ObjectOrEvent != "ProbeObject" {
		t.Fatalf("ObjectOrEvent = %q, want ProbeObject", got.ObjectOrEvent)
	}
}

func TestResolveWorkflowEmptyStartFromCurrentVariable(t *testing.T) {
	def := &models.FlowDefinition{ProcessType: "Workflow"}
	version := &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{},
			Variables: []models.FlowVariable{
				{Name: "myVariable_current", DataType: "SObject", ObjectType: "ProbeObject"},
			},
		},
	}

	got, err := Resolve(def, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindRecord)
	}
	if got.TriggerType != "RecordAfterSave" {
		t.Fatalf("TriggerType = %q, want RecordAfterSave", got.TriggerType)
	}
	if got.ObjectOrEvent != "ProbeObject" {
		t.Fatalf("ObjectOrEvent = %q, want ProbeObject", got.ObjectOrEvent)
	}
}

func TestResolveCanonicalizesStartRecordTriggerCasing(t *testing.T) {
	version := &models.FlowVersion{
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "recordbeforesave",
				Object:      "ProbeObject",
			},
		},
	}

	got, err := Resolve(&models.FlowDefinition{}, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindRecord)
	}
	if got.TriggerType != "RecordBeforeSave" {
		t.Fatalf("TriggerType = %q, want RecordBeforeSave", got.TriggerType)
	}
	if got.ObjectOrEvent != "ProbeObject" {
		t.Fatalf("ObjectOrEvent = %q, want ProbeObject", got.ObjectOrEvent)
	}
}

func TestResolveExplicitAutoLaunchedClearsProjection(t *testing.T) {
	version := &models.FlowVersion{
		ProcessType: "AutoLaunchedFlow",
		Metadata:    models.FlowMetadata{},
	}

	got, err := Resolve(&models.FlowDefinition{ProcessType: "AutoLaunchedFlow"}, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindExplicitNonRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindExplicitNonRecord)
	}
	if got.TriggerType != "" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want cleared", got.TriggerType, got.ObjectOrEvent)
	}
}

func TestResolveExplicitAutoLaunchedWithEmptyStartClearsProjection(t *testing.T) {
	version := &models.FlowVersion{
		ProcessType: "AutoLaunchedFlow",
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{},
		},
	}

	got, err := Resolve(&models.FlowDefinition{ProcessType: "AutoLaunchedFlow"}, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindExplicitNonRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindExplicitNonRecord)
	}
	if got.TriggerType != "" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want cleared", got.TriggerType, got.ObjectOrEvent)
	}
}

func TestResolveExplicitScheduledClearsRecordObject(t *testing.T) {
	version := &models.FlowVersion{
		ProcessType: "AutoLaunchedFlow",
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "scheduled",
			},
		},
	}

	got, err := Resolve(&models.FlowDefinition{ProcessType: "AutoLaunchedFlow"}, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindExplicitNonRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindExplicitNonRecord)
	}
	if got.TriggerType != "Scheduled" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want (Scheduled, empty)", got.TriggerType, got.ObjectOrEvent)
	}
}

func TestResolveInvocableProcessNilStartClearsProjection(t *testing.T) {
	got, err := Resolve(&models.FlowDefinition{ProcessType: "InvocableProcess"}, &models.FlowVersion{
		ProcessType: "InvocableProcess",
		Metadata:    models.FlowMetadata{},
	})
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindExplicitNonRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindExplicitNonRecord)
	}
	if got.TriggerType != "" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want cleared", got.TriggerType, got.ObjectOrEvent)
	}
}

func TestResolveScreenFlowNilStartClearsProjection(t *testing.T) {
	got, err := Resolve(&models.FlowDefinition{ProcessType: "Flow"}, &models.FlowVersion{
		ProcessType: "Flow",
		Metadata:    models.FlowMetadata{},
	})
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindExplicitNonRecord {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindExplicitNonRecord)
	}
	if got.TriggerType != "" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want cleared", got.TriggerType, got.ObjectOrEvent)
	}
}

func TestResolveUnknownDoesNotClearProjection(t *testing.T) {
	got, err := Resolve(&models.FlowDefinition{ProcessType: "Workflow"}, &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Variables: []models.FlowVariable{
				{Name: "myVariable_current", DataType: "String", ObjectType: "ProbeObject"},
			},
		},
	})
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}

	if got.Kind != KindUnknown {
		t.Fatalf("Kind = %v, want %v", got.Kind, KindUnknown)
	}
	if got.TriggerType != "" || got.ObjectOrEvent != "" {
		t.Fatalf("projection = (%q, %q), want empty unknown", got.TriggerType, got.ObjectOrEvent)
	}
}

// --------------------------------------------------------------------------
// Fact 3 — onAllChanges alias (issue #571)
// --------------------------------------------------------------------------

// TestResolve_WorkflowOnAllChanges_WithObject_ProjectsAfterSave verifies that
// a Workflow with start.triggerType="onAllChanges" and start.object="Child__c"
// resolves to (KindRecord, RecordAfterSave, Child__c).
func TestResolve_WorkflowOnAllChanges_WithObject_ProjectsAfterSave(t *testing.T) {
	def := &models.FlowDefinition{ProcessType: "Workflow"}
	version := &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "onAllChanges",
				Object:      "Child__c",
			},
		},
	}

	got, err := Resolve(def, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}
	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want KindRecord", got.Kind)
	}
	if got.TriggerType != "RecordAfterSave" {
		t.Fatalf("TriggerType = %q, want RecordAfterSave", got.TriggerType)
	}
	if got.ObjectOrEvent != "Child__c" {
		t.Fatalf("ObjectOrEvent = %q, want Child__c", got.ObjectOrEvent)
	}
}

// TestResolve_OnAllChangesWithoutWorkflow_StillAfterSave verifies that the
// onAllChanges alias is processType-agnostic: AutoLaunchedFlow with
// onAllChanges also resolves to RecordAfterSave.
func TestResolve_OnAllChangesWithoutWorkflow_StillAfterSave(t *testing.T) {
	def := &models.FlowDefinition{ProcessType: "AutoLaunchedFlow"}
	version := &models.FlowVersion{
		ProcessType: "AutoLaunchedFlow",
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "onAllChanges",
				Object:      "SomeObject__c",
			},
		},
	}

	got, err := Resolve(def, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}
	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want KindRecord", got.Kind)
	}
	if got.TriggerType != "RecordAfterSave" {
		t.Fatalf("TriggerType = %q, want RecordAfterSave", got.TriggerType)
	}
}

// TestIsRecordTriggerType_OnAllChanges verifies IsRecordTriggerType returns
// true for "onAllChanges".
func TestIsRecordTriggerType_OnAllChanges(t *testing.T) {
	if !IsRecordTriggerType("onAllChanges") {
		t.Fatal("IsRecordTriggerType(\"onAllChanges\") = false, want true")
	}
}

// TestResolve_WorkflowVariablesOnly_NoRegression verifies that a Workflow
// with no Start.Object (variables-only path) still resolves via
// resolveWorkflowProjection.
func TestResolve_WorkflowVariablesOnly_NoRegression(t *testing.T) {
	def := &models.FlowDefinition{ProcessType: "Workflow"}
	version := &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Variables: []models.FlowVariable{
				{Name: "myVariable_old", DataType: "SObject", ObjectType: "IgnoredObject"},
				{Name: "myVariable_current", DataType: "SObject", ObjectType: "ProbeObject"},
			},
		},
	}

	got, err := Resolve(def, version)
	if err != nil {
		t.Fatalf("Resolve() error = %v", err)
	}
	if got.Kind != KindRecord {
		t.Fatalf("Kind = %v, want KindRecord", got.Kind)
	}
	if got.TriggerType != "RecordAfterSave" {
		t.Fatalf("TriggerType = %q, want RecordAfterSave", got.TriggerType)
	}
	if got.ObjectOrEvent != "ProbeObject" {
		t.Fatalf("ObjectOrEvent = %q, want ProbeObject", got.ObjectOrEvent)
	}
}

func TestResolveMalformedRecordStartRequiresObject(t *testing.T) {
	_, err := Resolve(&models.FlowDefinition{}, &models.FlowVersion{
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "RecordAfterSave",
			},
		},
	})

	if !errors.Is(err, ErrMalformed) {
		t.Fatalf("Resolve() error = %v, want ErrMalformed", err)
	}
}

func TestResolveMalformedStartObjectRequiresTriggerType(t *testing.T) {
	_, err := Resolve(&models.FlowDefinition{}, &models.FlowVersion{
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				Object: "ProbeObject",
			},
		},
	})

	if !errors.Is(err, ErrMalformed) {
		t.Fatalf("Resolve() error = %v, want ErrMalformed", err)
	}
}

func TestResolveMalformedStartObjectRequiresKnownTriggerType(t *testing.T) {
	_, err := Resolve(&models.FlowDefinition{}, &models.FlowVersion{
		Metadata: models.FlowMetadata{
			Start: &models.FlowStart{
				TriggerType: "NotAFlowTrigger",
				Object:      "ProbeObject",
			},
		},
	})

	if !errors.Is(err, ErrMalformed) {
		t.Fatalf("Resolve() error = %v, want ErrMalformed", err)
	}
}

func TestResolveMalformedWorkflowCurrentVariableRequiresObjectType(t *testing.T) {
	_, err := Resolve(&models.FlowDefinition{ProcessType: "Workflow"}, &models.FlowVersion{
		ProcessType: "Workflow",
		Metadata: models.FlowMetadata{
			Variables: []models.FlowVariable{
				{Name: "myVariable_current", DataType: "SObject"},
			},
		},
	})

	if !errors.Is(err, ErrMalformed) {
		t.Fatalf("Resolve() error = %v, want ErrMalformed", err)
	}
}

func TestResolveMalformedRawStartShapeFailsClosed(t *testing.T) {
	_, err := Resolve(&models.FlowDefinition{ProcessType: "AutoLaunchedFlow"}, &models.FlowVersion{
		ProcessType: "AutoLaunchedFlow",
		MetadataRaw: []byte(`{"start":"not an object"}`),
	})

	if !errors.Is(err, ErrMalformed) {
		t.Fatalf("Resolve() error = %v, want ErrMalformed", err)
	}
}
