package profile

import (
	"crypto/ed25519"
	"crypto/rand"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig"
	"github.com/tidbcloud/AgentTeam/desktop/internal/crypto"
	"github.com/tidbcloud/AgentTeam/desktop/internal/provider"
)

func TestResolveAgentLLMRuntimeDefaultDoesNotPromoteNativeProvider(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	envKey := "AGENTTEAM_TEST_CRS_OAI_KEY"
	t.Setenv(envKey, "")
	os.Unsetenv(envKey)
	runtimeconfig.SetShellEnvForRuntimeResolution(map[string]string{
		envKey: "sk-native",
	})
	t.Cleanup(func() {
		runtimeconfig.SetShellEnvForRuntimeResolution(nil)
	})

	codexDir := filepath.Join(home, ".codex")
	require.NoError(t, os.MkdirAll(codexDir, 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(codexDir, "config.toml"), []byte(`model_provider = "crs"
model = "gpt-5.4"
model_reasoning_effort = "high"

[model_providers.crs]
name = "CRS"
base_url = "https://proxy.example.com/v1"
wire_api = "responses"
env_key = "`+envKey+`"
`), 0o644))

	resolved, err := ResolveAgentLLM(agent.Config{
		ID:      "agt_native",
		Runtime: "codex",
		Source:  agent.NewLocalSource(),
	}, runtimeconfig.TypeCodex)
	require.NoError(t, err)
	require.Nil(t, resolved.Upstream)
	require.Equal(t, "gpt-5.4", resolved.Model)
	require.Equal(t, "high", resolved.ReasoningEffort)
	require.Equal(t, agent.LLMModeRuntimeDefault, resolved.Mode)
}

func TestResolveAgentLLMPartialOverrideMergesRuntimeConfig(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)

	claudeDir := filepath.Join(home, ".claude")
	require.NoError(t, os.MkdirAll(claudeDir, 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(claudeDir, "settings.json"), []byte(`{
  "env": {
    "ANTHROPIC_BASE_URL": "https://native.example.com",
    "ANTHROPIC_MODEL": "claude-native"
  }
}`), 0o644))

	resolved, err := ResolveAgentLLM(agent.Config{
		ID:      "agt_inline",
		Name:    "Inline Agent",
		Runtime: "claude",
		Source:  agent.NewLocalSource(),
		APIKey:  "sk-inline",
		Model:   "claude-override",
	}, runtimeconfig.TypeClaudeCode)
	require.NoError(t, err)
	require.NotNil(t, resolved.Upstream)
	require.Equal(t, agent.LLMModeInline, resolved.Mode)
	require.Equal(t, "claude-override", resolved.Model)
	require.Equal(t, "https://native.example.com", resolved.Upstream.BaseURL)
	require.Equal(t, "sk-inline", resolved.Upstream.APIKey)
	require.Equal(t, []string{provider.WireAPIAnthropic}, resolved.Upstream.WireAPIs)
}

func TestResolveAgentLLMClaudeRuntimeDefaultDoesNotPromoteNativeProvider(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)

	claudeDir := filepath.Join(home, ".claude")
	require.NoError(t, os.MkdirAll(claudeDir, 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(claudeDir, "settings.json"), []byte(`{
  "env": {
    "ANTHROPIC_BASE_URL": "https://dendro.codgician.me/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "Bearer dendro-token",
    "ANTHROPIC_MODEL": "claude-opus-4.6"
  }
}`), 0o644))

	resolved, err := ResolveAgentLLM(agent.Config{
		ID:      "agt_native_claude",
		Runtime: "claude",
		Source:  agent.NewLocalSource(),
	}, runtimeconfig.TypeClaudeCode)
	require.NoError(t, err)
	require.Nil(t, resolved.Upstream)
	require.Equal(t, agent.LLMModeRuntimeDefault, resolved.Mode)
	require.Equal(t, "claude-opus-4.6", resolved.Model)
}

func TestResolveAgentLLMInjectsBuiltinRuntimeDefaultProvider(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	_, err := UpdateRuntimeConfig("builtin", RuntimeConfigEditable{
		BaseURL: "https://builtin.example.com/v1",
		APIKey:  "sk-builtin",
		Model:   "gpt-builtin",
	})
	require.NoError(t, err)

	resolved, err := ResolveAgentLLM(agent.Config{
		ID:      "agt_builtin",
		Runtime: "builtin",
		Source:  agent.NewLocalSource(),
	}, runtimeconfig.TypeBuiltin)
	require.NoError(t, err)
	require.NotNil(t, resolved.Upstream)
	require.Equal(t, agent.LLMModeRuntimeDefault, resolved.Mode)
	require.Equal(t, "gpt-builtin", resolved.Model)
	require.Equal(t, "https://builtin.example.com/v1", resolved.Upstream.BaseURL)
	require.Equal(t, "sk-builtin", resolved.Upstream.APIKey)
	require.Equal(t, []string{provider.WireAIOpenAI}, resolved.Upstream.WireAPIs)
}

func TestResolveRuntimeLLMInjectsBuiltinProviderAndNormalizesNativeModel(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)
	opencodeDir := filepath.Join(home, ".config", "opencode")
	require.NoError(t, os.MkdirAll(opencodeDir, 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(opencodeDir, "opencode.json"), []byte(`{
  "model": "anthropic/deepseek-v4-pro",
  "provider": {
    "anthropic": {
      "npm": "@ai-sdk/anthropic",
      "options": {
        "baseURL": "https://builtin.example.com/anthropic",
        "apiKey": "sk-builtin"
      }
    }
  }
}`), 0o600))

	resolved, err := ResolveRuntimeLLM(runtimeconfig.TypeBuiltin, "anthropic/deepseek-v4-pro", "high")
	require.NoError(t, err)
	require.NotNil(t, resolved.Upstream)
	require.Equal(t, "agentteam", resolved.Upstream.ID)
	require.Equal(t, "https://builtin.example.com/anthropic", resolved.Upstream.BaseURL)
	require.Equal(t, "sk-builtin", resolved.Upstream.APIKey)
	require.Equal(t, []string{provider.WireAPIAnthropic}, resolved.Upstream.WireAPIs)
	require.Equal(t, "deepseek-v4-pro", resolved.Model)
	require.Equal(t, "high", resolved.ReasoningEffort)
}

func TestResolveAgentLLMDecryptsStoredInlineAPIKey(t *testing.T) {
	home := t.TempDir()
	t.Setenv("HOME", home)

	pub, pri, err := ed25519.GenerateKey(rand.Reader)
	require.NoError(t, err)
	require.NoError(t, crypto.SetKeys(pub, pri))
	t.Cleanup(crypto.ResetKeys)

	ciphertext, err := crypto.Encrypt("sk-inline")
	require.NoError(t, err)

	claudeDir := filepath.Join(home, ".claude")
	require.NoError(t, os.MkdirAll(claudeDir, 0o755))
	require.NoError(t, os.WriteFile(filepath.Join(claudeDir, "settings.json"), []byte(`{
  "env": {
    "ANTHROPIC_BASE_URL": "https://native.example.com"
  }
}`), 0o644))

	resolved, err := ResolveAgentLLM(agent.Config{
		ID:      "agt_inline",
		Name:    "Inline Agent",
		Runtime: "claude",
		Source:  agent.NewLocalSource(),
		APIKey:  ciphertext,
	}, runtimeconfig.TypeClaudeCode)
	require.NoError(t, err)
	require.NotNil(t, resolved.Upstream)
	require.Equal(t, "sk-inline", resolved.Upstream.APIKey)
}
