package automation

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	automationstore "github.com/tidbcloud/AgentTeam/internal/store/automation"
)

func TestStartRepositoryAutomationRunRequiresSecretUsePermissionForSecretEnv(t *testing.T) {
	ctx := context.Background()
	service, store, _ := newAutomationRunSecretTestService(t, automationWithSecretEnvForTest())
	authorizer := &recordingAutomationSecretAuthorizer{denyUse: true}
	service.SetAuthorizer(authorizer)

	_, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.ErrorIs(t, err, ErrForbidden)
	require.Equal(t, []string{"pingcap/agentteam"}, authorizer.useRepos)

	runs, err := store.ListRuns(ctx, "ws-1", "", "", "", 1, 10)
	require.NoError(t, err)
	require.Empty(t, runs.Runs)
}

func TestStartRepositoryAutomationRunRejectsMissingSecretEnvReference(t *testing.T) {
	ctx := context.Background()
	service, store, _ := newAutomationRunSecretTestService(t, automationWithSecretEnvForTest())
	authorizer := &recordingAutomationSecretAuthorizer{}
	service.SetAuthorizer(authorizer)

	_, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.ErrorIs(t, err, ErrInvalidArgument)
	require.Contains(t, err.Error(), "missing secret")
	require.Contains(t, err.Error(), "github_publish_token")
	require.Equal(t, []string{"pingcap/agentteam"}, authorizer.useRepos)

	runs, err := store.ListRuns(ctx, "ws-1", "", "", "", 1, 10)
	require.NoError(t, err)
	require.Empty(t, runs.Runs)
}

func TestStartRepositoryAutomationRunAllowsExistingSecretEnvReferenceWithoutPersistingSecretMaterial(t *testing.T) {
	ctx := context.Background()
	service, store, now := newAutomationRunSecretTestService(t, automationWithSecretEnvForTest())
	authorizer := &recordingAutomationSecretAuthorizer{}
	service.SetAuthorizer(authorizer)
	const plaintext = "plain-secret-token"
	const ciphertext = "ciphertext-token"
	for _, name := range []string{"global_token", "github_publish_token", "deploy_slack_token"} {
		_, err := store.UpsertAutomationSecret(ctx, automationstore.AutomationSecret{
			WorkspaceID:     "ws-1",
			RepositoryOwner: "pingcap",
			RepositoryName:  "agentteam",
			Name:            name,
			ValueCiphertext: ciphertext,
			CreatedBy:       "admin-1",
			UpdatedBy:       "admin-1",
			CreatedAt:       now.Add(-time.Hour),
			UpdatedAt:       now.Add(-time.Hour),
		})
		require.NoError(t, err)
	}

	run, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.NoError(t, err)
	require.Equal(t, []string{"pingcap/agentteam"}, authorizer.useRepos)

	persisted, err := store.GetRun(ctx, "ws-1", run.ID)
	require.NoError(t, err)
	assertAutomationSecretMaterialAbsent(t, []string{persisted.InputJSON, persisted.OutputJSON, persisted.ErrorJSON, persisted.Metadata}, plaintext, ciphertext)

	jobRuns, err := store.ListJobRuns(ctx, "ws-1", run.ID)
	require.NoError(t, err)
	require.Len(t, jobRuns, 1)
	require.Contains(t, jobRuns[0].InputJSON, "${{ secrets.github_publish_token }}")
	assertAutomationSecretMaterialAbsent(t, []string{jobRuns[0].InputJSON, jobRuns[0].OutputJSON, jobRuns[0].ErrorJSON, jobRuns[0].RunsOnJSON, jobRuns[0].DependsOnJSON}, plaintext, ciphertext)

	events, err := store.ListEvents(ctx, "ws-1", run.ID, 1, 20)
	require.NoError(t, err)
	for _, event := range events {
		assertAutomationSecretMaterialAbsent(t, []string{event.Summary, event.MetadataJSON, event.ResourceID}, plaintext, ciphertext)
	}
}

func TestStartRepositoryAutomationRunDoesNotRequireSecretUsePermissionForInputEnv(t *testing.T) {
	ctx := context.Background()
	service, store, _ := newAutomationRunSecretTestService(t, "name: Input env\non:\n  manual: {}\nenv:\n  TOKEN: ${{ inputs.token }}\njobs:\n  build:\n    steps:\n      - run: echo hi\n")
	authorizer := &recordingAutomationSecretAuthorizer{denyUse: true}
	service.SetAuthorizer(authorizer)

	run, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.NoError(t, err)
	require.Empty(t, authorizer.useRepos)

	runs, err := store.ListRuns(ctx, "ws-1", "", "", "", 1, 10)
	require.NoError(t, err)
	require.Len(t, runs.Runs, 1)
	require.Equal(t, run.ID, runs.Runs[0].ID)
}

func TestStartRepositoryAutomationRunRequiresSecretUseForInheritedAutomationEnv(t *testing.T) {
	ctx := context.Background()
	service, _, _ := newAutomationRunSecretTestService(t, "name: Inherited secret env\non:\n  manual: {}\nenv:\n  TOKEN: ${{ secrets.inherited_token }}\njobs:\n  build:\n    steps:\n      - run: echo hi\n")
	authorizer := &recordingAutomationSecretAuthorizer{denyUse: true}
	service.SetAuthorizer(authorizer)

	_, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.ErrorIs(t, err, ErrForbidden)
	require.Equal(t, []string{"pingcap/agentteam"}, authorizer.useRepos)
}

func TestStartRepositoryAutomationRunIgnoresAutomationSecretEnvOverriddenByEveryJob(t *testing.T) {
	ctx := context.Background()
	service, store, _ := newAutomationRunSecretTestService(t, "name: Overridden secret env\non:\n  manual: {}\nenv:\n  TOKEN: ${{ secrets.overridden_token }}\njobs:\n  build:\n    env:\n      TOKEN: literal\n    steps:\n      - run: echo hi\n  test:\n    env:\n      TOKEN: ${{ inputs.token }}\n    steps:\n      - run: echo hi\n")
	authorizer := &recordingAutomationSecretAuthorizer{denyUse: true}
	service.SetAuthorizer(authorizer)

	run, err := service.StartRepositoryAutomationRun(ctx, "ws-1", "user-1", AutomationRepositoryRunMutation{RepositoryOwner: "pingcap", RepositoryName: "agentteam", DefinitionPath: ".agentteam/build.yml", GitRef: "main"})
	require.NoError(t, err)
	require.Empty(t, authorizer.useRepos)

	runs, err := store.ListRuns(ctx, "ws-1", "", "", "", 1, 10)
	require.NoError(t, err)
	require.Len(t, runs.Runs, 1)
	require.Equal(t, run.ID, runs.Runs[0].ID)
}

func newAutomationRunSecretTestService(t *testing.T, content string) (*AutomationsService, *automationstore.Store, time.Time) {
	t.Helper()
	db, err := openServiceTestSQLiteDB(t)
	require.NoError(t, err)
	migrateAutomationServiceTestSchema(t, db, &automationstore.AutomationSecret{})
	store := automationstore.New(db)
	now := time.Date(2026, 6, 5, 10, 47, 0, 0, time.UTC)
	service := NewAutomationsService(store, func() time.Time { return now }, staticAutomationSourceResolver{content: content})
	return service, store, now
}

func automationWithSecretEnvForTest() string {
	return "name: Secret env\non:\n  manual: {}\nenv:\n  NODE_ENV: production\n  GLOBAL_TOKEN: ${{ secrets.global_token }}\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    env:\n      GH_TOKEN: ${{ secrets.github_publish_token }}\n    steps:\n      - run: ./deploy.sh\n        env:\n          SLACK_TOKEN: ${{ secrets.deploy_slack_token }}\n"
}

func assertAutomationSecretMaterialAbsent(t *testing.T, values []string, forbidden ...string) {
	t.Helper()
	for _, value := range values {
		for _, token := range forbidden {
			require.NotContains(t, value, token)
		}
	}
}
