package core

import (
	"bytes"
	"dbweb/lib/bill"
	"dbweb/lib/crypter"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"errors"

	"github.com/Sirupsen/logrus"
	"github.com/linlexing/dbx/ddb"

	"dbweb/lib/safe"

	"database/sql"

	"github.com/linlexing/dbx/schema"
)

const (
	SKIP_FILL = iota
	FILL_AT_OPEN
	FILL_AT_SAVE
	BillOperateAdd    = "add"
	BillOperateEdit   = "edit"
	BillOperateDelete = "delete"
	BillOperateBrowse = "browse"
)

type BillResult struct {
	Ok           bool
	Error        string
	CheckResults []*TableCheckResult
}
type FieldSetting struct {
	Name       string
	Fill       string
	FillAt     int
	Visibility string
}

//BillParam bill操作的参数
type BillParam struct {
	Operate       string
	AllowClone    bool
	FieldSettings []*FieldSetting
	Tag           string
}

//用于界面渲染的字段信息，隐藏的字段不生成
type BillRenderField struct {
	*schema.Column
	Readonly bool
	Hidden   bool
	Value    string
	ValueI   interface{}
	More     map[string]interface{}
}

func (b *BillRenderField) JSONDate() string {
	if len(b.Value) == 0 {
		return ""
	}
	t, err := time.Parse("2006-01-02 15:04:05", b.Value)
	if err != nil {
		LOG.Panic(err)
	}
	return t.Format("2006-01-02T15:04:05")
}

//FixFieldSettings 给定一个表，每个字段生成一个FieldSettings，如果表中字段还没有填充设置，则自动增加一个
func (b *BillParam) FixFieldSettings(cols []*schema.Column) {
	newFeilds := []*FieldSetting{}
	for _, field := range cols {
		if findField := b.Field(field.Name); findField != nil {
			newFeilds = append(newFeilds, findField)
		} else {
			newFeilds = append(newFeilds, &FieldSetting{
				Name:       field.Name,
				Fill:       "",
				FillAt:     SKIP_FILL,
				Visibility: "",
			})
		}
	}
	b.FieldSettings = newFeilds
}
func (b *BillParam) FieldIsHidden(name string) bool {
	fld := b.Field(name)
	if nil == fld {
		return false
	}
	return fld.Visibility == "hidden"
}
func (b *BillParam) FieldIsReadonly(name string) bool {
	fld := b.Field(name)
	if nil == fld {
		return false
	}
	return fld.Visibility == "readonly"
}
func (b *BillParam) Field(name string) *FieldSetting {
	for _, v := range b.FieldSettings {
		if v.Name == name {
			return v
		}
	}
	return nil
}

type BillGetHandleArgs struct {
	*ElementHandleArgs
	renderFields map[string]*BillRenderField
	fieldMore    map[string]map[string]interface{} //每个字段附加的属性
	Param        *BillParam
	Bill         *bill.Bill
	Record       *bill.Record
	Checkes      []*TableCheck
	CheckResults []*TableCheckResult
}

type BillPostHandleArgs struct {
	*ElementHandleArgs
	CheckOnly bool
	Param     *BillParam
	Bill      *bill.Bill
	Checkes   []*TableCheck
	Remarks   map[string]string
	NewRecord *bill.Record
	OldRecord *bill.Record
	OnSave    func(b *bill.Bill) error
}
type BillDeleteHandleArgs struct {
	*ElementHandleArgs
	Bill      *bill.Bill
	OldRecord *bill.Record
	OnSave    func(b *bill.Bill) error
}
type GetEvent interface {
	Get(args *ElementHandleArgs)
}
type PostEvent interface {
	Post(args *ElementHandleArgs)
}
type DeleteEvent interface {
	Delete(args *ElementHandleArgs)
}
type BillGetEvent interface {
	Get(args *BillGetHandleArgs)
}
type BillPostEvent interface {
	Post(args *BillPostHandleArgs)
}
type BillDeleteEvent interface {
	Delete(args *BillDeleteHandleArgs)
}

//返回经过加密的主键值，用sessionid加密
func (p *BillGetHandleArgs) SignKeyValues() string {
	out := bytes.NewBuffer(nil)
	if err := gob.NewEncoder(out).Encode(p.KeyValues()); err != nil {
		LOG.Panic(err)
	}
	return p.User.EncodeQueryValueB(out.Bytes())
}
func (p *BillGetHandleArgs) KeyValues() []interface{} {
	return p.Bill.Main.KeyValues(p.Record.Main)
}

func (p *BillGetHandleArgs) LoadCheckResult(keyValues []interface{}) {
	p.CheckResults = []*TableCheckResult{}
	p.Checkes = loadTableCheckes(p.DB, p.Bill.Main.Name, p.User.Dept)
	for _, v := range p.Checkes {
		r, err := v.LoadCheckResult(p.Bill.DB(), p.Bill.Main.Table.Table, keyValues)
		if err != nil {
			LOG.Panic(err)
		}
		if r != nil {
			p.CheckResults = append(p.CheckResults, r)
		}
	}
}

//设置每个字段附加的属性
func (p *BillGetHandleArgs) FieldMore(name string) map[string]interface{} {
	if _, ok := p.fieldMore[name]; !ok {
		p.fieldMore[name] = map[string]interface{}{}
	}
	return p.fieldMore[name]
}

//返回一个字段渲染的信息
func (p *BillGetHandleArgs) Field(name string) *BillRenderField {

	if fnd, ok := p.renderFields[name]; ok {
		return fnd
	}
	field := p.Bill.Main.ColumnByName(name)
	if field == nil {
		LOG.WithFields(logrus.Fields{
			"col":     name,
			"table":   p.Bill.Main.FullName(),
			"columns": p.Bill.Main.ColumnNames,
		}).Info("can't found column")
		return nil
	}

	p.renderFields[name] = &BillRenderField{
		Column:   field,
		Hidden:   p.Param.FieldIsHidden(name),
		Readonly: p.Param.FieldIsReadonly(name) || p.Param.Operate == "delete" || p.Param.Operate == "browse",
		Value:    field.Type.ToString(p.Record.Main[name]),
		ValueI:   p.Record.Main[name],
		More:     p.FieldMore(name),
	}
	return p.renderFields[name]
}
func (p *BillGetHandleArgs) Fields() []*BillRenderField {
	result := []*BillRenderField{}
	for _, v := range p.Bill.Main.Columns {
		result = append(result, p.Field(v.Name))
	}
	return result
}
func (p *BillGetHandleArgs) FieldTypes() map[string]schema.DataType {
	result := map[string]schema.DataType{}
	for _, v := range p.Bill.Main.Columns {
		result[v.Name] = v.Type
	}
	return result
}

//返回加密的数据，用于post作为旧值传回服务器，加密是防止数据泄密
func (p *BillGetHandleArgs) EncodeData() string {
	if p.Param.Operate == "add" {
		return ""
	}

	if bys, err := p.Record.Encode(); err != nil {
		LOG.Panic(err)
		return ""
	} else {
		return crypter.Encode(bys, p.User.Name+"bd")
	}
}

func NewBillGetHandleArgs(args *ElementHandleArgs) *BillGetHandleArgs {
	result := &BillGetHandleArgs{
		renderFields:      map[string]*BillRenderField{},
		fieldMore:         map[string]map[string]interface{}{},
		ElementHandleArgs: args,
		Param:             &BillParam{Operate: "", FieldSettings: []*FieldSetting{}},
		CheckResults:      []*TableCheckResult{},
		Bill:              args.Bill(),
	}

	if err := json.Unmarshal(args.Element.Params, result.Param); err != nil {
		LOG.Panic(err)
	}
	if result.Param.Operate != "add" &&
		result.Param.Operate != "edit" &&
		result.Param.Operate != "delete" &&
		result.Param.Operate != "browse" {
		LOG.Panic("bill operate error")
	}

	switch result.Param.Operate {
	case "add":
		//fill default value
		result.Record = &bill.Record{
			Main: map[string]interface{}{},
		}
		tab := result.Bill.Main
		tabPk := tab.PrimaryKeys
		for _, v := range result.Param.FieldSettings {
			if field := tab.ColumnByName(v.Name); v.FillAt == FILL_AT_OPEN &&
				v.Fill != "" && field != nil {
				if tv, err := ExecuteFill(v.Fill, result.Record.Main, args.User, args.LSession); err != nil {
					LOG.Panic(err)
				} else {
					result.Record.Main[v.Name] = field.Type.ParseString(tv)
				}
			}
		}
		//如果有传入的_more参数，则填充主键
		if more_str := args.Req.URL.Query().Get("_more"); len(more_str) > 0 {
			moreParam := []string{}
			bys := bytes.NewBufferString(args.User.DecodeQueryValue(more_str))
			if err := gob.NewDecoder(bys).Decode(&moreParam); err != nil {
				LOG.Panic(err)
			}
			for i, v := range moreParam {
				colName := tabPk[i]
				result.Record.Main[colName] = tab.ColumnByName(colName).Type.ParseString(v)
			}
		}

	default:
		if len(args.PKS) == 0 {
			LOG.Panic("the pk is nil")
		}
		var err error
		result.Record, err = result.Bill.Record(safe.Inter(args.PKS)...)
		if err == sql.ErrNoRows {
			LOG.Panic(fmt.Errorf("%s 主键为 %s 的记录找不到", result.Bill.Main.Name, args.PKS))
		}
		if err != nil {
			LOG.Panic(err)
		}
		result.LoadCheckResult(safe.Inter(args.PKS))
		if result.Param.Operate == "edit" {
			tab := result.Bill.Main
			for _, v := range result.Param.FieldSettings {
				if field := tab.ColumnByName(v.Name); v.FillAt == FILL_AT_OPEN &&
					v.Fill != "" && field != nil {
					if tv, err := ExecuteFill(v.Fill, result.Record.Main, args.User, args.LSession); err != nil {
						LOG.Panic(err)
					} else {
						result.Record.Main[v.Name] = field.Type.ParseString(tv)
					}
				}
			}
		}
	}
	return result
}
func (a *BillGetHandleArgs) HTML(layout ...string) {
	if len(a.Element.Controller.Layout) > 0 {
		a.Render.HTML(200, a.Element.Controller.Name, a, a.Element.Controller.Layout)
	} else if len(layout) > 0 {
		a.Render.HTML(200, a.Element.Controller.Name, a, layout[0])
	} else if a.Element.Controller.Bill {
		a.Render.HTML(200, a.Element.Controller.Name, a, "_layout/bill_normal")

	} else {
		a.Render.HTML(200, a.Element.Controller.Name, a, "_layout/normal")

	}
}

type postEventBody struct {
	CheckOnly bool   //是否只是审核数据，而不保存
	OldData   string //加密的数据
	NewRecord *bill.Record
	Remarks   map[string]string
}

func NewBillPostHandleArgs(args *ElementHandleArgs) *BillPostHandleArgs {
	result := &BillPostHandleArgs{
		ElementHandleArgs: args,
		Param:             &BillParam{Operate: "", FieldSettings: []*FieldSetting{}},
		Remarks:           map[string]string{},
		Bill:              args.Bill(),
	}
	result.Checkes = loadTableCheckes(args.DB, result.Bill.Main.Name, args.User.Dept)

	jsData := postEventBody{}
	js := json.NewDecoder(args.Req.Body)
	if err := js.Decode(&jsData); err != nil {
		LOG.Panic(err)
	}
	result.CheckOnly = jsData.CheckOnly
	result.Remarks = jsData.Remarks
	if err := json.Unmarshal(args.Element.Params, result.Param); err != nil {
		LOG.Panic(err)
	}
	result.NewRecord = jsData.NewRecord
	//新增没有旧数据
	if result.Param.Operate != "add" {
		if oldRecord, err := bill.DecodeRecord(crypter.Decode(jsData.OldData, args.User.Name+"bd")); err != nil {
			LOG.Panic(err)
		} else {
			result.OldRecord = oldRecord
		}
		//根据旧值，生成新值全行
		for k, v := range result.OldRecord.Main {
			//如果没有新值，则取旧值代替
			if _, ok := result.NewRecord.Main[k]; !ok {
				result.NewRecord.Main[k] = v
			}
		}
		if len(result.Bill.Child) > 0 && result.NewRecord.Child == nil {
			result.NewRecord.Child = map[string][]map[string]interface{}{}
		}
		for childName, childData := range result.OldRecord.Child {
			//如果没有新值，则取新值代替
			if _, ok := result.NewRecord.Child[childName]; !ok {
				result.NewRecord.Child[childName] = childData
			}
		}
	}
	//将所有的数据转换成实际的类型,因为从前端传过来的全部是string
	for k, v := range result.NewRecord.Main {
		if tv, ok := v.(string); ok {
			fld := result.Bill.Main.ColumnByName(k)
			if fld == nil {
				LOG.Panic("not found", k, "fields", result.Bill.Main.Columns)
			}
			result.NewRecord.Main[k] = fld.Type.ParseString(tv)
		}
	}
	for childName, childData := range result.NewRecord.Child {
		childStruct := result.Bill.Child[childName]
		for _, row := range childData {
			for k, v := range row {
				if tv, ok := v.(string); ok {
					row[k] = childStruct.ColumnByName(k).Type.ParseString(tv)
				}
			}
		}
	}

	//将所有Child中关联的主键替换成最新的主键
	mainPKNames := result.Bill.Main.PrimaryKeys
	for childName, childData := range result.NewRecord.Child {
		childPKNames := result.Bill.Child[childName].PrimaryKeys
		for _, row := range childData {
			for i, v := range mainPKNames {
				row[childPKNames[i]] = result.NewRecord.Main[v]
			}
		}
	}

	tab := result.Bill.Main
	//fill Silent
	switch result.Param.Operate {
	case "add", "edit":
		for _, v := range result.Param.FieldSettings {
			if field := tab.ColumnByName(v.Name); field != nil &&
				v.Fill != "" && v.FillAt == FILL_AT_SAVE {
				if tv, err := ExecuteFill(v.Fill, result.NewRecord.Main, args.User, args.LSession); err != nil {
					LOG.Panic(err)
				} else {
					result.NewRecord.Main[v.Name] = field.Type.ParseString(tv)
				}
			}
		}
	default:
		LOG.Panic("invalid operate:" + result.Param.Operate)
	}

	return result
}

func NewBillDeleteHandleArgs(args *ElementHandleArgs) (result *BillDeleteHandleArgs) {
	result = &BillDeleteHandleArgs{
		ElementHandleArgs: args,
		Bill:              args.Bill(),
	}
	oldData, err := ioutil.ReadAll(args.Req.Body)
	if err != nil {
		LOG.Panic(err)
	}
	if oldRecord, err := bill.DecodeRecord(crypter.Decode(string(oldData), args.User.Name+"bd")); err != nil {
		LOG.Panic(err)
	} else {
		result.OldRecord = oldRecord
	}

	return result
}

//Error 返回一个错误
func (b *BillPostHandleArgs) Error(err error) {
	b.Render.JSON(http.StatusOK, BillResult{
		Ok:    false,
		Error: err.Error(),
	})
}
func (b *BillPostHandleArgs) Process() {
	if b.CheckOnly {
		if r, err := b.check(); err != nil {
			b.Render.JSON(http.StatusOK, BillResult{
				Ok:    false,
				Error: err.Error(),
			})
		} else {
			b.Render.JSON(http.StatusOK, BillResult{
				Ok:           true,
				CheckResults: r,
			})
		}

	} else {
		if ok, r, err := b.save(); err != nil {
			b.Render.JSON(http.StatusOK, BillResult{
				Ok:           false,
				Error:        err.Error(),
				CheckResults: r,
			})
		} else {
			b.Render.JSON(http.StatusOK, BillResult{
				Ok:           ok,
				CheckResults: r,
			})
		}
	}
	return
}

//Error 返回一个错误
func (b *BillDeleteHandleArgs) Error(err error) {
	b.Render.JSON(http.StatusOK, BillResult{
		Ok:    false,
		Error: err.Error(),
	})
}
func (b *BillDeleteHandleArgs) Remove() {
	if err := b.remove(); err != nil {
		b.Render.JSON(http.StatusOK, BillResult{
			Ok:    false,
			Error: err.Error(),
		})
	} else {
		b.Render.JSON(http.StatusOK, BillResult{
			Ok: true,
		})
	}
	return
}
func (p *BillPostHandleArgs) KeyValues() []interface{} {
	return p.Bill.Main.KeyValues(p.NewRecord.Main)
}

//审核记录
func (p *BillPostHandleArgs) check() (result []*TableCheckResult, err error) {
	//先保存，再审核，最后rollback
	stopErr := errors.New("STOP")
	if er := ddb.RunAtTx(p.DB, func(tx ddb.Txer) error {
		b := p.Bill.Clone(tx)
		if _, err := p.innerSave(b); err != nil {
			return err
		}
		result = []*TableCheckResult{}
		for _, v := range p.Checkes {
			r, err := v.LoadCheckResult(b.DB(), b.Main.Table.Table, p.KeyValues())
			if err != nil {
				return err
			}
			if r != nil {
				result = append(result, r)
			}
		}
		return stopErr
	}); er != stopErr {
		err = er
	}
	return
}
func (p *BillPostHandleArgs) innerSave(b *bill.Bill) (checks []*TableCheckResult, err error) {
	ope := p.Param.Operate
	switch ope {
	case "add":
		//在新增前，先验证主键值是否重复，提供更友善的提示
		var has bool
		if has, err = b.Main.KeyExists(p.KeyValues()...); err != nil {
			log.Println("check primary key error:" + err.Error())
			return
		} else if has {
			err = fmt.Errorf("主键为:%v 的记录已经存在，不能新增", p.KeyValues())
			return
		}

		if err = b.Insert(p.NewRecord); err != nil {
			log.Println("insert error:", err)
			return
		}
	case "edit":
		if err = b.Update(p.OldRecord, p.NewRecord); err != nil {
			log.Println("update error:", err)
			return
		}
	default:
		LOG.Panic("invalid opt :" + ope)
	}
	checks = []*TableCheckResult{}
	keyValues := p.KeyValues()
	for _, v := range p.Checkes {
		if chk, err := v.CheckRow(b.Main.Table, keyValues, p.Remarks[v.ID], p.User); err != nil {
			return nil, err
		} else if chk != nil {
			checks = append(checks, chk)
		}
	}
	return
}
func (p *BillPostHandleArgs) save() (success bool, checks []*TableCheckResult, err error) {
	errStop := errors.New("ErrStop")
	err = ddb.RunAtTx(p.Bill.DB().(ddb.TxDB), func(tx ddb.Txer) error {
		b := p.Bill.Clone(tx)
		checks, err = p.innerSave(b)
		if err != nil {
			return err
		}
		success = true
		//检查必审的条件是否满足，或者可以填写备注
		for _, v := range checks {
			//1的必须满足，2的可以填备注通过，其他的不检查
			if v.Must == 1 || (v.Must == 2 && len(v.Remark) == 0) {
				success = false
				break
			}
		}
		//审核不成功则回滚
		if !success{
			return errStop
		}
		//处理onsave事件
		if p.OnSave != nil {
			if err = p.OnSave(b); err != nil {
				return err
			}
		}
		return nil

	})
	if err == errStop{
		err =nil
	}
	return

}
func (p *BillDeleteHandleArgs) KeyValues() []interface{} {
	return p.Bill.Main.KeyValues(p.OldRecord.Main)
}

func (p *BillDeleteHandleArgs) remove() error {
	return ddb.RunAtTx(p.Bill.DB().(ddb.TxDB), func(tx ddb.Txer) error {
		b := p.Bill.Clone(tx)

		if err := b.Remove(p.OldRecord); err != nil {
			return err
		}
		//处理onsave事件
		if p.OnSave != nil {
			if err := p.OnSave(b); err != nil {
				return err
			}
		}
		return nil
	})

}
