package dbaas

import (
	"fmt"
	"log"
	"strings"
	"time"

	"go.yaml.in/yaml/v3"

	"github.com/tidbcloud/AgentTeam/desktop/internal/crypto"
)

// Known cloud vendors.
const (
	VendorAWS     = "aws"
	VendorGCP     = "gcp"
	VendorAliyun  = "aliyun"
	VendorPrivate = "private"
)

// AllVendors contains the supported cloud vendors.
var AllVendors = []string{VendorAWS, VendorGCP, VendorAliyun, VendorPrivate}

// Known regions per vendor.
var VendorRegions = map[string][]string{
	VendorAWS:     {"us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"},
	VendorGCP:     {"us-central1", "us-east1", "europe-west1", "asia-east1"},
	VendorAliyun:  {"cn-beijing", "cn-shanghai", "cn-hangzhou", "cn-shenzhen"},
	VendorPrivate: {"default"},
}

// DatabaseStatus tracks the lifecycle of a database instance.
type DatabaseStatus string

const (
	DatabaseStatusCreating DatabaseStatus = "creating"
	DatabaseStatusRunning  DatabaseStatus = "running"
	DatabaseStatusStopped  DatabaseStatus = "stopped"
	DatabaseStatusFailed   DatabaseStatus = "failed"
)

// Database defines a database instance created via a DBaaS provider.
type Database struct {
	ID        string         `yaml:"id" json:"id"`
	Name      string         `yaml:"name,omitempty" json:"name,omitempty"`
	ServiceID string         `yaml:"service_id" json:"service_id"`
	Provider  string         `yaml:"provider" json:"provider"`
	Vendor    string         `yaml:"vendor" json:"vendor"`
	Region    string         `yaml:"region" json:"region"`
	Status    DatabaseStatus `yaml:"status" json:"status"`
	Host      string         `yaml:"host,omitempty" json:"host,omitempty"`
	Port      int            `yaml:"port,omitempty" json:"port,omitempty"`
	DBName    string         `yaml:"db_name,omitempty" json:"db_name,omitempty"`
	Username  string         `yaml:"username,omitempty" json:"username,omitempty"`
	Password  string         `yaml:"password,omitempty" json:"password,omitempty"`
	DSN       string         `yaml:"dsn,omitempty" json:"dsn,omitempty"`
	CreatedAt *time.Time     `yaml:"created_at,omitempty" json:"created_at,omitempty"`
}

type databaseAlias Database

func (d Database) MarshalYAML() (interface{}, error) {
	alias := databaseAlias(d)
	if alias.Password != "" && crypto.KeysSet() {
		encrypted, err := crypto.Encrypt(alias.Password)
		if err != nil {
			log.Printf("dbaas: encrypt password for %q: %v", d.ID, err)
			return alias, nil
		}
		alias.Password = encrypted
	}
	return alias, nil
}

func (d *Database) UnmarshalYAML(value *yaml.Node) error {
	var alias databaseAlias
	if err := value.Decode(&alias); err != nil {
		return err
	}
	*d = Database(alias)
	return d.decryptPassword()
}

func (d *Database) decryptPassword() error {
	if d.Password != "" && crypto.KeysSet() && crypto.IsEncrypted(d.Password) {
		decrypted, err := crypto.Decrypt(d.Password)
		if err != nil {
			log.Printf("dbaas: decrypt password for %q: %v", d.ID, err)
			return err
		}
		d.Password = decrypted
	}
	return nil
}

// ValidateDatabase checks a database instance for correctness.
func ValidateDatabase(d Database) error {
	var errs []string
	if strings.TrimSpace(d.ID) == "" {
		errs = append(errs, "database ID must not be empty")
	}
	if strings.TrimSpace(d.ServiceID) == "" {
		errs = append(errs, "service ID must not be empty")
	}
	providerValid := false
	for _, p := range AllProviders {
		if d.Provider == p {
			providerValid = true
			break
		}
	}
	if !providerValid {
		errs = append(errs, fmt.Sprintf("unsupported provider %q", d.Provider))
	}
	if len(errs) > 0 {
		return fmt.Errorf("validation failed: %s", strings.Join(errs, "; "))
	}
	return nil
}

// NormalizeDatabase trims whitespace on fields.
func NormalizeDatabase(d Database) Database {
	d.ID = strings.TrimSpace(d.ID)
	d.Name = strings.TrimSpace(d.Name)
	d.ServiceID = strings.TrimSpace(d.ServiceID)
	d.Provider = strings.TrimSpace(d.Provider)
	d.Vendor = strings.TrimSpace(d.Vendor)
	d.Region = strings.TrimSpace(d.Region)
	return d
}
