package config

import (
	"os"
	"path/filepath"
	"testing"
	"time"
)

// TestLoadFromYAML tests loading configuration from a YAML file.
func TestLoadFromYAML(t *testing.T) {
	// Create a temporary config file
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
feishu:
  app_id: "test-app-id"
  app_secret: "test-app-secret"
  approval_code: "test-approval-code"
  chat_id: "test-chat-id"
  base_url: "https://custom.feishu.cn"

database:
  host: "db.example.com"
  port: 3307
  user: "testuser"
  password: "testpass"
  database: "testdb"
  max_open_conns: 20
  max_idle_conns: 10
  max_lifetime: "2h"

providers:
  aws:
    identity_center_arn: "arn:aws:sso:::instance/test"
    identity_store_id: "d-test123"
    region: "us-west-2"
    accounts:
      - id: "123456789012"
        name: "test-account"
        type: "prod"

  gcp:
    credentials_file: "/path/to/creds.json"
    role_mapping_file: "/path/to/roles.ini"
    projects:
      - id: "test-project"
        name: "Test Project"
        type: "test"

  azure:
    tenant_id: "test-tenant-id"
    client_id: "test-client-id"
    client_secret: "test-client-secret"
    subscriptions:
      - id: "sub-123"
        name: "test-subscription"
        type: "prod"
        role_suffix: "-test"

  alibaba:
    access_key_id: "test-ak"
    access_key_secret: "test-sk"
    directory_id: "d-test"
    endpoint: "cloudsso.test.aliyuncs.com"
    accounts:
      - id: "1234567890"
        name: "test-alibaba"
        type: "prod"

runtime:
  poll_interval: "10s"
  expire_check_interval: "2h"
  grace_period_days: 5
  http_timeout: "30s"
  max_retries: 5
  log_level: "debug"
  whitelist_emails:
    - "admin@example.com"
    - "security@example.com"

permission_mappings:
  - feishu_name: "viewonly"
    real_name: "ViewOnly-Role"
  - feishu_name: "admin"
    real_name: "Admin-Role"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

	// Load the configuration
	cfg, err := Load(configPath)
	if err != nil {
		t.Fatalf("Failed to load config: %v", err)
	}

	// Verify Feishu config
	if cfg.Feishu.AppID != "test-app-id" {
		t.Errorf("Expected Feishu.AppID to be 'test-app-id', got '%s'", cfg.Feishu.AppID)
	}
	if cfg.Feishu.AppSecret != "test-app-secret" {
		t.Errorf("Expected Feishu.AppSecret to be 'test-app-secret', got '%s'", cfg.Feishu.AppSecret)
	}
	if cfg.Feishu.BaseURL != "https://custom.feishu.cn" {
		t.Errorf("Expected Feishu.BaseURL to be 'https://custom.feishu.cn', got '%s'", cfg.Feishu.BaseURL)
	}

	// Verify Database config
	if cfg.Database.Host != "db.example.com" {
		t.Errorf("Expected Database.Host to be 'db.example.com', got '%s'", cfg.Database.Host)
	}
	if cfg.Database.Port != 3307 {
		t.Errorf("Expected Database.Port to be 3307, got %d", cfg.Database.Port)
	}
	if cfg.Database.MaxLifetime != 2*time.Hour {
		t.Errorf("Expected Database.MaxLifetime to be 2h, got %v", cfg.Database.MaxLifetime)
	}

	// Verify AWS config
	if cfg.Providers.AWS.Region != "us-west-2" {
		t.Errorf("Expected AWS.Region to be 'us-west-2', got '%s'", cfg.Providers.AWS.Region)
	}
	if len(cfg.Providers.AWS.Accounts) != 1 {
		t.Errorf("Expected 1 AWS account, got %d", len(cfg.Providers.AWS.Accounts))
	}

	// Verify Runtime config
	if cfg.Runtime.PollInterval != 10*time.Second {
		t.Errorf("Expected Runtime.PollInterval to be 10s, got %v", cfg.Runtime.PollInterval)
	}
	if cfg.Runtime.GracePeriodDays != 5 {
		t.Errorf("Expected Runtime.GracePeriodDays to be 5, got %d", cfg.Runtime.GracePeriodDays)
	}
	if len(cfg.Runtime.WhitelistEmails) != 2 {
		t.Errorf("Expected 2 whitelist emails, got %d", len(cfg.Runtime.WhitelistEmails))
	}

	// Verify Permission mappings
	if len(cfg.PermissionMappings) != 2 {
		t.Errorf("Expected 2 permission mappings, got %d", len(cfg.PermissionMappings))
	}
}

// TestDefaultValues tests that default values are applied correctly.
func TestDefaultValues(t *testing.T) {
	// Create a minimal config file
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
feishu:
  app_id: "test-app-id"
  app_secret: "test-app-secret"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

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

	// Verify default values are applied
	if cfg.Feishu.BaseURL != "https://open.feishu.cn" {
		t.Errorf("Expected default Feishu.BaseURL to be 'https://open.feishu.cn', got '%s'", cfg.Feishu.BaseURL)
	}
	if cfg.Database.Host != "127.0.0.1" {
		t.Errorf("Expected default Database.Host to be '127.0.0.1', got '%s'", cfg.Database.Host)
	}
	if cfg.Database.Port != 3306 {
		t.Errorf("Expected default Database.Port to be 3306, got %d", cfg.Database.Port)
	}
	if cfg.Database.Database != "security" {
		t.Errorf("Expected default Database.Database to be 'security', got '%s'", cfg.Database.Database)
	}
	if cfg.Database.MaxOpenConns != 10 {
		t.Errorf("Expected default Database.MaxOpenConns to be 10, got %d", cfg.Database.MaxOpenConns)
	}
	if cfg.Database.MaxIdleConns != 5 {
		t.Errorf("Expected default Database.MaxIdleConns to be 5, got %d", cfg.Database.MaxIdleConns)
	}
	if cfg.Database.MaxLifetime != time.Hour {
		t.Errorf("Expected default Database.MaxLifetime to be 1h, got %v", cfg.Database.MaxLifetime)
	}
	if cfg.Providers.AWS.Region != "ap-northeast-1" {
		t.Errorf("Expected default AWS.Region to be 'ap-northeast-1', got '%s'", cfg.Providers.AWS.Region)
	}
	if cfg.Providers.Alibaba.Endpoint != "cloudsso.cn-shanghai.aliyuncs.com" {
		t.Errorf("Expected default Alibaba.Endpoint to be 'cloudsso.cn-shanghai.aliyuncs.com', got '%s'", cfg.Providers.Alibaba.Endpoint)
	}
	if cfg.Runtime.PollInterval != 5*time.Second {
		t.Errorf("Expected default Runtime.PollInterval to be 5s, got %v", cfg.Runtime.PollInterval)
	}
	if cfg.Runtime.ExpireCheckInterval != time.Hour {
		t.Errorf("Expected default Runtime.ExpireCheckInterval to be 1h, got %v", cfg.Runtime.ExpireCheckInterval)
	}
	if cfg.Runtime.GracePeriodDays != 3 {
		t.Errorf("Expected default Runtime.GracePeriodDays to be 3, got %d", cfg.Runtime.GracePeriodDays)
	}
	if cfg.Runtime.HTTPTimeout != 10*time.Second {
		t.Errorf("Expected default Runtime.HTTPTimeout to be 10s, got %v", cfg.Runtime.HTTPTimeout)
	}
	if cfg.Runtime.MaxRetries != 3 {
		t.Errorf("Expected default Runtime.MaxRetries to be 3, got %d", cfg.Runtime.MaxRetries)
	}
	if cfg.Runtime.LogLevel != "info" {
		t.Errorf("Expected default Runtime.LogLevel to be 'info', got '%s'", cfg.Runtime.LogLevel)
	}
}

// TestEnvOverrides tests that environment variables override config file values.
func TestEnvOverrides(t *testing.T) {
	// Create a config file with values that will be overridden
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
feishu:
  app_id: "file-app-id"
  app_secret: "file-app-secret"

database:
  user: "file-user"
  password: "file-password"

providers:
  azure:
    tenant_id: "file-tenant-id"
    client_id: "file-client-id"
    client_secret: "file-client-secret"

  alibaba:
    access_key_id: "file-ak"
    access_key_secret: "file-sk"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

	// Set environment variables
	envVars := map[string]string{
		"FEISHU_APP_ID":             "env-app-id",
		"FEISHU_APP_SECRET":         "env-app-secret",
		"DB_USER":                   "env-user",
		"DB_PASSWORD":               "env-password",
		"AZURE_TENANT_ID":           "env-tenant-id",
		"AZURE_CLIENT_ID":           "env-client-id",
		"AZURE_CLIENT_SECRET":       "env-client-secret",
		"ALIBABA_ACCESS_KEY_ID":     "env-ak",
		"ALIBABA_ACCESS_KEY_SECRET": "env-sk",
	}

	for k, v := range envVars {
		os.Setenv(k, v)
		defer os.Unsetenv(k)
	}

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

	// Verify environment variables override file values
	if cfg.Feishu.AppID != "env-app-id" {
		t.Errorf("Expected Feishu.AppID to be 'env-app-id', got '%s'", cfg.Feishu.AppID)
	}
	if cfg.Feishu.AppSecret != "env-app-secret" {
		t.Errorf("Expected Feishu.AppSecret to be 'env-app-secret', got '%s'", cfg.Feishu.AppSecret)
	}
	if cfg.Database.User != "env-user" {
		t.Errorf("Expected Database.User to be 'env-user', got '%s'", cfg.Database.User)
	}
	if cfg.Database.Password != "env-password" {
		t.Errorf("Expected Database.Password to be 'env-password', got '%s'", cfg.Database.Password)
	}
	if cfg.Providers.Azure.TenantID != "env-tenant-id" {
		t.Errorf("Expected Azure.TenantID to be 'env-tenant-id', got '%s'", cfg.Providers.Azure.TenantID)
	}
	if cfg.Providers.Azure.ClientID != "env-client-id" {
		t.Errorf("Expected Azure.ClientID to be 'env-client-id', got '%s'", cfg.Providers.Azure.ClientID)
	}
	if cfg.Providers.Azure.ClientSecret != "env-client-secret" {
		t.Errorf("Expected Azure.ClientSecret to be 'env-client-secret', got '%s'", cfg.Providers.Azure.ClientSecret)
	}
}

// TestConfigFileNotFound tests error handling when config file doesn't exist.
func TestConfigFileNotFound(t *testing.T) {
	_, err := Load("/nonexistent/path/config.yaml")
	if err == nil {
		t.Error("Expected error when config file doesn't exist")
	}
}

// TestInvalidYAML tests error handling for invalid YAML content.
func TestInvalidYAML(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	invalidYAML := `
feishu:
  app_id: "test"
  invalid yaml content here
    - broken
`

	if err := os.WriteFile(configPath, []byte(invalidYAML), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

	_, err := Load(configPath)
	if err == nil {
		t.Error("Expected error for invalid YAML content")
	}
}

// TestConfigPathFromEnv tests loading config path from environment variable.
func TestConfigPathFromEnv(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "custom-config.yaml")

	yamlContent := `
feishu:
  app_id: "env-path-test"
  app_secret: "secret"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

	os.Setenv("CONFIG_PATH", configPath)
	defer os.Unsetenv("CONFIG_PATH")

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

	if cfg.Feishu.AppID != "env-path-test" {
		t.Errorf("Expected Feishu.AppID to be 'env-path-test', got '%s'", cfg.Feishu.AppID)
	}
}

// TestEnvVarExpansionInYAML tests that environment variables in YAML are expanded.
func TestEnvVarExpansionInYAML(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
feishu:
  app_id: "${TEST_FEISHU_APP_ID}"
  app_secret: "${TEST_FEISHU_APP_SECRET}"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

	os.Setenv("TEST_FEISHU_APP_ID", "expanded-app-id")
	os.Setenv("TEST_FEISHU_APP_SECRET", "expanded-app-secret")
	defer os.Unsetenv("TEST_FEISHU_APP_ID")
	defer os.Unsetenv("TEST_FEISHU_APP_SECRET")

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

	if cfg.Feishu.AppID != "expanded-app-id" {
		t.Errorf("Expected Feishu.AppID to be 'expanded-app-id', got '%s'", cfg.Feishu.AppID)
	}
	if cfg.Feishu.AppSecret != "expanded-app-secret" {
		t.Errorf("Expected Feishu.AppSecret to be 'expanded-app-secret', got '%s'", cfg.Feishu.AppSecret)
	}
}

// TestCloudAccountsLoading tests loading cloud account configurations.
func TestCloudAccountsLoading(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
feishu:
  app_id: "test"
  app_secret: "test"

providers:
  aws:
    accounts:
      - id: "111111111111"
        name: "account-1"
        type: "prod"
      - id: "222222222222"
        name: "account-2"
        type: "test"

  gcp:
    projects:
      - id: "project-1"
        name: "Project One"
        type: "prod"

  azure:
    subscriptions:
      - id: "sub-1"
        name: "Subscription One"
        type: "prod"
        role_suffix: "-0"
      - id: "sub-2"
        name: "Subscription Two"
        type: "prod"
        role_suffix: "-1"

  alibaba:
    accounts:
      - id: "1234567890"
        name: "alibaba-account"
        type: "prod"
`

	if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
		t.Fatalf("Failed to write test config file: %v", err)
	}

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

	// Verify AWS accounts
	if len(cfg.Providers.AWS.Accounts) != 2 {
		t.Errorf("Expected 2 AWS accounts, got %d", len(cfg.Providers.AWS.Accounts))
	}
	if cfg.Providers.AWS.Accounts[0].ID != "111111111111" {
		t.Errorf("Expected first AWS account ID to be '111111111111', got '%s'", cfg.Providers.AWS.Accounts[0].ID)
	}

	// Verify GCP projects
	if len(cfg.Providers.GCP.Projects) != 1 {
		t.Errorf("Expected 1 GCP project, got %d", len(cfg.Providers.GCP.Projects))
	}

	// Verify Azure subscriptions with role suffix
	if len(cfg.Providers.Azure.Subscriptions) != 2 {
		t.Errorf("Expected 2 Azure subscriptions, got %d", len(cfg.Providers.Azure.Subscriptions))
	}
	if cfg.Providers.Azure.Subscriptions[0].RoleSuffix != "-0" {
		t.Errorf("Expected first Azure subscription role_suffix to be '-0', got '%s'", cfg.Providers.Azure.Subscriptions[0].RoleSuffix)
	}

	// Verify Alibaba accounts
	if len(cfg.Providers.Alibaba.Accounts) != 1 {
		t.Errorf("Expected 1 Alibaba account, got %d", len(cfg.Providers.Alibaba.Accounts))
	}
}
