package core

import (
	"dbweb/lib/bill"
	"encoding/json"
	"errors"
	"fmt"
	"os"

	"github.com/linlexing/dbx/ddb"

	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/linlexing/dbx/render"

	"github.com/linlexing/dbx/common"
	_ "github.com/mattn/go-sqlite3"

	"github.com/linlexing/dbx/data"
	"github.com/linlexing/dbx/schema"
)

const (
	EXISTS_OVERWRITE = iota //相同主键的记录存在时，进行覆盖
	EXISTS_SKIP             //...跳过
)

//DumpBill 业务
type DumpBill struct {
	Name         string
	DBName       string
	Tables       []string //第一个表是主表，其余是明细表，明细表主键前几个必须就是主表中的主键
	Where        string
	ActionExists int32
	BeforeSQL    string
	AfterSQL     string
	RowCount     int64
}

//DumpParam 导出的参数
type DumpParam struct {
	Name      string
	Bills     []*DumpBill
	BeforeSQL string //开始导入前运行
	AfterSQL  string //导入完成后运行

}

//DumpConfig 导出的配置
type DumpConfig struct {
	Param                  *DumpParam
	DBVersions             map[string]int64
	ModuleVersions         map[string]int64
	UserName               string
	DeptCode               string
	ToRootDeptCodesAndSelf []string
	Time                   time.Time
	ServerIP               []string
	ClientIP               string
}

//FilterBill 过滤单据，只有名称传入的才保留，其他删除
func (u *DumpParam) FilterBill(names []string) {
	if len(names) == 0 {
		return
	}
	LOG.WithFields(log.Fields{
		"names": names,
	}).Info("filterBill")
	nameIndex := map[string]bool{}
	for _, v := range names {
		nameIndex[v] = true
	}
	r := []*DumpBill{}
	for _, v := range u.Bills {
		if _, ok := nameIndex[v.Name]; ok {
			r = append(r, v)
		}
	}
	u.Bills = r
	return
}

//JSON 返回配置的JSON字符串
func (u *DumpConfig) JSON() string {

	if bys, err := json.MarshalIndent(u, "", "\t"); err != nil {
		LOG.Panic(err)
		return ""
	} else {
		return string(bys)
	}

}
func (b *DumpBill) dump(db ddb.DB, dumpDB ddb.TxDB, user *User,
	progressFunc func(message string)) (int64, error) {
	if dumpDB == nil {
		return -1, errors.New("dumpDB nil")
	}
	billDB := LoadOuterDB(db, b.DBName)
	newBill, err := bill.OpenBill(billDB, b.Tables[0], b.Tables[1:]...)
	if err != nil {
		LOG.Println("new src bill:", b.Tables, "error,", err)
		return -1, err
	}
	where, err := render.RenderSQL(b.Where, map[string]interface{}{"User": user})
	if err != nil {
		LOG.Println("render src where:", b.Where, "error", err)
		return -1, err
	}
	//创建结构,先主表
	println("update table:", newBill.Main.Table.FullName())
	if err := newBill.Main.Table.Table.Update(dumpDB.DriverName(), dumpDB); err != nil {
		LOG.Println("update table:", newBill.Main.Table.FullName(), " schema error", err)
		return -1, err
	}
	//再明细表
	for _, ct := range newBill.Child {
		if err := ct.Table.Table.Update(dumpDB.DriverName(), dumpDB); err != nil {
			LOG.Println("update table:", ct.FullName(), " schema error", err)
			return -1, err
		}
	}
	dumpBill, err := bill.OpenBill(dumpDB, b.Tables[0], b.Tables[1:]...)
	if err != nil {
		LOG.Println("new dump bill:", b.Tables, "error", err)
		return -1, err
	}
	return dumpBill.ImportFrom(newBill, progressFunc, where)
}

//Check 检查数据包是否有效,能否恢复
func (u *DumpConfig) Check(dumpName string) error {
	if dumpName != u.Param.Name {
		return fmt.Errorf("该数据包不是%s数据", dumpName)
	}
	//判断版本号，如果数据库版本或者模块版本比当前程序高，则出错
	for k, v := range u.DBVersions {
		//数据包中的model在程序中找不到，允许导入
		if md := modelList.FindByName(k); md != nil {
			if md.dbver < v {
				return fmt.Errorf("该数据包中的model:[%s] 的版本 [%d] 比当前的版本 [%d] 高", k, v, md.dbver)
			}
		} else {
			LOG.Println("model", k, "not found")
		}
	}
	for k, v := range u.ModuleVersions {
		if ver, ok := Versions[k]; !ok {
			log.Printf("该数据包中的Module:[%s] 在当前程序中找不到\n", k)
		} else if ver.Version() < v {
			return fmt.Errorf("该数据包中的Module:[%s] 的版本 [%d] 比当前的版本 [%d] 高", k, v, ver.Version())
		}
	}
	return nil
}

//DumpToFile 导出一个数据包至Stream中
func (u *DumpConfig) DumpToFile(db ddb.DB, user *User, fileName string,
	progressFunc func(message string)) (err error) {
	println("open sqlite3 db", fileName)
	dumpDb, err := ddb.Openx("sqlite3", fileName)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			return
		}
		//如果出错，则删除临时文件
		err = dumpDb.Close()
	}()

	//下面开始导出各个单据
	for _, v := range u.Param.Bills {
		if v.RowCount, err = v.dump(db, dumpDb, user, func(mes string) {
			LOG.Println(mes)
		}); err != nil {
			LOG.Println("dump bill:", v.Name, "error")
			return
		}
		progressFunc(fmt.Sprintf("导出 %s %d 个记录", v.Name, v.RowCount))
	}
	//保存config
	tabConfig := schema.NewTable("DUMP_CONFIG")
	tabConfig.Columns = []*schema.Column{
		&schema.Column{
			Name:      "NAME",
			Type:      schema.TypeString,
			MaxLength: 50,
			Null:      false,
		},
		&schema.Column{
			Name:      "VALUE",
			Type:      schema.TypeString,
			MaxLength: -1,
			Null:      false,
		},
	}
	tabConfig.PrimaryKeys = []string{"NAME"}
	var listSQL []string
	if listSQL, err = schema.Find(dumpDb.DriverName()).CreateTableSQL(dumpDb, tabConfig); err != nil {
		return
	}
	if err = common.BatchRun(dumpDb, listSQL); err != nil {
		return
	}
	bys, err := json.MarshalIndent(u, "", "\t")
	if err != nil {
		return
	}
	dataConfig := data.NewTable(dumpDb.DriverName(), dumpDb, tabConfig)
	if err = dataConfig.Insert([]map[string]interface{}{
		map[string]interface{}{
			"NAME":  "config",
			"VALUE": string(bys),
		},
	}); err != nil {
		return
	}

	return nil
}
func DumpBillTestFull() error {
	billDB := InitMetaDB("oci8", "tjbm/llx123@localhost:1521/orcl")
	fileName := "e:\\temp\\test.sq3"
	if billDB == nil {
		return errors.New("billDB nil")
	}
	var iCount int64
	if err := billDB.QueryRow("select 1 from dual").Scan(&iCount); err != nil {
		return err
	}
	if iCount != 1 {
		return errors.New("not equ 1")
	}
	defer billDB.Close()
	dumpDB, err := ddb.Openx("sqlite3", fileName)
	if err != nil {
		return err
	}
	if dumpDB == nil {
		return errors.New("dumpDB nil")
	}
	defer func() {
		if err = dumpDB.Close(); err != nil {
			LOG.Panic(err)
		}
		if err = os.Remove(fileName); err != nil {
			LOG.Panic(err)
		}
	}()

	dumpBill := &DumpBill{
		Name:         "test",
		DBName:       "",
		Tables:       []string{"dept"},
		Where:        "",
		ActionExists: 0,
		BeforeSQL:    "",
		AfterSQL:     "",
		RowCount:     20,
	}
	user := NewUser(billDB, "sys")
	_, err = dumpBill.dump(billDB, dumpDB, user, func(mes string) {
		println(mes)
	})
	if err != nil {
		return err
	}
	return nil

}

func DumpBillTest(billDB, dumpDB ddb.TxDB, user *User) error {
	dumpBill := &DumpBill{
		Name:         "test",
		DBName:       "",
		Tables:       []string{"dept"},
		Where:        "",
		ActionExists: 0,
		BeforeSQL:    "",
		AfterSQL:     "",
		RowCount:     20,
	}
	_, err := dumpBill.dump(billDB, dumpDB, user, func(mes string) {
		println(mes)
	})
	if err != nil {
		return err
	}
	return nil

}
