package models

import (
	"database/sql"
	"fmt"

	"time"

	_ "github.com/go-sql-driver/mysql"
)

type GoSql struct {
	conn *sql.DB
	arr  []interface{}
	stmt *sql.Stmt
}

// mysqlPasswd DES  8位Key加密过了
func (ggg *GoSql) ConnectDb(mysqlHost, mysqlUser, mysqlPasswd, port, dbDatabase string) *GoSql {
	var err error
	ggg.conn, err = sql.Open("mysql", mysqlUser+":"+mysqlPasswd+"@tcp("+mysqlHost+":"+port+")/"+dbDatabase+"?charset=utf8")
	if err != nil {
		fmt.Println("连接数据库fail")
		ggg.conn.Close()
		return nil
	}
	//设置每个链接的过期时间
	ggg.conn.SetConnMaxLifetime(180 * time.Second)
	//设置最大连接数
	ggg.conn.SetMaxOpenConns(30)
	//设置最大空闲连接数
	ggg.conn.SetMaxIdleConns(10)
	return ggg
}
func (ggg *GoSql) Ping() error {
	// 测试数据库连接
	err := ggg.conn.Ping()
	if err != nil {
		return err
	}
	return nil
}

// 第一个参数 sql语句 第二个参数 sql语句 ? 对应的值
func (ggg *GoSql) Sql(sql string, arr ...interface{}) *GoSql {
	Stmt, err := ggg.conn.Prepare(sql)
	if err != nil {
		ggg.conn.Close()
		return nil
	}
	ggg.stmt = Stmt
	ggg.arr = arr
	return ggg
}

// 参数就是返回结果只  查询success 返回 true 如果只判断bool Row里不用传 指针取值
func (ggg *GoSql) Row(show ...interface{}) bool {
	row, err := ggg.stmt.Query(ggg.arr...)
	if err != nil {
		return false
	}
	defer row.Close()
	bl := row.Next()
	if !bl {
		return false
	}
	row.Scan(show...)

	return true
}

// 返回*sql.Rows, error 手动写 指针取值
func (ggg *GoSql) MultiRows() (*sql.Rows, error) {
	rows, err1 := ggg.stmt.Query(ggg.arr...)
	return rows, err1
}

/*
执行查询并返回多行结果。
每行结果存储为一个映射，键为列名，值为该列的值。
返回的映射的键是行号，值是该行的数据映射。
如果查询过程中发生错误，将返回一个空的映射和错误。
fail返回空MAP 数据库字段默认null即sql.NullString之类不能使用该方法
*/

func (ggg *GoSql) AutoMultiRows() map[int]map[string]interface{} {
	// 初始化结果映射
	result := make(map[int]map[string]interface{})
	// 执行查询
	rows, err := ggg.stmt.Query(ggg.arr...)
	if err != nil {
		// 关闭rows资源，避免资源泄露
		rows.Close()
		return nil
	}

	// 获取查询结果的列名
	columns, err1 := rows.Columns()
	if err1 != nil {
		// 关闭rows资源，避免资源泄露
		rows.Close()
		return nil
	}

	// 初始化与列数相等的切片，用于存储每行的数据
	values := make([]interface{}, len(columns))
	valuePtrs := make([]interface{}, len(columns))
	// 将每列的指针存储到valuePtrs中，以便Scan能够直接填充数据
	for i := range columns {
		valuePtrs[i] = &values[i]
	}
	rowIndex := 0
	// 关闭rows资源，避免资源泄露
	defer rows.Close()
	// 遍历查询结果的每一行
	for rows.Next() {
		// 将当前行的数据填充到valuePtrs指向的变量中
		if err := rows.Scan(valuePtrs...); err != nil {
			return nil
		}
		// 初始化当前行的数据映射
		rowData := make(map[string]interface{})
		for i, colName := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			rowData[colName] = v
		}
		// 将当前行的数据添加到结果集中
		result[rowIndex] = rowData
		rowIndex++
	}
	// 检查是否有查询过程中的错误
	if err = rows.Err(); err != nil {
		return nil
	}
	return result
}

// fail返回0 success返回 受影响行的个数
func (ggg *GoSql) Execute() int64 {
	crow, err := ggg.stmt.Exec(ggg.arr...)
	if err != nil {
		return 0
	}
	re, err1 := crow.RowsAffected()
	if err1 != nil {
		return 0
	}
	return re
}

func (ggg *GoSql) AutoInsert(table string, m map[string]interface{}) int64 {
	sql := "INSERT INTO " + table + " ("
	//(rid,user,password) VALUES (?,?,?)
	ik := ""
	iv := ""
	var mv []interface{}
	for k, v := range m {
		if ik == "" {
			ik = k + ","
		} else {
			ik = ik + k + ","
		}
		if iv == "" {
			iv = "?,"
		} else {
			iv = iv + "?" + ","
		}
		mv = append(mv, v)
	}
	ik = ik[0 : len(ik)-1]
	iv = iv[0 : len(iv)-1]
	sql = sql + ik + ") VALUES (" + iv + ")"
	return ggg.Sql(sql, mv...).Execute()
}
func (ggg *GoSql) Count(table string) int64 {
	count := int64(0)
	sql := "SELECT count(id) FROM " + table
	ggg.Sql(sql).Row(&count)
	return count
}
