package plan

import (
	"errors"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
	planlang "github.com/tidbcloud/AgentTeam/internal/workflow/plan"
)

func TestFrozenDraftCommandFenceSeparatesInputAndSnapshotRevisions(t *testing.T) {
	actor := Actor{Type: ActorTypeAgent, ID: "agent-1", MemberID: "member-1"}
	snapshotRevision := int64(4)
	commandRevision := int64(3)
	request, err := normalizePublishRequest(publishVersionRequest{
		WorkspaceID:          "workspace-1",
		PlanID:               "plan-1",
		ExpectedPlanRevision: 7,
		DraftRevision:        &snapshotRevision,
		CommandDraftRevision: &commandRevision,
		CompilerVersion:      "compiler-1", Digest: "digest-1",
		Actor: actor, IdempotencyKey: "publish-1",
		CommandRequestDigest: "team-command-digest",
	})
	require.NoError(t, err)
	require.Equal(t, int64(4), *request.DraftRevision)
	require.Equal(t, int64(3), *request.CommandDraftRevision)
	require.Equal(t, "team-command-digest", request.RequestDigest)

	ordinary, err := normalizePublishRequest(publishVersionRequest{
		WorkspaceID: "workspace-1", PlanID: "plan-1", ExpectedPlanRevision: 7,
		DraftRevision:   &snapshotRevision,
		CompilerVersion: "compiler-1", Digest: "digest-1",
		Actor: actor, IdempotencyKey: "publish-1",
	})
	require.NoError(t, err)
	require.NotEqual(t, request.RequestDigest, ordinary.RequestDigest)
}

func TestVersionSummarySelectOmitsImmutablePayloads(t *testing.T) {
	projection, _, found := strings.Cut(versionSummarySelectSQL(), "FROM workflow_plan_versions")
	require.True(t, found)

	columns := strings.Split(strings.TrimPrefix(strings.TrimSpace(projection), "SELECT "), ",")
	selected := make(map[string]struct{}, len(columns))
	for _, column := range columns {
		selected[strings.TrimSpace(column)] = struct{}{}
	}

	require.NotContains(t, selected, "source")
	require.NotContains(t, selected, "layout")
	require.NotContains(t, selected, "artifact")
	require.Contains(t, selected, "v.id")
	require.NotContains(t, selected, "version_number")
	require.Contains(t, selected, "v.digest")
	require.Contains(t, selected, "v.published_at")
}

func TestValidateVersionArtifactForBindingChecksEnvelopeCompilerAndDigest(t *testing.T) {
	artifact, diagnostics, err := planlang.Compile([]byte(bindingVersionPlanSource))
	require.NoError(t, err)
	require.False(t, planlang.HasErrors(diagnostics), diagnostics)
	payload, err := artifact.DeterministicJSON()
	require.NoError(t, err)
	version := PlanVersion{Artifact: payload, CompilerVersion: artifact.CompilerVersion, Digest: artifact.Digest}
	decoded, err := validateVersionArtifactForBinding(version)
	require.NoError(t, err)
	require.Equal(t, artifact.Digest, decoded.Digest)

	version.Digest = "sha256:wrong"
	_, err = validateVersionArtifactForBinding(version)
	require.Error(t, err)
	require.True(t, errors.Is(err, ErrInvalidVersionArtifact))

	version.Digest = artifact.Digest
	version.CompilerVersion = "other/compiler"
	_, err = validateVersionArtifactForBinding(version)
	require.Error(t, err)
	require.True(t, errors.Is(err, ErrInvalidVersionArtifact))

	version.CompilerVersion = artifact.CompilerVersion
	version.Artifact = []byte(`{"artifactVersion":"wrong"}`)
	_, err = validateVersionArtifactForBinding(version)
	require.Error(t, err)
	require.True(t, errors.Is(err, ErrInvalidVersionArtifact))
}

const bindingVersionPlanSource = `apiVersion: loop.plan/v1
kind: PlanVersion
graph:
  entry: {nodeId: pause, port: in}
  nodes:
  - id: pause
    type: wait
    wait:
      until: {value: PT1M}
      timeout: PT2M
      retry: {maxAttempts: 1, backoff: 1s}
  - id: done
    type: end
    end: {outcome: succeeded}
  - id: failed
    type: end
    end: {outcome: failed, code: wait_failed}
  edges:
  - id: completed
    source: {nodeId: pause, port: completed}
    target: {nodeId: done, port: in}
  - id: timed_out
    source: {nodeId: pause, port: timed_out}
    target: {nodeId: failed, port: in}
  - id: action_failed
    source: {nodeId: pause, port: failed}
    target: {nodeId: failed, port: in}`
