package plan

import (
	"reflect"
	"strings"
	"testing"
)

func TestParallelBranchFailedEndIsScopedAndLowered(t *testing.T) {
	raw := forkJoinWithBranchTerminal()
	artifact, diagnostics, err := Compile([]byte(raw))
	if err != nil || HasErrors(diagnostics) {
		t.Fatalf("Compile() = (%v, %#v)", err, diagnostics)
	}
	if len(artifact.Scopes) != 1 || artifact.Scopes[0].Parallel == nil {
		t.Fatalf("scopes = %#v", artifact.Scopes)
	}
	root := artifact.Scopes[0]
	scope := root.Parallel
	if root.OwnerID != "fanout" || scope.JoinID != "gather" || scope.Mode != JoinAll {
		t.Fatalf("scope identity = %#v", scope)
	}
	var left *ArtifactParallelBranch
	for i := range scope.Branches {
		if scope.Branches[i].Port == "left" {
			left = &scope.Branches[i]
		}
	}
	if left == nil || !reflect.DeepEqual(left.Members, []string{"left_route"}) ||
		!reflect.DeepEqual(left.SuccessArrivals, []string{"left_success"}) ||
		!reflect.DeepEqual(left.FailedTerminals, []string{"branch_failure"}) {
		t.Fatalf("left branch analysis = %#v", left)
	}
}

func TestParallelBranchSucceededEndBeforeJoinIsRejected(t *testing.T) {
	raw := strings.Replace(forkJoinWithBranchTerminal(),
		"end: {outcome: failed, code: branch_failed}",
		"end: {outcome: succeeded}", 1)
	_, diagnostics, err := Compile([]byte(raw))
	if err != nil {
		t.Fatalf("Compile() error = %v", err)
	}
	if !hasCode(diagnostics, "parallel.open_branch") {
		t.Fatalf("diagnostics = %#v, want parallel.open_branch", diagnostics)
	}
}

func forkJoinWithBranchTerminal() string {
	raw := strings.Replace(validForkJoinPlan, "  - id: gather", `  - id: left_route
    type: switch
    input:
      value: {type: string, required: true}
    switch:
      mode: first_match
      cases:
      - port: success
        when: 'input.value == "ok"'
      - port: failure
        isDefault: true
  - id: branch_failure
    type: end
    end: {outcome: failed, code: branch_failed}
  - id: gather`, 1)
	return strings.Replace(raw, `  - id: left
    source: {nodeId: fanout, port: left}
    target: {nodeId: gather, port: left}
    bindings:
      value: {ref: input.value}`, `  - id: left_start
    source: {nodeId: fanout, port: left}
    target: {nodeId: left_route, port: in}
    bindings:
      value: {ref: input.value}
  - id: left_success
    source: {nodeId: left_route, port: success}
    target: {nodeId: gather, port: left}
    bindings:
      value: {ref: input.value}
  - id: left_failure
    source: {nodeId: left_route, port: failure}
    target: {nodeId: branch_failure, port: in}`, 1)
}
