package cli

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

	"github.com/tidbcloud/aws-shared-provider/tools/cloud-instances/provider"
	"github.com/tidbcloud/aws-shared-provider/tools/cloud-instances/query"
)

type AliyunCredentials struct {
	AccessKey string
	SecretKey string
	Token     string
}

func ResolveProviders(engine *query.QueryEngine, providerFilter string) map[string]provider.CloudProvider {
	providers := make(map[string]provider.CloudProvider)
	if providerFilter == "all" {
		for name, p := range engine.Providers {
			providers[name] = p
		}
		return providers
	}
	if p, exists := engine.Providers[providerFilter]; exists {
		providers[providerFilter] = p
	}
	return providers
}

func ValidateAuth(engine *query.QueryEngine, opts query.FilterOptions) error {
	providers := ResolveProviders(engine, opts.Provider)
	for name := range providers {
		switch name {
		case "aws":
			// No auth required.
		case "aliyun":
			if os.Getenv("ALICLOUD_ACCESS_KEY") == "" || os.Getenv("ALICLOUD_SECRET_KEY") == "" {
				return fmt.Errorf("aliyun requires ALICLOUD_ACCESS_KEY and ALICLOUD_SECRET_KEY")
			}
		case "azure":
			if os.Getenv("AZURE_ACCESS_TOKEN") != "" {
				if os.Getenv("AZURE_SUBSCRIPTION_ID") == "" {
					return fmt.Errorf("azure requires AZURE_SUBSCRIPTION_ID when using AZURE_ACCESS_TOKEN")
				}
				continue
			}
			if os.Getenv("AZURE_TENANT_ID") == "" || os.Getenv("AZURE_CLIENT_ID") == "" || os.Getenv("AZURE_CLIENT_SECRET") == "" || os.Getenv("AZURE_SUBSCRIPTION_ID") == "" {
				return fmt.Errorf("azure requires AZURE_ACCESS_TOKEN or AZURE_TENANT_ID/AZURE_CLIENT_ID/AZURE_CLIENT_SECRET/AZURE_SUBSCRIPTION_ID")
			}
		case "gcp":
			if os.Getenv("GCP_PROJECT_ID") == "" {
				return fmt.Errorf("gcp requires GCP_PROJECT_ID")
			}
			if os.Getenv("GCP_ACCESS_TOKEN") != "" {
				continue
			}
			credPath := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
			if credPath == "" {
				return fmt.Errorf("gcp requires GCP_ACCESS_TOKEN or GOOGLE_APPLICATION_CREDENTIALS")
			}
			if _, err := os.Stat(credPath); err != nil {
				return fmt.Errorf("gcp credentials file not found: %v", err)
			}
		default:
			return fmt.Errorf("unknown provider: %s", name)
		}
	}
	return nil
}

func LoadAliyunCredentials() (AliyunCredentials, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return AliyunCredentials{}, err
	}
	configPath := filepath.Join(home, ".aliyun", "config.json")
	data, err := os.ReadFile(configPath)
	if err != nil {
		return AliyunCredentials{}, fmt.Errorf("aliyun config not found: %v", err)
	}

	var raw struct {
		Current  string                   `json:"current"`
		Profiles []map[string]interface{} `json:"profiles"`
	}
	if err := json.Unmarshal(data, &raw); err != nil {
		return AliyunCredentials{}, fmt.Errorf("failed to parse aliyun config: %v", err)
	}
	if len(raw.Profiles) == 0 {
		return AliyunCredentials{}, fmt.Errorf("aliyun config has no profiles")
	}

	profileName := os.Getenv("ALICLOUD_PROFILE")
	if profileName == "" {
		profileName = raw.Current
	}

	var profile map[string]interface{}
	if profileName != "" {
		for _, p := range raw.Profiles {
			if name, _ := p["name"].(string); name == profileName {
				profile = p
				break
			}
		}
	}
	if profile == nil {
		profile = raw.Profiles[0]
	}

	accessKey := getString(profile, "access_key_id", "accessKeyId", "accessKey", "AccessKeyId")
	secretKey := getString(profile, "access_key_secret", "accessKeySecret", "secret_key", "AccessKeySecret")
	token := getString(profile, "security_token", "sts_token", "SecurityToken", "StsToken")

	return AliyunCredentials{
		AccessKey: accessKey,
		SecretKey: secretKey,
		Token:     token,
	}, nil
}

func getString(profile map[string]interface{}, keys ...string) string {
	for _, key := range keys {
		if val, ok := profile[key]; ok {
			if s, ok := val.(string); ok {
				return strings.TrimSpace(s)
			}
		}
	}
	return ""
}
