package openclaw

import (
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/sipeed/picoclaw/pkg/config"
)

func TestLoadOpenClawConfig(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"agents": {
			"defaults": {
				"model": {
					"primary": "anthropic/claude-sonnet-4-20250514"
				},
				"workspace": "~/.openclaw/workspace"
			},
			"list": [
				{
					"id": "main",
					"name": "Main Agent",
					"model": {
						"primary": "openai/gpt-4o",
						"fallbacks": ["claude-3-opus"]
					}
				}
			]
		},
		"channels": {
			"telegram": {
				"enabled": true,
				"botToken": "test-token",
				"allowFrom": ["user1", "user2"]
			},
			"discord": {
				"enabled": true,
				"token": "discord-token"
			}
		},
		"models": {
			"providers": {
				"anthropic": {
					"api_key": "sk-ant-test",
					"base_url": "https://api.anthropic.com"
				},
				"openai": {
					"api_key": "sk-test"
				}
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	if cfg.Agents == nil {
		t.Error("agents should not be nil")
	}

	if cfg.Agents.Defaults == nil {
		t.Error("agents.defaults should not be nil")
	}

	provider, model := cfg.GetDefaultModel()
	if provider != "anthropic" {
		t.Errorf("expected provider 'anthropic', got '%s'", provider)
	}
	if model != "claude-sonnet-4-20250514" {
		t.Errorf("expected model 'claude-sonnet-4-20250514', got '%s'", model)
	}

	workspace := cfg.GetDefaultWorkspace()
	if workspace != "~/.picoclaw/workspace" {
		t.Errorf("expected workspace '~/.picoclaw/workspace', got '%s'", workspace)
	}

	agents := cfg.GetAgents()
	if len(agents) != 1 {
		t.Errorf("expected 1 agent, got %d", len(agents))
	}
	if agents[0].ID != "main" {
		t.Errorf("expected agent id 'main', got '%s'", agents[0].ID)
	}

	if cfg.Channels == nil {
		t.Error("channels should not be nil")
	}
	if cfg.Channels.Telegram == nil {
		t.Error("telegram channel should not be nil")
	}
	if cfg.Channels.Telegram.BotToken == nil || *cfg.Channels.Telegram.BotToken != "test-token" {
		t.Error("telegram bot token not parsed correctly")
	}
}

func TestGetProviderConfig(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"models": {
			"providers": {
				"anthropic": {
					"api_key": "sk-ant-test",
					"base_url": "https://api.anthropic.com",
					"max_tokens": 4096
				},
				"openai": {
					"api_key": "sk-test",
					"base_url": "https://api.openai.com"
				}
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	providers := GetProviderConfig(cfg.Models)
	if len(providers) != 2 {
		t.Errorf("expected 2 providers, got %d", len(providers))
	}

	if anthropic, ok := providers["anthropic"]; ok {
		if anthropic.APIKey != "sk-ant-test" {
			t.Errorf("expected anthropic api_key 'sk-ant-test', got '%s'", anthropic.APIKey)
		}
		if anthropic.BaseURL != "https://api.anthropic.com" {
			t.Errorf("expected anthropic base_url 'https://api.anthropic.com', got '%s'", anthropic.BaseURL)
		}
	} else {
		t.Error("anthropic provider not found")
	}

	if openai, ok := providers["openai"]; ok {
		if openai.APIKey != "sk-test" {
			t.Errorf("expected openai api_key 'sk-test', got '%s'", openai.APIKey)
		}
	} else {
		t.Error("openai provider not found")
	}
}

func TestConvertToPicoClaw(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"agents": {
			"defaults": {
				"model": {
					"primary": "anthropic/claude-sonnet-4-20250514"
				},
				"workspace": "~/.openclaw/workspace"
			},
			"list": [
				{
					"id": "main",
					"name": "Main Agent"
				},
				{
					"id": "assistant",
					"name": "Assistant",
					"skills": ["skill1", "skill2"]
				}
			]
		},
		"channels": {
			"telegram": {
				"enabled": true,
				"botToken": "test-token",
				"allowFrom": ["user1", "user2"]
			},
			"discord": {
				"enabled": false,
				"token": "discord-token"
			},
			"whatsapp": {
				"enabled": true,
				"bridgeUrl": "http://localhost:3000"
			},
			"feishu": {
				"enabled": true,
				"appId": "app-id",
				"appSecret": "app-secret",
				"allowFrom": ["user3"]
			},
			"signal": {
				"enabled": true
			}
		},
		"models": {
			"providers": {
				"anthropic": {
					"api_key": "sk-ant-test"
				},
				"openai": {
					"api_key": "sk-test"
				}
			}
		},
		"skills": {
			"entries": {
				"skill1": {}
			}
		},
		"memory": {"enabled": true},
		"cron": {"enabled": true}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	picoCfg, warnings, err := cfg.ConvertToPicoClaw("")
	if err != nil {
		t.Fatalf("failed to convert config: %v", err)
	}

	if picoCfg.Agents.Defaults.ModelName != "claude-sonnet-4-20250514" {
		t.Errorf("expected model 'claude-sonnet-4-20250514', got '%s'", picoCfg.Agents.Defaults.ModelName)
	}
	if picoCfg.Agents.Defaults.Workspace != "~/.picoclaw/workspace" {
		t.Errorf("expected workspace '~/.picoclaw/workspace', got '%s'", picoCfg.Agents.Defaults.Workspace)
	}

	if len(picoCfg.Agents.List) != 2 {
		t.Errorf("expected 2 agents, got %d", len(picoCfg.Agents.List))
	}
	if picoCfg.Agents.List[0].ID != "main" {
		t.Errorf("expected first agent id 'main', got '%s'", picoCfg.Agents.List[0].ID)
	}
	if picoCfg.Agents.List[1].Skills == nil || len(picoCfg.Agents.List[1].Skills) != 2 {
		t.Errorf("expected 2 skills for assistant agent")
	}

	if !picoCfg.Channels.Telegram.Enabled {
		t.Error("telegram should be enabled")
	}
	if picoCfg.Channels.Telegram.Token != "test-token" {
		t.Errorf("expected telegram token 'test-token', got '%s'", picoCfg.Channels.Telegram.Token)
	}

	if picoCfg.Channels.WhatsApp.BridgeURL != "http://localhost:3000" {
		t.Errorf("expected whatsapp bridge URL 'http://localhost:3000', got '%s'", picoCfg.Channels.WhatsApp.BridgeURL)
	}

	if picoCfg.Channels.Feishu.AppID != "app-id" {
		t.Errorf("expected feishu app ID 'app-id', got '%s'", picoCfg.Channels.Feishu.AppID)
	}

	if len(picoCfg.ModelList) != 1 {
		t.Errorf("expected 1 model config (no models.json provided), got %d", len(picoCfg.ModelList))
	}

	foundWarning := false
	for _, w := range warnings {
		if len(w) > 0 {
			foundWarning = true
			break
		}
	}
	if !foundWarning {
		t.Log("warnings should be generated for skills, memory, cron, and unsupported channels")
	}
}

func TestToStandardConfig_ExecAllowRemoteDefaultsTrue(t *testing.T) {
	cfg := (&PicoClawConfig{
		Tools: ToolsConfig{
			Exec: ExecConfig{
				EnableDenyPatterns: true,
			},
		},
	}).ToStandardConfig()

	if !cfg.Tools.Exec.AllowRemote {
		t.Fatal("ToStandardConfig() should preserve the default tools.exec.allow_remote=true")
	}
}

func TestConvertToPicoClawWithQQAndDingTalk(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"agents": {
			"defaults": {
				"model": {
					"primary": "anthropic/claude-sonnet-4-20250514"
				}
			}
		},
		"channels": {
			"qq": {
				"enabled": true,
				"appId": "qq-app-id",
				"appSecret": "qq-app-secret"
			},
			"dingtalk": {
				"enabled": true,
				"appId": "ding-app-id",
				"appSecret": "ding-app-secret"
			},
			"maixcam": {
				"enabled": true,
				"host": "192.168.1.100",
				"port": 9000
			},
			"slack": {
				"enabled": true,
				"botToken": "xoxb-test",
				"appToken": "xapp-test"
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	picoCfg, _, err := cfg.ConvertToPicoClaw("")
	if err != nil {
		t.Fatalf("failed to convert config: %v", err)
	}

	if !picoCfg.Channels.QQ.Enabled {
		t.Error("qq should be enabled")
	}
	if picoCfg.Channels.QQ.AppID != "qq-app-id" {
		t.Errorf("expected qq app ID 'qq-app-id', got '%s'", picoCfg.Channels.QQ.AppID)
	}

	if !picoCfg.Channels.DingTalk.Enabled {
		t.Error("dingtalk should be enabled")
	}
	if picoCfg.Channels.DingTalk.ClientID != "ding-app-id" {
		t.Errorf("expected dingtalk client ID 'ding-app-id', got '%s'", picoCfg.Channels.DingTalk.ClientID)
	}

	if !picoCfg.Channels.MaixCam.Enabled {
		t.Error("maixcam should be enabled")
	}
	if picoCfg.Channels.MaixCam.Host != "192.168.1.100" {
		t.Errorf("expected maixcam host '192.168.1.100', got '%s'", picoCfg.Channels.MaixCam.Host)
	}
	if picoCfg.Channels.MaixCam.Port != 9000 {
		t.Errorf("expected maixcam port 9000, got %d", picoCfg.Channels.MaixCam.Port)
	}

	if !picoCfg.Channels.Slack.Enabled {
		t.Error("slack should be enabled")
	}
	if picoCfg.Channels.Slack.BotToken != "xoxb-test" {
		t.Errorf("expected slack bot token 'xoxb-test', got '%s'", picoCfg.Channels.Slack.BotToken)
	}
	if picoCfg.Channels.Slack.AppToken != "xapp-test" {
		t.Errorf("expected slack app token 'xapp-test', got '%s'", picoCfg.Channels.Slack.AppToken)
	}
}

func TestConvertToPicoClawWithMatrix(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"channels": {
			"matrix": {
				"enabled": true,
				"homeserver": "https://matrix.example.com",
				"userId": "@bot:matrix.example.com",
				"accessToken": "syt_test_token",
				"allowFrom": ["@alice:matrix.example.com"]
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	picoCfg, warnings, err := cfg.ConvertToPicoClaw("")
	if err != nil {
		t.Fatalf("failed to convert config: %v", err)
	}

	if !picoCfg.Channels.Matrix.Enabled {
		t.Error("matrix should be enabled")
	}
	if picoCfg.Channels.Matrix.Homeserver != "https://matrix.example.com" {
		t.Errorf("expected matrix homeserver, got %q", picoCfg.Channels.Matrix.Homeserver)
	}
	if picoCfg.Channels.Matrix.UserID != "@bot:matrix.example.com" {
		t.Errorf("expected matrix user_id, got %q", picoCfg.Channels.Matrix.UserID)
	}
	if picoCfg.Channels.Matrix.AccessToken != "syt_test_token" {
		t.Errorf("expected matrix access_token, got %q", picoCfg.Channels.Matrix.AccessToken)
	}
	if len(picoCfg.Channels.Matrix.AllowFrom) != 1 ||
		picoCfg.Channels.Matrix.AllowFrom[0] != "@alice:matrix.example.com" {
		t.Errorf("unexpected matrix allow_from: %#v", picoCfg.Channels.Matrix.AllowFrom)
	}

	for _, w := range warnings {
		if strings.Contains(w, "Channel 'matrix'") {
			t.Fatalf("matrix should no longer be reported as unsupported, warning=%q", w)
		}
	}
}

func TestConvertToPicoClawWithMatrixDisabled(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{
		"channels": {
			"matrix": {
				"enabled": false,
				"homeserver": "https://matrix.example.com",
				"userId": "@bot:matrix.example.com",
				"accessToken": "syt_test_token"
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfig(configPath)
	if err != nil {
		t.Fatalf("failed to load config: %v", err)
	}

	picoCfg, _, err := cfg.ConvertToPicoClaw("")
	if err != nil {
		t.Fatalf("failed to convert config: %v", err)
	}

	if picoCfg.Channels.Matrix.Enabled {
		t.Error("matrix should respect enabled=false from source config")
	}
}

func TestOpenClawAgentModel(t *testing.T) {
	model := &OpenClawAgentModel{
		Primary:   strPtr("anthropic/claude-3-opus"),
		Fallbacks: []string{"claude-3-sonnet", "claude-3-haiku"},
	}

	primary := model.GetPrimary()
	if primary != "anthropic/claude-3-opus" {
		t.Errorf("expected primary 'anthropic/claude-3-opus', got '%s'", primary)
	}

	fallbacks := model.GetFallbacks()
	if len(fallbacks) != 2 {
		t.Errorf("expected 2 fallbacks, got %d", len(fallbacks))
	}

	model2 := &OpenClawAgentModel{
		Simple: "claude-3-opus",
	}

	primary2 := model2.GetPrimary()
	if primary2 != "claude-3-opus" {
		t.Errorf("expected primary 'claude-3-opus' from Simple, got '%s'", primary2)
	}
}

func TestChannelEnabled(t *testing.T) {
	cfg := &OpenClawConfig{
		Channels: &OpenClawChannels{
			Telegram: &OpenClawTelegramConfig{
				Enabled: boolPtr(true),
			},
			Discord: &OpenClawDiscordConfig{
				Enabled: boolPtr(false),
			},
			Slack: &OpenClawSlackConfig{
				Enabled: boolPtr(true),
			},
		},
	}

	if !cfg.IsChannelEnabled("telegram") {
		t.Error("telegram should be enabled")
	}
	if cfg.IsChannelEnabled("discord") {
		t.Error("discord should be disabled")
	}
	if !cfg.IsChannelEnabled("slack") {
		t.Error("slack should be enabled (explicitly set)")
	}
	if !cfg.IsChannelEnabled("matrix") {
		t.Error("matrix should be enabled (nil config defaults to enabled)")
	}
	if cfg.IsChannelEnabled("line") {
		t.Error("line should return false (not in switch cases)")
	}
}

func TestGetDefaultModel(t *testing.T) {
	cfg := &OpenClawConfig{
		Agents: &OpenClawAgents{
			Defaults: &OpenClawAgentDefaults{
				Model: &OpenClawAgentModel{
					Primary: strPtr("openai/gpt-4"),
				},
			},
		},
	}

	provider, model := cfg.GetDefaultModel()
	if provider != "openai" {
		t.Errorf("expected provider 'openai', got '%s'", provider)
	}
	if model != "gpt-4" {
		t.Errorf("expected model 'gpt-4', got '%s'", model)
	}
}

func TestGetDefaultModelWithNoDefaults(t *testing.T) {
	cfg := &OpenClawConfig{}

	provider, model := cfg.GetDefaultModel()
	if provider != "anthropic" {
		t.Errorf("expected default provider 'anthropic', got '%s'", provider)
	}
	if model != "claude-sonnet-4-20250514" {
		t.Errorf("expected default model 'claude-sonnet-4-20250514', got '%s'", model)
	}
}

func TestHasFunctions(t *testing.T) {
	cfg := &OpenClawConfig{
		Skills:  &OpenClawSkills{Entries: map[string]json.RawMessage{"skill1": nil}},
		Memory:  json.RawMessage(`{"enabled": true}`),
		Cron:    json.RawMessage(`{"enabled": true}`),
		Hooks:   json.RawMessage(`{"enabled": true}`),
		Session: json.RawMessage(`{"enabled": true}`),
		Auth:    &OpenClawAuth{Profiles: json.RawMessage(`{"profile1": {}}`)},
	}

	if !cfg.HasSkills() {
		t.Error("should have skills")
	}
	if !cfg.HasMemory() {
		t.Error("should have memory")
	}
	if !cfg.HasCron() {
		t.Error("should have cron")
	}
	if !cfg.HasHooks() {
		t.Error("should have hooks")
	}
	if !cfg.HasSession() {
		t.Error("should have session")
	}
	if !cfg.HasAuthProfiles() {
		t.Error("should have auth profiles")
	}

	cfg2 := &OpenClawConfig{}
	if cfg2.HasSkills() {
		t.Error("should not have skills")
	}
	if cfg2.HasMemory() {
		t.Error("should not have memory")
	}
}

func TestLoadOpenClawConfigFromDir(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "openclaw.json")

	testConfig := `{"agents": {}}`
	err := os.WriteFile(configPath, []byte(testConfig), 0o644)
	if err != nil {
		t.Fatalf("failed to write test config: %v", err)
	}

	cfg, err := LoadOpenClawConfigFromDir(tmpDir)
	if err != nil {
		t.Fatalf("failed to load config from dir: %v", err)
	}

	if cfg.Agents == nil {
		t.Error("agents should not be nil")
	}

	_, err = LoadOpenClawConfigFromDir("/nonexistent/dir")
	if err == nil {
		t.Error("should return error for nonexistent dir")
	}
}

func TestToStandardConfig(t *testing.T) {
	picoCfg := &PicoClawConfig{
		Agents: AgentsConfig{
			Defaults: AgentDefaults{
				Provider:  "anthropic",
				ModelName: "claude-sonnet-4-20250514",
				Workspace: "~/.picoclaw/workspace",
			},
			List: []AgentConfig{
				{
					ID:      "main",
					Name:    "Main Agent",
					Default: true,
				},
			},
		},
		ModelList: []ModelConfig{
			{
				ModelName: "claude-sonnet-4-20250514",
				Model:     "anthropic/claude-sonnet-4-20250514",
				APIKey:    "sk-ant-test",
			},
		},
		Channels: ChannelsConfig{
			Telegram: TelegramConfig{
				Enabled:   true,
				Token:     "test-token",
				AllowFrom: []string{"user1"},
			},
			WhatsApp: WhatsAppConfig{
				Enabled:   true,
				BridgeURL: "http://localhost:3000",
			},
		},
		Gateway: GatewayConfig{
			Host: "0.0.0.0",
			Port: 8080,
		},
	}

	stdCfg := picoCfg.ToStandardConfig()

	if stdCfg.Agents.Defaults.Provider != "anthropic" {
		t.Errorf("expected provider 'anthropic', got '%s'", stdCfg.Agents.Defaults.Provider)
	}
	if stdCfg.Agents.Defaults.ModelName != "claude-sonnet-4-20250514" {
		t.Errorf("expected model name 'claude-sonnet-4-20250514', got '%s'", stdCfg.Agents.Defaults.ModelName)
	}
	if stdCfg.Agents.Defaults.Workspace != "~/.picoclaw/workspace" {
		t.Errorf("expected workspace '~/.picoclaw/workspace', got '%s'", stdCfg.Agents.Defaults.Workspace)
	}

	if len(stdCfg.Agents.List) != 1 {
		t.Errorf("expected 1 agent, got %d", len(stdCfg.Agents.List))
	}
	if stdCfg.Agents.List[0].ID != "main" {
		t.Errorf("expected agent id 'main', got '%s'", stdCfg.Agents.List[0].ID)
	}

	foundModel := false
	var foundAPIKey string
	for _, m := range stdCfg.ModelList {
		if m.ModelName == "claude-sonnet-4-20250514" {
			foundModel = true
			foundAPIKey = m.APIKey()
			break
		}
	}
	if !foundModel {
		t.Error("expected to find claude-sonnet-4-20250514 model config")
	}
	if foundAPIKey != "sk-ant-test" {
		t.Errorf("expected api key 'sk-ant-test', got '%s'", foundAPIKey)
	}

	if !stdCfg.Channels["telegram"].Enabled {
		t.Error("telegram should be enabled")
	}
	decoded, err := stdCfg.Channels["telegram"].GetDecoded()
	if err != nil {
		t.Fatalf("GetDecoded() error = %v", err)
	}
	if tCfg, ok := decoded.(*config.TelegramSettings); ok &&
		tCfg.Token.String() != "test-token" {
		t.Errorf("expected token 'test-token', got '%s'", tCfg.Token.String())
	}

	if stdCfg.Gateway.Port != 8080 {
		t.Errorf("expected gateway port 8080, got %d", stdCfg.Gateway.Port)
	}
}

func TestLoadProviderConfigFromAgentsDir(t *testing.T) {
	tmpDir := t.TempDir()

	agentsDir := filepath.Join(tmpDir, "agents", "main", "agent")
	err := os.MkdirAll(agentsDir, 0o755)
	if err != nil {
		t.Fatalf("failed to create agents dir: %v", err)
	}

	modelsJSON := `{
		"providers": {
			"anthropic": {
				"baseUrl": "https://api.anthropic.com",
				"api": "anthropic",
				"apiKey": "sk-ant-from-models",
				"models": [
					{
						"id": "claude-sonnet-4-20250514",
						"name": "Claude Sonnet 4"
					}
				]
			},
			"openai": {
				"baseUrl": "https://api.openai.com",
				"api": "openai",
				"apiKey": "sk-from-models",
				"models": [
					{
						"id": "gpt-4o",
						"name": "GPT-4o"
					}
				]
			},
			"zhipu": {
				"baseUrl": "https://open.bigmodel.cn/api/paas/v4",
				"api": "openai",
				"apiKey": "zhipu-key",
				"models": []
			}
		}
	}`

	err = os.WriteFile(filepath.Join(agentsDir, "models.json"), []byte(modelsJSON), 0o644)
	if err != nil {
		t.Fatalf("failed to write models.json: %v", err)
	}

	providers := GetProviderConfigFromDir(tmpDir)
	if len(providers) != 3 {
		t.Errorf("expected 3 providers, got %d", len(providers))
	}

	if anthropic, ok := providers["anthropic"]; ok {
		if anthropic.ApiKey != "sk-ant-from-models" {
			t.Errorf("expected anthropic apiKey 'sk-ant-from-models', got '%s'", anthropic.ApiKey)
		}
		if anthropic.BaseUrl != "https://api.anthropic.com" {
			t.Errorf("expected anthropic baseUrl 'https://api.anthropic.com', got '%s'", anthropic.BaseUrl)
		}
	} else {
		t.Error("anthropic provider not found")
	}

	if openai, ok := providers["openai"]; ok {
		if openai.ApiKey != "sk-from-models" {
			t.Errorf("expected openai apiKey 'sk-from-models', got '%s'", openai.ApiKey)
		}
		if openai.BaseUrl != "https://api.openai.com" {
			t.Errorf("expected openai baseUrl 'https://api.openai.com', got '%s'", openai.BaseUrl)
		}
	} else {
		t.Error("openai provider not found")
	}

	if zhipu, ok := providers["zhipu"]; ok {
		if zhipu.ApiKey != "zhipu-key" {
			t.Errorf("expected zhipu apiKey 'zhipu-key', got '%s'", zhipu.ApiKey)
		}
		if zhipu.BaseUrl != "https://open.bigmodel.cn/api/paas/v4" {
			t.Errorf("expected zhipu baseUrl 'https://open.bigmodel.cn/api/paas/v4', got '%s'", zhipu.BaseUrl)
		}
	} else {
		t.Error("zhipu provider not found")
	}
}

func TestGetProviderConfigFromDirNotExist(t *testing.T) {
	providers := GetProviderConfigFromDir("/nonexistent/path")
	if len(providers) != 0 {
		t.Errorf("expected 0 providers for nonexistent path, got %d", len(providers))
	}
}

func strPtr(s string) *string {
	return &s
}

func boolPtr(b bool) *bool {
	return &b
}
