package db

import (
	"bytes"
	"fmt"

	"github.com/BurntSushi/toml"
	"github.com/jmoiron/sqlx"
	"github.com/tidbcloud/config-center/internal/config"
	"github.com/tidbcloud/config-center/pkg/utils"
)

var db *sqlx.DB

func Init(c config.DatabaseConfig) {
	var err error
	db, err = sqlx.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4", c.User, c.Password, c.Host, c.Port, c.Database))
	if err != nil {
		panic(err)
	}
	if err = db.Ping(); err != nil {
		panic(err)
	}
}

func CreateConfigurationTemplate(template Template) (int64, error) {
	tx, err := db.Beginx()
	if err != nil {
		return 0, err
	}

	res, err := tx.Exec("INSERT INTO templates (name) VALUES (?)", template.TemplateName)
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	id, err := res.LastInsertId()
	if err != nil {
		tx.Rollback()
		return 0, err
	}

	var values []TemplateValueRow
	for k, v := range template.Data {
		values = append(values, TemplateValueRow{
			TemplateID: id,
			Key:        k,
			Value:      NewAnyValue(v.Value),
			Comment:    v.Comment,
		})
	}

	_, err = tx.NamedExec("INSERT INTO template_values (template_id, key, value, comment) VALUES (template_id, :key, :value, comment)", values)
	if err != nil {
		tx.Rollback()
		return 0, err
	}
	return 0, tx.Commit()
}

// it is safe to call getTemplateValues outside of TX since it is forbidden to update a template after creation
func getTemplateValues(templateID int64) ([]TemplateValueRow, error) {
	templateValues := []TemplateValueRow{}
	err := db.Select(&templateValues, "SELECT `key`, value FROM template_values WHERE template_id = ?", templateID)
	return templateValues, err
}

func CreateClusterConfigurationFile(f ClusterConfigFile) error {
	tx, err := db.Beginx()
	if err != nil {
		return err
	}
	templateValues := []TemplateValueRow{}
	err = tx.Select(&templateValues, "SELECT `key`, value FROM template_values WHERE template_id = ?", f.TemplateID)
	if err != nil {
		tx.Rollback()
		return err
	}

	customConfigs := []ClusterConfigParamRow{}
	for k, v := range f.CustomConfig {
		customConfigs = append(customConfigs, ClusterConfigParamRow{
			ClusterType: f.ClusterType,
			ClusterID:   f.ClusterID,
			Filename:    f.Filename,
			ConfigurationItem: ConfigurationItem{
				Key:     k,
				Value:   NewAnyValue(v.Value),
				Comment: v.Comment,
			},
		})
	}
	// res.RowsAffected() 可能不一样吗
	_, err = tx.NamedExec("INSERT INTO cluster_custom_configs (cluster_type, cluster_id, filename, `key`, value, comment) VALUES(:cluster_type, :cluster_id, :filename, :key, :value, :comment)", customConfigs)
	if err != nil {
		tx.Rollback()
		return err
	}

	fileText, err := calculateFileText("toml", templateValues, customConfigs)
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = tx.Exec("INSERT INTO cluster_configuration_files (cluster_type, cluster_id, filename, data) VALUES (?, ?, ?, ?)", f.ClusterType, f.ClusterID, f.Filename, fileText)
	return err
}

func GetClusterConfigurationFile(clusterType, clusterID, filename string) (*ClusterConfigFile, error) {
	tx, err := db.Beginx()
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	var info ClusterConfigInfoRow
	err = tx.Get(&info, "SELECT format, template_id, file_text FROM cluster_config_info WHERE cluster_type = ? AND cluster_id = ? AND filename = ?", clusterType, clusterID, filename)
	if err != nil {
		return nil, err
	}

	var customConfigRows []ClusterConfigParamRow
	err = tx.Select(&customConfigRows, "SELECT `key`, value, comment FROM cluster_custom_configs WHERE cluster_type = ? AND cluster_id = ? AND filename = ?", clusterType, clusterID, filename)
	if err != nil {
		return nil, err
	}
	customConfigs := make(map[string]ConfigItem)
	for _, row := range customConfigRows {
		customConfigs[row.Key] = ConfigItem{
			Value:   row.Value.Raw,
			Comment: row.Comment,
		}
	}

	return &ClusterConfigFile{
		ClusterType:  clusterType,
		ClusterID:    clusterID,
		Filename:     filename,
		Format:       info.Format,
		TemplateID:   info.TemplateID,
		CustomConfig: customConfigs,
		FileText:     "",
	}, nil
}

func calculateFileText(format string, templateValueRows []TemplateValueRow, customConfigRows []ClusterConfigParamRow) (string, error) {
	templateValues := map[string]any{}
	for _, row := range templateValueRows {
		templateValues[row.Key] = row.Value.Raw
	}
	customConfigs := map[string]any{}
	for _, row := range customConfigRows {
		customConfigs[row.Key] = row.Value.Raw
	}
	configs := utils.MergeConfig(templateValues, customConfigs)
	switch format {
	case "toml":
		buf := bytes.NewBuffer(nil)
		err := toml.NewEncoder(buf).Encode(configs)
		return buf.String(), err
	default:
		return "", nil
	}
}
