package app

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"GoNavi-Wails/internal/connection"
	"GoNavi-Wails/internal/secretstore"
)

func (a *App) resolveConnectionSecrets(config connection.ConnectionConfig) (connection.ConnectionConfig, error) {
	if strings.TrimSpace(config.ID) == "" {
		return config, nil
	}

	repo := newSavedConnectionRepository(a.configDir, a.secretStore)
	view, err := repo.Find(config.ID)
	if err != nil {
		if shouldFallbackToInlineConnectionSecrets(config, err) {
			return config, nil
		}
		return config, normalizeConnectionSecretResolutionError(config, err)
	}

	base := config
	if connectionMetadataLooksEmpty(base) {
		base = view.Config
	}
	bundle, err := repo.loadSecretBundle(view)
	if err != nil {
		if shouldFallbackToInlineConnectionSecrets(config, err) {
			return mergeInlineConnectionSecrets(base, config), nil
		}
		return base, normalizeConnectionSecretResolutionError(base, err)
	}
	resolved := mergeConnectionSecretBundleIntoConfig(base, bundle)
	resolved.ID = view.ID

	return resolved, nil
}

func shouldFallbackToInlineConnectionSecrets(config connection.ConnectionConfig, err error) bool {
	if err == nil || !connectionConfigCarriesInlineSecrets(config) || secretstore.IsUnavailable(err) {
		return false
	}
	if errors.Is(err, os.ErrNotExist) {
		return true
	}
	lower := strings.ToLower(strings.TrimSpace(err.Error()))
	return strings.Contains(lower, "saved connection not found:")
}

func connectionConfigCarriesInlineSecrets(config connection.ConnectionConfig) bool {
	return strings.TrimSpace(config.Password) != "" ||
		strings.TrimSpace(config.SSH.Password) != "" ||
		strings.TrimSpace(config.Proxy.Password) != "" ||
		strings.TrimSpace(config.HTTPTunnel.Password) != "" ||
		strings.TrimSpace(config.MySQLReplicaPassword) != "" ||
		strings.TrimSpace(config.MongoReplicaPassword) != "" ||
		strings.TrimSpace(config.RedisSentinelPassword) != "" ||
		strings.TrimSpace(config.URI) != "" ||
		strings.TrimSpace(config.DSN) != ""
}

func mergeInlineConnectionSecrets(base connection.ConnectionConfig, inline connection.ConnectionConfig) connection.ConnectionConfig {
	merged := base
	if strings.TrimSpace(inline.Password) != "" {
		merged.Password = inline.Password
	}
	if strings.TrimSpace(inline.SSH.Password) != "" {
		merged.SSH.Password = inline.SSH.Password
	}
	if strings.TrimSpace(inline.Proxy.Password) != "" {
		merged.Proxy.Password = inline.Proxy.Password
	}
	if strings.TrimSpace(inline.HTTPTunnel.Password) != "" {
		merged.HTTPTunnel.Password = inline.HTTPTunnel.Password
	}
	if strings.TrimSpace(inline.MySQLReplicaPassword) != "" {
		merged.MySQLReplicaPassword = inline.MySQLReplicaPassword
	}
	if strings.TrimSpace(inline.MongoReplicaPassword) != "" {
		merged.MongoReplicaPassword = inline.MongoReplicaPassword
	}
	if strings.TrimSpace(inline.RedisSentinelPassword) != "" {
		merged.RedisSentinelPassword = inline.RedisSentinelPassword
	}
	if strings.TrimSpace(inline.URI) != "" {
		merged.URI = inline.URI
	}
	if strings.TrimSpace(inline.DSN) != "" {
		merged.DSN = inline.DSN
	}
	return merged
}

func normalizeConnectionSecretResolutionError(config connection.ConnectionConfig, err error) error {
	if err == nil {
		return nil
	}

	lower := strings.ToLower(strings.TrimSpace(err.Error()))
	switch {
	case strings.Contains(lower, "saved connection not found:"):
		if connectionMetadataLooksEmpty(config) {
			return fmt.Errorf("未找到已保存连接，可能已被删除，请刷新后重试")
		}
		return fmt.Errorf("未找到当前连接对应的已保存密文，请重新填写密码并保存后再试")
	case errors.Is(err, os.ErrNotExist):
		return fmt.Errorf("未找到当前连接对应的已保存密文，请重新填写密码并保存后再试")
	case strings.Contains(lower, "secret store unavailable"):
		return fmt.Errorf("系统密文存储当前不可用，请检查系统钥匙串或凭据管理器后再试")
	default:
		return err
	}
}

func connectionMetadataLooksEmpty(config connection.ConnectionConfig) bool {
	return strings.TrimSpace(config.Type) == "" &&
		strings.TrimSpace(config.Host) == "" &&
		config.Port == 0 &&
		strings.TrimSpace(config.User) == "" &&
		strings.TrimSpace(config.Database) == "" &&
		strings.TrimSpace(config.DSN) == "" &&
		strings.TrimSpace(config.URI) == "" &&
		len(config.Hosts) == 0
}

func mergeConnectionSecretBundleIntoConfig(config connection.ConnectionConfig, bundle connectionSecretBundle) connection.ConnectionConfig {
	merged := config
	if strings.TrimSpace(merged.Password) == "" {
		merged.Password = bundle.Password
	}
	if strings.TrimSpace(merged.SSH.Password) == "" {
		merged.SSH.Password = bundle.SSHPassword
	}
	if strings.TrimSpace(merged.Proxy.Password) == "" {
		merged.Proxy.Password = bundle.ProxyPassword
	}
	if strings.TrimSpace(merged.HTTPTunnel.Password) == "" {
		merged.HTTPTunnel.Password = bundle.HTTPTunnelPassword
	}
	if strings.TrimSpace(merged.MySQLReplicaPassword) == "" {
		merged.MySQLReplicaPassword = bundle.MySQLReplicaPassword
	}
	if strings.TrimSpace(merged.MongoReplicaPassword) == "" {
		merged.MongoReplicaPassword = bundle.MongoReplicaPassword
	}
	if strings.TrimSpace(merged.RedisSentinelPassword) == "" {
		merged.RedisSentinelPassword = bundle.RedisSentinelPassword
	}
	if strings.TrimSpace(merged.URI) == "" {
		merged.URI = bundle.OpaqueURI
	}
	if strings.TrimSpace(merged.DSN) == "" {
		merged.DSN = bundle.OpaqueDSN
	}
	return merged
}
