package db

import (
	"database/sql"
	"fmt"

	sq "github.com/Masterminds/squirrel"
	"github.com/tidbcloud/config-center/pkg/sqlbuilder"
)

// Insert.
func (db *realDB) InsertConfigDicts(items []*ConfigDictRow) error {
	_, err := db.NamedExec(`INSERT INTO
    param_dict (
        cluster_type,
        config_type,
        version,
        param_key,
        default_value,
        type,
        hot_reloadable,
		risk_level,
        description,
        need_template,
		validation,
		updated_by
    )
VALUES
    (
		:cluster_type,
        :config_type,
        :version,
        :param_key,
        :default_value,
        :type,
        :hot_reloadable,
		:risk_level,
        :description,
        :need_template,
		:validation,
		:updated_by
    )`, items)
	if err != nil {
		fmt.Println("error when insert", err)
	}
	return err
}

// Get.
func (db *realDB) GetConfigDict(clusterType, configType, paramKey, version string) (*ConfigDictRow, error) {
	ConfigDict := ConfigDictRow{}
	err := db.Get(&ConfigDict, "SELECT * FROM param_dict WHERE cluster_type = ? AND config_type = ? AND param_key = ? AND version = ?", clusterType, configType, paramKey, version)
	if err == sql.ErrNoRows {
		return &ConfigDict, ErrNotFound
	}
	return &ConfigDict, err
}

type ListConfigDictOption struct {
	// Required
	ClusterType string
	ConfigType  *string

	// Filter
	Version      *string
	ParamKeyIn   []string
	ParamKeyLike *string

	Page     int64
	PageSize int64
}

func buildCondition(opt *ListConfigDictOption) []sqlbuilder.Cond {
	conds := []sqlbuilder.Cond{
		sqlbuilder.Equal("cluster_type", opt.ClusterType),
		sqlbuilder.Raw("delete_time IS NULL"),
	}
	if opt.ConfigType != nil {
		conds = append(conds, sqlbuilder.Equal("config_type", *opt.ConfigType))
	}
	if opt.Version != nil {
		conds = append(conds, sqlbuilder.Equal("version", *opt.Version))
	}
	if len(opt.ParamKeyIn) > 0 {
		conds = append(conds, sqlbuilder.In("param_key", opt.ParamKeyIn))
	}
	if opt.ParamKeyLike != nil {
		conds = append(conds, sqlbuilder.Like("param_key", fmt.Sprintf("%%%s%%", *opt.ParamKeyLike)))
	}
	return conds
}

// List.
func (db *realDB) ListConfigDict(opt *ListConfigDictOption) ([]*ConfigDictRow, error) {
	ConfigDict := []*ConfigDictRow{}

	conds := buildCondition(opt)
	var sql string
	if opt.Page == 0 || opt.PageSize == 0 {
		sql = fmt.Sprintf("select * from param_dict where %s", sqlbuilder.And(conds...))
	} else {
		sql = fmt.Sprintf("select * from param_dict where %s order by param_key limit %d offset %d", sqlbuilder.And(conds...), opt.PageSize, (opt.Page-1)*opt.PageSize)
	}
	err := db.Select(&ConfigDict, sql)
	return ConfigDict, err
}

func (db *realDB) GetTotalParamNum(opt *ListConfigDictOption) (int64, error) {
	var count int64
	conds := buildCondition(opt)
	sql := fmt.Sprintf("SELECT count(*) FROM param_dict where %s", sqlbuilder.And(conds...))
	err := db.Get(&count, sql)
	return count, err
}

// Update.
func (db *realDB) UpdateConfigDict(item *ConfigDictRow) error {
	_, err := db.Exec(`Update
    param_dict
set
        default_value = ?,
        hot_reloadable = ?,
        risk_level = ?,
        description = ?,
        need_template = ?,
		validation = ?,
		updated_by = ?
where
    (
        cluster_type = ?
        and config_type = ?
        and version = ?
        and param_key = ?
    )`,
		item.DefaultValue,
		item.HotReloadable,
		item.RiskLevel,
		item.Description,
		item.NeedTemplate,
		item.Validation,
		item.UpdatedBy,
		item.ClusterType,
		item.ConfigType,
		item.Version,
		item.ParamKey)
	return err
}

// Support Delete ?

func (db *realDB) ListVersionsFromConfigDict(clusterType, configType string) ([]string, error) {
	var versions []string

	conds := sq.And{
		sq.Eq{"cluster_type": clusterType},
		sq.Expr("delete_time IS NULL"),
	}
	if configType != "" {
		conds = append(conds, sq.Eq{"config_type": configType})
	}
	sql, args, err := sq.Select("DISTINCT version").From("param_dict").Where(conds).ToSql()
	if err != nil {
		return nil, err
	}
	err = db.Select(&versions, sql, args...)
	return versions, err
}

func (db *realDB) DeleteDictItems(clusterType, configType string, version *string) (int64, error) {
	builder := sq.Delete("param_dict").
		Where(sq.Eq{"cluster_type": clusterType}).
		Where(sq.Eq{"config_type": configType})
	if version != nil && *version != "" {
		builder = builder.Where(sq.Eq{"version": *version})
	}
	query, args, err := builder.ToSql()
	if err != nil {
		return 0, err
	}
	res, err := db.Exec(query, args...)
	if err != nil {
		return 0, err
	}
	count, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}
	return count, nil
}
