package utils

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/astaxie/beego/orm"
	"gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
	"reflect"
	"strings"
)

// 更新指定表的几个列
func UpdateTableByMap(tabeleStruct interface{}, changeMap map[string]interface{}) error {
	if reflect.TypeOf(tabeleStruct).Kind() != reflect.Ptr {
		err := errors.New("UpdateTableByMap: tableStruct must ptr")
		log.Error(err)
		return err
	}
	if len(changeMap) < 1 {
		log.Info("changeMap is nil")
		return nil
	}
	o := orm.NewOrm()
	changeColumn := make([]string, 0, len(changeMap))
	for i, v := range changeMap {
		changeColumn = append(changeColumn, i)
		if err := SetStructValueByType(tabeleStruct, i, v); err != nil {
			log.Error(err, i, v)
			return err
		}
	}
	num, err := o.Update(tabeleStruct, changeColumn...)
	if err != nil {
		log.Error(err)
		return err
	}
	log.Info(fmt.Sprintf("UpdateTableByMap: table:%s effect records:%d column:%v values:%v", GetTableName(tabeleStruct), num, changeColumn, changeMap))
	return nil
}

func UpdateTableByMapWithOrmer(o orm.Ormer, tabeleStruct interface{}, changeMap map[string]interface{}) error {
	if reflect.TypeOf(tabeleStruct).Kind() != reflect.Ptr {
		err := errors.New("UpdateTableByMap: tableStruct must ptr")
		log.Error(err)
		return err
	}
	if len(changeMap) < 1 {
		log.Info("changeMap is nil")
		return nil
	}
	changeColumn := make([]string, 0, len(changeMap))
	for i, v := range changeMap {
		changeColumn = append(changeColumn, i)
		if err := SetStructValueByType(tabeleStruct, i, v); err != nil {
			log.Error(err, i, v)
			return err
		}
	}
	num, err := o.Update(tabeleStruct, changeColumn...)
	if err != nil {
		log.Error(err)
		return err
	}
	log.Info(fmt.Sprintf("UpdateTableByMap: table:%s effect records:%d column:%v", GetTableName(tabeleStruct), num, changeColumn))
	return nil
}

// 通过反射调用结构对应的TableName函数,达到返回表名的目的
func GetTableName(tableStruct interface{}) string {
	m := reflect.ValueOf(tableStruct).MethodByName("TableName")
	if m.IsValid() && m.Kind() == reflect.Func {
		re := m.Call(nil)
		for _, v := range re {
			if v.IsValid() {
				return v.String()
			}
		}
	}
	return "unknown"
}

// 通用事物提交sql结构体
type SqlData struct {
	Sql   string
	Param []interface{}
}

func ExecuteSqlByRoll(isCheck bool, sqlSlice ...*SqlData) bool {
	o := orm.NewOrm()
	var someError bool = false
	o.Begin()
	for i := range sqlSlice {
		if sqlSlice[i].Sql == "" {
			continue
		}
		log.Info("execute sql:", sqlSlice[i])
		if ret, err := o.Raw(sqlSlice[i].Sql, sqlSlice[i].Param...).Exec(); err != nil {
			log.Error(err)
			someError = true
		} else {
			num, _ := ret.RowsAffected()
			log.Debug("num:", num)
			if isCheck && num < 1 {
				someError = true
			}
		}
	}
	if someError {
		log.Error("出错,回滚事物")
		o.Rollback()
		return false
	} else {
		log.Info("成功,提交事物")
		o.Commit()
		return true
	}
	return true
}

//PrintLogSql 打印sql语句
func PrintLogSql(sql string, param ...interface{}) {
	format := `SQL EXCEUTE:[%s]-%s`
	parmformat := `[%v]`
	var p strings.Builder
	for i := range param {
		p.WriteString(fmt.Sprintf(parmformat, param[i]))
	}
	log.Debug(fmt.Sprintf(format, sql, p.String()))
}

//ExecuteQueryOne 执行原生sql查询单条记录;结果用结构体接收
func ExecuteQueryOne(result interface{}, sqlstr string, param ...interface{}) error {
	var err error
	o := orm.NewOrm()
	err = ExecuteQueryOneWithOrmer(o, result, sqlstr, param)
	return err
}

//ExecuteQueryOneWithOrmer 执行原生sql查询单条
func ExecuteQueryOneWithOrmer(o orm.Ormer, result interface{}, sqlstr string, param ...interface{}) error {
	PrintLogSql(sqlstr, param...)
	var err error
	err = o.Raw(sqlstr, param).QueryRow(result)
	if err != nil {
		return err
	}
	return nil
}

//ExecuteQuerySql 执行原生sql查询多条记录
func ExecuteQueryAll(result interface{}, sqlstr string, param ...interface{}) error {
	//PrintLogSql(sqlstr, param...)
	var err error
	o := orm.NewOrm()
	err = ExecuteQueryAllWithOrmer(o, result, sqlstr, param)
	return err
}

//ExecuteQueryOneWithOrmer 执行原生sql查询多条记录
func ExecuteQueryAllWithOrmer(o orm.Ormer, result interface{}, sqlstr string, param ...interface{}) error {
	PrintLogSql(sqlstr, param...)
	var (
		err error
	)
	_, err = o.Raw(sqlstr, param).QueryRows(result)
	if err != nil {
		return err
	}
	return nil
}

func ExecuteSQLWithOrmer(o orm.Ormer, sqlstr string, param ...interface{}) error {
	PrintLogSql(sqlstr, param...)
	var (
		err error
	)
	r, err := o.Raw(sqlstr, param...).Exec()
	if err != nil {
		return err
	}
	num, _ := r.RowsAffected()
	log.Debug(fmt.Sprintf("RowsAffected:%d", num))
	return nil
}

type SqlExcutor struct {
	table    string
	wherestr []string
	orderstr []string
	islimit  bool
	offset   int
	pagenum  int
}

func NewSqlExutor() *SqlExcutor {
	return &SqlExcutor{}
}

func (s *SqlExcutor) Table(str string) *SqlExcutor {
	s.table = str
	return s
}

func (s *SqlExcutor) Where(condition ...string) *SqlExcutor {
	if len(condition) <= 0 {
		return s
	}
	s.wherestr = append(s.wherestr, condition...)
	return s
}

func (s *SqlExcutor) Order(condition ...string) *SqlExcutor {
	if len(condition) <= 0 {
		return s
	}
	s.orderstr = append(s.orderstr, condition...)
	return s
}

func (s *SqlExcutor) Limit(page, pagenum int) *SqlExcutor {
	offset := 0
	if page > 0 {
		offset = (page - 1) * pagenum
	}
	s.islimit = true
	s.offset = offset
	s.pagenum = pagenum
	return s
}

func (s *SqlExcutor) WhereString() string {
	sql := bytes.NewBufferString("")
	if len(s.wherestr) > 0 {
		sql.WriteString(" where ")
		for i := range s.wherestr {
			if i != 0 {
				sql.WriteString(" AND ")
			}
			sql.WriteString(s.wherestr[i])
		}
	}
	return sql.String()
}