package orchestrator

import (
	"context"
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/store/workflow/invocation"
	"github.com/tidbcloud/AgentTeam/internal/store/workflow/plan"
	"github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	invocationlang "github.com/tidbcloud/AgentTeam/internal/workflow/invocation"
)

type pinnedPlanStoreStub struct {
	value plan.PlanVersion
	err   error
}

func (stub pinnedPlanStoreStub) LoadVersionByID(context.Context, string, string, string) (plan.PlanVersion, error) {
	return stub.value, stub.err
}

type pinnedInvocationStoreStub struct {
	value invocation.Invocation
	err   error
}

func (stub pinnedInvocationStoreStub) LoadInvocation(context.Context, string, string) (invocation.Invocation, error) {
	return stub.value, stub.err
}

func TestPinnedMaterialLoaderUsesExactVersionAndInvocationOnly(t *testing.T) {
	run := run.Run{
		ID: "wri_1", WorkspaceID: "wsp_1", InvocationID: "wiv_1",
		PlanID: "wfp_1", PlanVersionID: "wfv_7", InputDigest: "sha256:input",
		WorkflowNameSnapshot: "成语接龙",
		Identity: invocationlang.ExecutionIdentitySnapshot{
			OwnerUserID: "usr_owner", OwnerMemberID: "mem_owner", PrincipalMemberID: "mem_principal", AuthorMemberID: "mem_author",
		},
	}
	version := plan.PlanVersion{
		ID: "wfv_7", WorkspaceID: "wsp_1", PlanID: "wfp_1", CompilerVersion: "loop.plan.compiler/v1",
		Digest: "sha256:artifact", Artifact: []byte(`{"artifactVersion":"loop.plan.artifact/v1"}`),
	}
	invocation := invocation.Invocation{
		ID: "wiv_1", RunID: "wri_1", WorkspaceID: "wsp_1",
		PlanID: "wfp_1", PlanVersionID: "wfv_7", SourceKind: invocationlang.SourceKindManual,
		WorkflowNameSnapshot: "成语接龙", InputDigest: "sha256:input", Identity: run.Identity,
	}
	loader, err := NewPinnedMaterialLoader(pinnedPlanStoreStub{value: version}, pinnedInvocationStoreStub{value: invocation})
	require.NoError(t, err)
	material, err := loader.LoadRunMaterial(context.Background(), run)
	require.NoError(t, err)
	require.Equal(t, "wfv_7", material.PlanVersionID)
	require.Equal(t, invocationlang.SourceKindManual, material.SourceKind)
	require.Equal(t, version.Artifact, []byte(material.ArtifactJSON))

	invocation.WorkflowNameSnapshot = "renamed"
	loader, err = NewPinnedMaterialLoader(pinnedPlanStoreStub{value: version}, pinnedInvocationStoreStub{value: invocation})
	require.NoError(t, err)
	_, err = loader.LoadRunMaterial(context.Background(), run)
	require.Error(t, err)
	require.True(t, IsPermanentMaterialError(err))
	invocation.WorkflowNameSnapshot = run.WorkflowNameSnapshot

	invocation.PlanVersionID = "wfv_current"
	loader, err = NewPinnedMaterialLoader(pinnedPlanStoreStub{value: version}, pinnedInvocationStoreStub{value: invocation})
	require.NoError(t, err)
	_, err = loader.LoadRunMaterial(context.Background(), run)
	require.Error(t, err)
	require.True(t, IsPermanentMaterialError(err))
	require.Contains(t, err.Error(), "immutable Invocation snapshot does not match")
}

func TestPinnedMaterialLoaderClassifiesOnlyDeterministicReadFailures(t *testing.T) {
	run := run.Run{WorkspaceID: "wsp_1", PlanID: "wfp_1", PlanVersionID: "wfv_1", InvocationID: "wiv_1"}
	transient := fmt.Errorf("database timeout")
	loader, err := NewPinnedMaterialLoader(pinnedPlanStoreStub{err: transient}, pinnedInvocationStoreStub{})
	require.NoError(t, err)
	_, err = loader.LoadRunMaterial(context.Background(), run)
	require.ErrorIs(t, err, transient)
	require.False(t, IsPermanentMaterialError(err))

	missing := storeerr.NotFound("workflow PlanVersion not found")
	loader, err = NewPinnedMaterialLoader(pinnedPlanStoreStub{err: missing}, pinnedInvocationStoreStub{})
	require.NoError(t, err)
	_, err = loader.LoadRunMaterial(context.Background(), run)
	require.True(t, IsPermanentMaterialError(err))
}
