package model

import (
	"errors"
	"reflect"

	"github.com/linlexing/dbx/ddb"

	"dbweb/lib/bill"

	"github.com/linlexing/dbx/schema"
)

//Model 定义一个Model,最多两层主从表,其实就是Bill，以struct替换Record
type Model struct {
	//实际的model类型，是一个struct
	db ddb.DB
	b  *bill.Bill
	m  interface{}
}

//New 新建一个Model，但是不会做任何数据库操作
func New(db ddb.DB, md interface{}, tabNames ...string) (*Model, error) {
	b, err := bill.NewBill(db, md, tabNames...)
	if err != nil {
		return nil, err
	}
	return &Model{db: db, b: b, m: md}, nil
}

//Name 主表的名称
func (m *Model) Name() string {
	return m.b.Main.FullName()
}

//Count 统计符合条件的记录数
func (m *Model) Count(where string, args ...interface{}) (int64, error) {
	return m.b.Main.Count(where, args...)
}

//Bill 通用的Bill操作类
func (m *Model) Bill() *bill.Bill {
	return m.b
}

// //UpdateSchema 更新结构到数据库中
// func (m *Model) UpdateSchema() error {
// 	if err := m.b.Main.Table.Table.Update(m.db.DriverName(), m.db); err != nil {
// 		return err
// 	}
// 	for _, t := range m.b.Child {
// 		if err := t.Table.Table.Update(m.db.DriverName(), m.db); err != nil {
// 			return err
// 		}
// 	}
// 	return nil
// }

//ExtractSchemaChanges 提取更新结构的语句
func (m *Model) ExtractSchemaChanges() ([]string, error) {
	rev, err := m.b.Main.Table.Table.Extract(m.db.DriverName(), m.db)
	if err != nil {
		return nil, err
	}
	for _, t := range m.b.Child {
		list, err := t.Table.Table.Extract(m.db.DriverName(), m.db)
		if err != nil {
			return nil, err
		}
		rev = append(rev, list...)
	}
	return rev, nil
}

//Get 获取一个记录，out必须是指针，如果找不到记录，返回ErrNoRows
func (m *Model) Get(out interface{}, key ...interface{}) error {
	rec, err := m.b.Record(key...)
	if err != nil {
		return err
	}
	return rec.Scan(out)
}

//Set 保存一个记录，插入或者更新
func (m *Model) Set(in interface{}) (err error) {
	rec, err := bill.ParseRecord(in)
	if err != nil {
		return err
	}
	return m.b.Save(rec)
}

//Insert 插入一个[]struct或者Struct,没有优化过多表批量速度，以后有需要进行优化
func (m *Model) Insert(sliceOrStruct interface{}) error {
	vtype := reflect.TypeOf(sliceOrStruct)
	if vtype.Kind() == reflect.Ptr {
		vtype = vtype.Elem()
	}
	if vtype.Kind() == reflect.Struct {
		return m.insert([]interface{}{sliceOrStruct})
	}
	return m.insert(sliceOrStruct)
}
func (m *Model) insert(inSlice interface{}) error {
	vtype := reflect.TypeOf(inSlice)
	if vtype.Kind() != reflect.Slice {
		return errors.New("in param must is slice")
	}

	vval := reflect.ValueOf(inSlice)
	//如果只有主表，则进行批量插入,批量插入速度快，只有一次硬解析
	if len(m.b.Child) == 0 {
		rows := []map[string]interface{}{}
		for i := 0; i < vval.Len(); i++ {
			row, _, err := schema.Struct2Row(vval.Index(i).Interface())
			if err != nil {
				return err
			}
			rows = append(rows, row)
		}
		return m.b.Main.Insert(rows)
	}
	for i := 0; i < vval.Len(); i++ {
		rec, err := bill.ParseRecord(vval.Index(i).Interface())
		if err != nil {
			return err
		}
		if err = m.b.Insert(rec); err != nil {
			return err
		}
	}
	return nil
}

//Query 查询一个结果，并返回一个Rows，类似sql.Rows
func (m *Model) Query(where string, args ...interface{}) (*Rows, error) {
	rows := &Rows{m: m}
	var err error
	if rows.rows, err = m.b.Query(where, args...); err != nil {
		return nil, err
	}
	return rows, nil
}

//QueryOrder 可以排序
func (m *Model) QueryOrder(orderby []string, where string, args ...interface{}) (*Rows, error) {
	rows := &Rows{m: m}
	var err error
	if rows.rows, err = m.b.QueryOrder(orderby, where, args...); err != nil {
		return nil, err
	}
	return rows, nil
}

//KeyExists 检测一个主键是否存在
func (m *Model) KeyExists(key ...interface{}) (bool, error) {
	return m.b.Main.KeyExists(key...)
}
