package app

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/secrets"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
)

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

	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	now := time.Unix(100, 0).UTC()
	fixture := seedAppAdminFixture(t, pool, "agent_permission_mode", now)
	seedMachineForRuntimeList(t, pool, fixture.WorkspaceID, fixture.UserID, "mac_agent_permission", now)

	store := New(pool, Options{})
	createdDefault, err := store.CreateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, "mac_agent_permission", agent.CreateAgentParams{
		Name:        "perm-default",
		DisplayName: "Permission Default",
		Runtime:     "codex",
		Model:       "gpt-5.4",
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, createdDefault.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdDefault.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdDefault.AgentID, 1)
	requireStoredAgentVisibleReplyContract(t, pool, createdDefault.AgentID, true)

	createdFullAccess, err := store.CreateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, "mac_agent_permission", agent.CreateAgentParams{
		Name:        "perm-full",
		DisplayName: "Permission Full",
		Runtime:     "codex",
		Model:       "gpt-5.4",
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, createdFullAccess.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 1)

	oldFullAccessToken := "full-access-token"
	_, err = pool.Exec(ctx, `
UPDATE agents
SET session_id = 'sess_restore_permission',
    auth_token_hash = $2
WHERE id = $1
`, createdFullAccess.AgentID, secrets.HashToken(oldFullAccessToken))
	require.NoError(t, err)
	workspacePermissionMode := agent.AgentPermissionModeWorkspaceWrite
	_, err = store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.UpdateAgentParams{
		PermissionMode: &workspacePermissionMode,
	})
	require.NoError(t, err)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentSessionID(t, pool, createdFullAccess.AgentID, "sess_restore_permission")
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 1)
	_, err = agent.New(pool).AuthenticateAgent(ctx, createdFullAccess.AgentID, oldFullAccessToken)
	require.NoError(t, err)

	err = store.RestoreAgentPermissionModeForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess, agent.AgentPermissionModeWorkspaceWrite)
	require.NoError(t, err)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentSessionID(t, pool, createdFullAccess.AgentID, "sess_restore_permission")
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 1)

	createdBuiltinDefault, err := store.CreateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, "mac_agent_permission", agent.CreateAgentParams{
		Name:        "perm-builtin-default",
		DisplayName: "Permission Builtin Default",
		Runtime:     "builtin",
		Model:       "gpt-5.4",
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, createdBuiltinDefault.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdBuiltinDefault.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdBuiltinDefault.AgentID, 1)

	createdClaudeDefault, err := store.CreateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, "mac_agent_permission", agent.CreateAgentParams{
		Name:        "perm-claude-default",
		DisplayName: "Permission Claude Default",
		Runtime:     "claude",
		Model:       "claude-sonnet-4-5",
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, createdClaudeDefault.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdClaudeDefault.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdClaudeDefault.AgentID, 1)

	createdBuiltinFullAccess, err := store.CreateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, "mac_agent_permission", agent.CreateAgentParams{
		Name:        "perm-builtin-full",
		DisplayName: "Permission Builtin Full",
		Runtime:     "builtin",
		Model:       "gpt-5.4",
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, createdBuiltinFullAccess.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdBuiltinFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdBuiltinFullAccess.AgentID, 1)

	builtinRuntime := "builtin"
	_, err = store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.UpdateAgentParams{
		PermissionMode: &workspacePermissionMode,
	})
	require.NoError(t, err)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 1)

	updatedBuiltinRuntime, err := store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.UpdateAgentParams{
		Runtime: &builtinRuntime,
	})
	require.NoError(t, err)
	require.Equal(t, "builtin", updatedBuiltinRuntime.Runtime)
	require.Equal(t, agent.AgentPermissionModeFullAccess, updatedBuiltinRuntime.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 2)

	claudeRuntime := "claude"
	updatedClaudeRuntime, err := store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.UpdateAgentParams{
		Runtime: &claudeRuntime,
	})
	require.NoError(t, err)
	require.Equal(t, "claude", updatedClaudeRuntime.Runtime)
	require.Equal(t, agent.AgentPermissionModeFullAccess, updatedClaudeRuntime.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 3)

	workspaceMode := agent.AgentPermissionModeWorkspaceWrite
	updatedBuiltinPermission, err := store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdBuiltinFullAccess.AgentID, agent.UpdateAgentParams{
		PermissionMode: &workspaceMode,
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, updatedBuiltinPermission.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdBuiltinFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdBuiltinFullAccess.AgentID, 1)

	nextMode := agent.AgentPermissionModeFullAccess
	updated, err := store.UpdateAgentForUser(ctx, fixture.UserID, fixture.WorkspaceID, createdFullAccess.AgentID, agent.UpdateAgentParams{
		PermissionMode: &nextMode,
	})
	require.NoError(t, err)
	require.Equal(t, agent.AgentPermissionModeFullAccess, updated.PermissionMode)
	requireStoredAgentPermissionMode(t, pool, createdFullAccess.AgentID, agent.AgentPermissionModeFullAccess)
	requireStoredAgentRuntimePolicyGeneration(t, pool, createdFullAccess.AgentID, 3)
}

func requireStoredAgentPermissionMode(t *testing.T, pool coredb.QueryRower, agentID string, want string) {
	t.Helper()

	var got string
	require.NoError(t, pool.QueryRow(context.Background(), `SELECT permission_mode FROM agents WHERE id = $1`, agentID).Scan(&got))
	require.Equal(t, want, got)
}

func requireStoredAgentSessionID(t *testing.T, pool coredb.QueryRower, agentID string, want string) {
	t.Helper()

	var got string
	require.NoError(t, pool.QueryRow(context.Background(), `SELECT session_id FROM agents WHERE id = $1`, agentID).Scan(&got))
	require.Equal(t, want, got)
}

func requireStoredAgentRuntimePolicyGeneration(t *testing.T, pool coredb.QueryRower, agentID string, want int64) {
	t.Helper()

	var got int64
	require.NoError(t, pool.QueryRow(context.Background(), `SELECT runtime_policy_generation FROM agents WHERE id = $1`, agentID).Scan(&got))
	require.Equal(t, want, got)
}

func requireStoredAgentVisibleReplyContract(t *testing.T, pool coredb.QueryRower, agentID string, want bool) {
	t.Helper()

	var got bool
	require.NoError(t, pool.QueryRow(context.Background(), `SELECT visible_reply_contract_enabled FROM agents WHERE id = $1`, agentID).Scan(&got))
	require.Equal(t, want, got)
}

func requireStoredAgentAuthTokenCleared(t *testing.T, pool coredb.QueryRower, agentID string) {
	t.Helper()

	var cleared bool
	require.NoError(t, pool.QueryRow(context.Background(), `SELECT auth_token_hash IS NULL FROM agents WHERE id = $1`, agentID).Scan(&cleared))
	require.True(t, cleared)
}
