package workflows

import (
	"testing"

	portainer "github.com/portainer/portainer/api"
	"github.com/stretchr/testify/assert"
)

func TestEffectiveStatus(t *testing.T) {
	t.Parallel()

	makeWorkflow := func(source, artifact, target Status) Workflow {
		return Workflow{
			Status: WorkflowStatusObject{
				Source:   WorkflowPhaseStatus{Status: source},
				Artifact: WorkflowPhaseStatus{Status: artifact},
				Target:   WorkflowPhaseStatus{Status: target},
			},
		}
	}

	cases := []struct {
		name string
		w    Workflow
		want Status
	}{
		{"all healthy", makeWorkflow(StatusHealthy, StatusHealthy, StatusHealthy), StatusHealthy},
		{"all unknown", makeWorkflow(StatusUnknown, StatusUnknown, StatusUnknown), StatusUnknown},
		{"source error wins over syncing target", makeWorkflow(StatusError, StatusSyncing, StatusHealthy), StatusError},
		{"artifact error wins over syncing target", makeWorkflow(StatusHealthy, StatusError, StatusSyncing), StatusError},
		{"target error wins over healthy phases", makeWorkflow(StatusHealthy, StatusHealthy, StatusError), StatusError},
		{"syncing beats paused and healthy", makeWorkflow(StatusPaused, StatusSyncing, StatusHealthy), StatusSyncing},
		{"paused beats healthy", makeWorkflow(StatusHealthy, StatusPaused, StatusHealthy), StatusPaused},
		{"healthy beats unknown", makeWorkflow(StatusUnknown, StatusHealthy, StatusUnknown), StatusHealthy},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			assert.Equal(t, tc.want, EffectiveStatus(tc.w))
		})
	}
}

func TestCountByStatus(t *testing.T) {
	t.Parallel()

	makeW := func(s Status) Workflow {
		return Workflow{
			Status: WorkflowStatusObject{
				Source:   WorkflowPhaseStatus{Status: s},
				Artifact: WorkflowPhaseStatus{Status: s},
				Target:   WorkflowPhaseStatus{Status: s},
			},
		}
	}

	t.Run("empty list", func(t *testing.T) {
		t.Parallel()
		assert.Equal(t, StatusSummary{}, CountByStatus(nil))
	})

	t.Run("single healthy", func(t *testing.T) {
		t.Parallel()
		assert.Equal(t, StatusSummary{Healthy: 1}, CountByStatus([]Workflow{makeW(StatusHealthy)}))
	})

	t.Run("mixed statuses", func(t *testing.T) {
		t.Parallel()
		workflows := []Workflow{
			makeW(StatusHealthy),
			makeW(StatusError),
			makeW(StatusSyncing),
			makeW(StatusPaused),
			makeW(StatusUnknown),
			makeW(StatusError),
		}
		assert.Equal(t, StatusSummary{Healthy: 1, Error: 2, Syncing: 1, Paused: 1, Unknown: 1}, CountByStatus(workflows))
	})

	t.Run("error phase overrides healthy target", func(t *testing.T) {
		t.Parallel()
		w := Workflow{
			Status: WorkflowStatusObject{
				Source:   WorkflowPhaseStatus{Status: StatusError},
				Artifact: WorkflowPhaseStatus{Status: StatusUnknown},
				Target:   WorkflowPhaseStatus{Status: StatusHealthy},
			},
		}
		s := CountByStatus([]Workflow{w})
		assert.Equal(t, 1, s.Error)
		assert.Equal(t, 0, s.Healthy)
	})
}

func TestDeriveEdgeStackTargetState(t *testing.T) {
	t.Parallel()

	ep := func(id portainer.EndpointID, typ portainer.EdgeStackStatusType) portainer.EdgeStackStatusForEnv {
		return portainer.EdgeStackStatusForEnv{
			EndpointID: id,
			Status:     []portainer.EdgeStackDeploymentStatus{{Type: typ}},
		}
	}

	cases := []struct {
		name     string
		statuses []portainer.EdgeStackStatusForEnv
		want     Status
	}{
		{"empty", nil, StatusUnknown},
		{"all per-env status slices empty", []portainer.EdgeStackStatusForEnv{{EndpointID: 1}}, StatusUnknown},
		{"running: healthy", []portainer.EdgeStackStatusForEnv{ep(1, portainer.EdgeStackStatusRunning)}, StatusHealthy},
		{"deploying: syncing", []portainer.EdgeStackStatusForEnv{ep(1, portainer.EdgeStackStatusDeploying)}, StatusSyncing},
		{"paused deploying: paused", []portainer.EdgeStackStatusForEnv{ep(1, portainer.EdgeStackStatusPausedDeploying)}, StatusPaused},
		{"error short-circuits", []portainer.EdgeStackStatusForEnv{ep(1, portainer.EdgeStackStatusError)}, StatusError},
		{
			"error + running gives error (short-circuit, order matters)",
			[]portainer.EdgeStackStatusForEnv{
				ep(1, portainer.EdgeStackStatusError),
				ep(2, portainer.EdgeStackStatusRunning),
			},
			StatusError,
		},
		{
			"syncing beats paused",
			[]portainer.EdgeStackStatusForEnv{
				ep(1, portainer.EdgeStackStatusPausedDeploying),
				ep(2, portainer.EdgeStackStatusDeploying),
			},
			StatusSyncing,
		},
		{
			"healthy does not downgrade syncing",
			[]portainer.EdgeStackStatusForEnv{
				ep(1, portainer.EdgeStackStatusDeploying),
				ep(2, portainer.EdgeStackStatusRunning),
			},
			StatusSyncing,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			result := deriveEdgeStackTargetState(tc.statuses)
			assert.Equal(t, tc.want, result.Status)
		})
	}
}
