package orm

import (
	"PPOSUtils/utils"
	"errors"
	"reflect"
	"regexp"
	"strings"

	"xorm.io/xorm"
)

func NewSession(engine *xorm.Engine) *Session {
	_session := &Session{
		injectionMp:   utils.NewMP(),
		Session:       engine.NewSession(),
		sessionSlice:  []*Session{},
		sessionStatus: new(sessionStatus).commitOrRollback(),
	}
	return _session
}

type sessionStatus struct {
	isClose                bool
	isCommitedOrRollbacked bool //
}

func (that *sessionStatus) begin() *sessionStatus {
	that.isCommitedOrRollbacked = false
	return that
}

func (that *sessionStatus) commitOrRollback() *sessionStatus {
	that.isCommitedOrRollbacked = true
	return that
}

func (that *sessionStatus) getCommitOrRollback() bool {
	return that.isCommitedOrRollbacked
}

func (that *sessionStatus) close() *sessionStatus {
	that.isClose = true
	return that
}

func (that *sessionStatus) isClosed() bool {
	return that.isClose
}

// Session 自己封装的session
type Session struct {
	isClone         bool
	tableMode       interface{}    //当前数据表模型
	injectionStatus bool           //是否已经注入到当前SQL
	injectionMp     utils.Mp       //注入的字段
	sessionStatus   *sessionStatus //事务提交的状态，采用指针方式clone时实际指向同一个
	sessionSlice    []*Session
	*xorm.Session   //xorm的session对象，采用指针方式clone时实际指向同一个
}

// IsBegin ...
func (that *Session) IsBegin() bool {
	return !that.sessionStatus.isCommitedOrRollbacked
}

// resetInjection 设置注入的字段
func (that *Session) resetInjection(injectionMp utils.Mp) {
	that.injectionMp = utils.NewMP()
	for _k, _v := range injectionMp {
		that.injectionMp.Set(_k, _v)
	}
}

func (that *Session) DelInjection(slice ...string) *Session {
	for _, _v := range slice {
		if _, _ok := that.injectionMp[_v]; _ok {
			delete(that.injectionMp, _v)
		}
	}
	return that
}

// Init 初始化
func (that *Session) Init(tableMode interface{}, injectionMp utils.Mp) {
	that.injectionStatus = false
	that.resetInjection(injectionMp)
	that.newTableMode(tableMode)
}

// injectionConditions 查询条件，自动加上注入字段
func (that *Session) injectionConditions() *Session {
	//隔离字段
	if that.injectionStatus {
		return that
	}
	//
	that.injectionStatus = true
	if that.tableMode == nil {
		for _field, _value := range that.injectionMp {
			that.Where(_field+" = ? ", _value)
		}
		return that
	}
	//
	t := reflect.TypeOf(that.tableMode)
	that.injectionTag(t.Elem())
	return that
}

// injectionTag 解析数据表字段
func (that *Session) injectionTag(t reflect.Type) {
	fieldNum := t.NumField()
	for i := 0; i < fieldNum; i++ {
		_f := t.Field(i)
		if _f.Tag.Get("xorm") == "extends" {
			that.injectionTag(_f.Type)
		}
		_injection := _f.Tag.Get("injection")
		if _injection != "" {
			if that.injectionMp.Has(_injection) {
				that.Where(_injection+" = ? ", that.injectionMp.Get(_injection))
			}
		}
	}
}

// checkTableMode 检测数据模型是否为空
func (that *Session) checkTableMode() error {
	if that.tableMode == nil {
		return errors.New("must do Session::New(tableMode interface{}) first")
	}
	return nil
}

// newTableMode 设置数据表模型
func (that *Session) newTableMode(tableMode interface{}) error {
	if that.injectionStatus {
		return errors.New("must do Session::New(tableMode interface{}) first")
	}
	that.tableMode = tableMode
	return nil
}

//regexpCode 将字段两边加上反引号
//@params query 执行的字符串
//@params flag  取值：1字段、 默认是条件
func (that *Session) regexpCodeString(query interface{}, flag ...int) string {
	return utils.ToString(that.regexpCode(query, flag...))
}
func (that *Session) regexpCode(query interface{}, flag ...int) interface{} {
	//达梦提供了odbc的连接，这样可以使用类似mssql的odbc连接方式连接达梦数据库
	if that != nil && that.Engine().Dialect().URI().DBType != "mssql" {
		return query
	}
	_query := utils.ToString(query)
	//select (count(*) as total,a,b,c) => count(*) as total,`a`,`b`,`c`
	if len(flag) > 0 && flag[0] == 1 {
		_query = strings.TrimLeft(_query, " ")
		_query = strings.TrimRight(_query, " ")
		_arrStr := strings.Split(_query, ",")
		_newStr := ""
		for _, _v := range _arrStr {
			if strings.Contains(_v, "count(*) as") {
				_newStr += _v + ","
				continue
			}
			_newStr += "`" + _v + "`,"
		}
		_newStr = strings.TrimRight(_newStr, ",")
		return _newStr
	}
	//where (abc = ?) => where (`abc` = ?)
	_reRule := regexp.MustCompile(`(\b[a-zA-Z_0-9]+\b)(\s*=\s*\??\s*)`)
	_newStr := _reRule.ReplaceAllStringFunc(_query, func(match string) string {
		segments := _reRule.FindStringSubmatch(match)
		if len(segments) > 0 {
			return _reRule.ReplaceAllString(segments[0], "`$1`$2")
		}
		return match
	})
	if _query != _newStr {
		return _newStr
	}
	//where (abc like ? ) => where (`abc` like ?)
	_reRule = regexp.MustCompile(`(\b\w+\b)\s+LIKE\s+(\?)`)
	_newStr = _reRule.ReplaceAllString(_query, "`$1` LIKE $2")
	if _query != _newStr {
		return _newStr
	}
	//原样返回
	return _query
}

//查询
func (that *Session) Select(str string) *Session {
	that.injectionConditions()
	that.Session.Select(that.regexpCodeString(str, 1))
	return that
}

func (that *Session) Where(query interface{}, args ...interface{}) *Session {
	that.injectionConditions()
	that.Session.Where(that.regexpCode(query), that._inArgs(args...)...)
	return that
}
func (that *Session) Cols(columns ...string) *Session {
	that.injectionConditions()
	that.Session.Cols(columns...)
	return that
}
func (that *Session) OrderBy(order string) *Session {
	that.injectionConditions()
	that.Session.OrderBy(order)
	return that
}

func (that *Session) Or(query interface{}, args ...interface{}) *Session {
	that.injectionConditions()
	that.Session.Or(that.regexpCode(query), that._inArgs(args...)...)
	return that
}

func (that *Session) And(query interface{}, args ...interface{}) *Session {
	that.injectionConditions()
	that.Session.And(that.regexpCode(query), that._inArgs(args...)...)
	return that
}

func (that *Session) In(column string, args ...interface{}) *Session {
	that.injectionConditions()
	that.Session.In(column, that._inArgs(args...)...)
	return that
}

func (that *Session) _inArgs(args ...interface{}) []interface{} {
	_args := make([]interface{}, 0)
	for _, _v := range args {
		switch _t := _v.(type) {
		case []int64:
			_args = append(_args, that._inArgs(utils.NewSliceInt64(_t...).ToInterface()...)...)
		case []string:
			_args = append(_args, that._inArgs(utils.NewSliceString(_t...).ToInterface()...)...)
		case []int:
			_args = append(_args, that._inArgs(utils.NewSliceInt(_t...).ToInterface()...)...)
		case []interface{}:
			_args = append(_args, that._inArgs(_t...)...)
		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, string:
			_args = append(_args, utils.ToString(_v, true))
		default:
			_args = append(_args, _t)
		}
	}
	return _args
}

func (that *Session) OrIn(column string, args ...interface{}) *Session {
	that.injectionConditions()
	_params := ""
	_func := func(data interface{}) {
		if _data, _ok := data.([]int); _ok {
			for _, _val := range _data {
				_params += utils.ToString(_val) + ","
			}
		}
		if _data, _ok := data.([]int64); _ok {
			for _, _val := range _data {
				_params += utils.ToString(_val) + ","
			}
		}
		if _data, _ok := data.([]interface{}); _ok {
			for _, _val := range _data {
				_params += utils.ToString(_val) + ","
			}
		}
		if _data, _ok := data.([]string); _ok {
			for _, _val := range _data {
				_params += utils.ToString(_val) + ","
			}
		}
	}
	for _, _val := range args {
		switch _val.(type) {
		case []int:
			_func(_val)
		case []int64:
			_func(_val)
		case []interface{}:
			_func(_val)
		case []string:
			_func(_val)
		}
	}
	_p := strings.Trim(strings.TrimRight(_params, ","), "\"")
	that.Session.Or(column + " IN" + " (" + _p + ")")
	return that
}

func (that *Session) Limit(limit int, start ...int) *Session {
	that.injectionConditions()
	that.Session.Limit(limit, start...)
	return that
}

func (that *Session) GroupBy(keys string) *Session {
	that.injectionConditions()
	that.Session.GroupBy(that.regexpCodeString(keys, 1))
	return that
}

// Incr ...
func (that *Session) Incr(column string, arg ...interface{}) *Session {
	that.injectionConditions()
	that.Session.Incr(column, arg...)
	return that
}

// Decr ...
func (that *Session) Decr(column string, arg ...interface{}) *Session {
	that.injectionConditions()
	that.Session.Decr(column, arg...)
	return that
}

// Desc 倒序排列
func (that *Session) Desc(colNames ...string) *Session {
	that.injectionConditions()
	that.Session.Desc(colNames...)
	return that
}

// Asc 顺序排列
func (that *Session) Asc(colNames ...string) *Session {
	that.injectionConditions()
	that.Session.Asc(colNames...)
	return that
}

// Count 统计
func (that *Session) Count(bean ...interface{}) (int64, error) {
	that.injectionConditions()
	return that.Session.Count(bean...)
}

// Get 查询单条记录
func (that *Session) Get(bean interface{}) (bool, error) {
	if _err := that.checkTableMode(); _err != nil {
		return false, _err
	}
	that.injectionConditions()
	_b, _err := that.Session.Get(bean)
	that.tableMode = nil
	return _b, _err
}

// Find 查询多条记录
func (that *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
	if _err := that.checkTableMode(); _err != nil {
		return _err
	}
	that.injectionConditions()
	_result := that.Session.Find(rowsSlicePtr, condiBean...)
	that.tableMode = nil
	return _result
}

// Exist 是否存在
func (that *Session) Exist(rowsSlicePtr interface{}) (bool, error) {
	if _err := that.checkTableMode(); _err != nil {
		return false, _err
	}
	that.injectionConditions()
	_bool, _err := that.Session.Exist(rowsSlicePtr)
	that.tableMode = nil
	return _bool, _err
}

// Update 更新
func (that *Session) Update(beans interface{}, condiBean ...interface{}) (int64, error) {
	if _err := that.checkTableMode(); _err != nil {
		return 0, _err
	}
	that.injectionConditions()
	_n, _err := that.Session.Update(beans, condiBean...)
	that.tableMode = nil
	return _n, _err
}

// AddMeeting ...
func (that *Session) AddMeeting(beans interface{}, cols ...string) (int64, error) {
	if _err := that.checkTableMode(); _err != nil {
		return 0, _err
	}
	_mode := beans
	t, v := reflect.TypeOf(_mode), reflect.ValueOf(_mode)
	//递归插入隔离字段
	that.injectionTagInsert(t.Elem(), v.Elem(), 0)
	//检测默认值字段
	that.parseOmit(_mode, cols...)
	_b, _err := that.Session.InsertOne(_mode)
	that.tableMode = nil
	return _b, _err
}

func (that *Session) parseOmit(mode interface{}, cols ...string) {
	t := reflect.TypeOf(mode)
	v := reflect.ValueOf(mode)
	t1 := t.Elem()
	t2 := v.Elem()
	_colsMap := utils.NewSliceString(cols...)
	for _k, _v := range *_colsMap {
		if strings.Count(_v, ",") > 0 {
			_nk := strings.Split(_v, ",")
			*_colsMap = append((*_colsMap)[:_k], (*_colsMap)[_k+1:]...)
			*_colsMap = append(*_colsMap, _nk...)
		}
	}
	_omitColsMap := utils.NewSliceString()
	_fieldNum := t1.NumField()
	for i := 0; i < _fieldNum; i++ {
		_n := t1.Field(i).Name
		_j := t1.Field(i).Tag.Get("json")
		_x := t1.Field(i).Tag.Get("xorm")
		_fv := t2.Field(i)
		if _colsMap.In(_j) {
			switch _fv.Kind().String() {
			case "string":
				if v.Elem().FieldByName(_n).String() == "" {
					_omitColsMap.Append(_j, true)
				}
			case "int", "int8", "int16", "int32", "int64":
				if v.Elem().FieldByName(_n).Int() == 0 {
					_omitColsMap.Append(_j, true)
				}
			}
		}
		if strings.Contains(_x, "default") {
			switch _fv.Kind().String() {
			case "string":
				if v.Elem().FieldByName(_n).String() == "" {
					_omitColsMap.Append(_j, true)
				}
			case "int", "int8", "int16", "int32", "int64":
				if v.Elem().FieldByName(_n).Int() == 0 {
					_omitColsMap.Append(_j, true)
				}
			}
		}
	}
	if len(*_omitColsMap) > 0 {
		that.Session.Omit(_omitColsMap.ToString()...)
	}
}

// Insert ...
func (that *Session) Insert(beans ...interface{}) (int64, error) {
	if _err := that.checkTableMode(); _err != nil {
		return 0, _err
	}
	_mode := beans[0]
	t, v := reflect.TypeOf(_mode), reflect.ValueOf(_mode)
	//递归插入隔离字段
	that.injectionTagInsert(t.Elem(), v.Elem(), 0)
	_b, _err := that.Session.Insert(_mode)
	that.tableMode = nil
	return _b, _err
}

// injectionTagInsert 在插入时，注入隔离字段值
func (that *Session) injectionTagInsert(t reflect.Type, v reflect.Value, k int64) {
	fieldNum := t.NumField()
	for i := 0; i < fieldNum; i++ {
		_f := t.Field(i)
		_fv := v.Field(i)
		k = k + 1
		if k > 1000 {
			panic("database injectionTagInsert more than 1000 loop time")
		}
		if _f.Tag.Get("xorm") == "extends" {
			if _f.Type.Kind() == reflect.Ptr {
				if !_fv.IsNil() {
					that.injectionTagInsert(_f.Type.Elem(), _fv.Elem(), k)
				}

			} else {
				that.injectionTagInsert(_f.Type, _fv, k)
			}
		}

		_injection := _f.Tag.Get("injection")
		if _injection != "" {
			if that.injectionMp.Has(_injection) {
				_fv.SetInt(utils.ToInt64(that.injectionMp.Get(_injection)))
			}
		}
	}
}

// InsertMulti 批量插入
func (that *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
	if _err := that.checkTableMode(); _err != nil {
		return 0, _err
	}
	_n, _err := that.Session.InsertMulti(rowsSlicePtr)
	that.tableMode = nil
	return _n, _err
}

func (that *Session) SQL(query interface{}, args ...interface{}) *Session {
	that.Session.SQL(query, args...)
	return that
}

func (that *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) {
	return that.Session.Query(sqlOrArgs...)
}

// Begin 开启事务
func (that *Session) Begin() error {
	if _err := that.Session.Begin(); _err != nil {
		return _err
	}
	that.sessionStatus.begin()
	return nil
}

// Commit 提交事务
func (that *Session) Commit() error {
	if _err := that.Session.Commit(); _err != nil {
		return _err
	}
	that.sessionStatus.commitOrRollback()
	return nil
}

// Rollback 回滚
func (that *Session) Rollback() error {
	if _err := that.Session.Rollback(); _err != nil {
		return _err
	}
	that.sessionStatus.commitOrRollback()
	return nil
}

func (that *Session) IsClosed() bool {
	if that.sessionStatus == nil {
		return true
	}
	return that.sessionStatus.isClosed()
}

// Close 释放
func (that *Session) Close(force ...bool) *Session {
	//
	if that == nil {
		return nil
	}
	//强行释放
	if len(force) > 0 && force[0] == true {
		that.CloseClone()
		that.Session.Close()
		that.sessionStatus.close()
		that.Session = nil
		that.injectionMp = nil
		that.tableMode = nil
		that.sessionStatus = nil
		return that
	}
	//若上下文为事务时，判断事务是否已回滚或执行
	if that.Session != nil && that.sessionStatus.getCommitOrRollback() {
		that.CloseClone()
		that.Session.Close()
		that.sessionStatus.close()
		that.Session = nil
		that.injectionMp = nil
		that.tableMode = nil
		that.sessionStatus = nil
		return that
	}
	return that
}

func (that *Session) GetCommitOrRollback() bool {
	return that.sessionStatus.getCommitOrRollback()
}

func (that *Session) CloseClone() {
	if !that.isClone && that.sessionSlice != nil && len(that.sessionSlice) > 0 {
		for _, _session := range that.sessionSlice {
			_session.Close()
		}
		that.sessionSlice = nil
	}
}

// Clone 克隆当前对象的值
func (that *Session) Clone() *Session {
	var sess = *that
	_rs := &sess
	//if len(isPrototype) > 0 && isPrototype[0] == true {
	//	_rs.Session = that.Session.Clone()
	//}
	_rs.isClone = true
	that.sessionSlice = append(that.sessionSlice, _rs)
	return _rs
}
