package common

import (
	"crypto/rand"
	"dbweb/core"
	"dbweb/lib/model"
	"dbweb/lib/safe"
	"encoding/hex"
	"fmt"

	"github.com/linlexing/mapfun"

	"encoding/json"

	"sort"

	"database/sql"

	"time"

	"github.com/linlexing/dbx/data"
	"golang.org/x/crypto/scrypt"
)

const modelUserName = "NUSER"

type modelUserRole struct {
	UserName string `dbx:"str(50) primary key"`
	RoleName string `dbx:"str(50) primary key"`
}
type modelUser struct {
	Name       string          `dbx:"str(50) primary key"`
	NickName   string          `dbx:"str(50)"`
	Salt       []byte          `dbx:"bytea not null"`
	Password   []byte          `dbx:"bytea not null"`
	Dept       string          `dbx:"str(50) not null"`
	Tag        string          `dbx:"str(300)"`
	FreezeTime time.Time       `dbx:"date"`
	FreezeDesc string          `dbx:"str(300)"`
	Roles      []modelUserRole `dbx:"USERROLE child"`
}
type userRole struct {
	Name string
	Type string
}

type user struct{}

func BuildUserPassword(pwd string, salt []byte) ([]byte, error) {
	return scrypt.Key([]byte(pwd), salt, 16384, 8, 1, 32)
}
func (m modelUser) OnAfterSchemaUpdate(md *model.Model, oldversion int64) error {
	if oldversion == 0 {
		core.LOG.Println("save sys user", oldversion)
		salt, err := hex.DecodeString("a4c03c99fdfb766299f102a267fd4588")
		if err != nil {
			return err
		}

		pwd, err := BuildUserPassword("123", salt)
		if err != nil {
			return err
		}
		return md.Set(modelUser{
			Name:     "sys",
			Salt:     salt,
			Password: pwd,
			Dept:     "r",
			Roles: []modelUserRole{
				modelUserRole{
					UserName: "sys",
					RoleName: "system",
				},
				modelUserRole{
					UserName: "sys",
					RoleName: "public",
				},
			},
		})
	}

	return nil
}
func init() {
	core.RegisterModel(modelUser{}, 1, true, modelUserName)
	core.RegisterBill("user", new(user), modelUserName)
}

func (u *user) Get(p *core.BillGetHandleArgs) {
	roles := []*userRole{}
	//如果有附加参数，则限定角色
	if len(p.Param.Tag) > 0 {
		out := map[string]interface{}{}
		if err := json.Unmarshal([]byte(p.Param.Tag), &out); err != nil {
			core.LOG.Panic(err)
		}
		switch rs := out["Roles"].(type) {
		case map[string]interface{}:
			names := mapfun.Keys(rs)
			sort.Strings(names)
			for _, k := range names {
				v := rs[k]
				if safe.String(v) == "always" {
					roles = append(roles, &userRole{Name: k, Type: "always"})
				} else {
					roles = append(roles, &userRole{Name: k, Type: "select"})
				}
			}
		}
	} else {
		//取得本级及上级部门所有的角色
		strSQL := "select name,dept,visible from role where dept in(?) order by dept,name"
		depts := []string{}
		for i := range p.User.Dept.Code {
			depts = append(depts, p.User.Dept.Code[:len(p.User.Dept.Code)-i])
		}
		strSQL, param, err := data.In(strSQL, depts)
		if err != nil {
			core.LOG.Panic(err)
		}

		rows, err := p.DB.Query(strSQL, param...)
		if err != nil {
			core.LOG.Panic(err)
		}
		defer rows.Close()
		//再逐个判断是否可以显示
		for rows.Next() {
			var vName string
			var vDept sql.NullString
			var vVisible sql.NullString
			if err = rows.Scan(&vName, &vDept, &vVisible); err != nil {
				core.LOG.Panic(err)
			}
			role := &DBRole{
				Name:    vName,
				Dept:    vDept.String,
				Visible: vVisible.String,
			}
			if role.IsShow(p.User) {
				roles = append(roles, &userRole{Name: role.Name, Type: "select"})
			}
		}

	}
	p.More["Roles"] = roles
	p.HTML()
}
func (u *user) Post(p *core.BillPostHandleArgs) {
	setPwd := func() {
		salt := make([]byte, 16)
		if _, err := rand.Read(salt); err != nil {
			core.LOG.Panic(err)
		}
		p.NewRecord.Main["SALT"] = salt
		if pwd, err := BuildUserPassword(safe.String(p.NewRecord.Main["PASSWORD"]),
			salt); err != nil {
			core.LOG.Panic(err)
		} else {
			p.NewRecord.Main["PASSWORD"] = pwd
		}

	}
	switch p.Param.Operate {
	case core.BillOperateEdit:
		//如果名称改变，需要改正相关表中的记录
		if safe.String(p.NewRecord.Main["NAME"]) != safe.String(p.OldRecord.Main["NAME"]) {
			if err := ChangeUserEleView(p.Bill.DB(), safe.String(p.OldRecord.Main["NAME"]),
				safe.String(p.NewRecord.Main["NAME"])); err != nil {
				core.LOG.Panic(err)
			}
		}
		//处理密码
		if p.NewRecord.Main["PASSWORD"] == nil {
			p.NewRecord.Main["PASSWORD"] = p.OldRecord.Main["PASSWORD"]
		} else {
			setPwd()
		}
	case core.BillOperateAdd:
		if p.NewRecord.Main["PASSWORD"] == nil {
			p.Error(fmt.Errorf("密码不能为空"))
			return
		}
		setPwd()
	}
	p.Process()
}
func (u *user) Delete(p *core.BillDeleteHandleArgs) {

	if err := RemoveUserEleView(p.Bill.DB(), safe.String(p.OldRecord.Main["NAME"])); err != nil {
		core.LOG.Panic(err)
	}
	p.Remove()
}
