package core

import (
	"bytes"
	"compress/zlib"
	"dbweb/lib/crypter"
	"encoding/gob"
	"io"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"strings"

	"github.com/linlexing/dbx/ddb"

	"github.com/linlexing/dbx/data"
)

var menuClear, _ = regexp.Compile(`^[0-9]{2}\.`)

//该类型和Element不一样，只是部分内容，用于生成菜单项
type UserElement struct {
	Name      string
	Label     string
	Category  string
	NewWindow bool
}

type User struct {
	Name          string
	Elements      []*UserElement
	Roles         []string
	Dept          *Dept
	RootDept      string //此root不是系统中的root，是用户能切换到的最顶级的部门，用户归属地，可下不能上
	Tag           string
	NextLevelDept []*Dept
	ToRootDept    []*Dept
}
type UserMenuCategory struct {
	Name string
}
type UserMenuCategories []*UserMenuCategory

func (s UserMenuCategories) Len() int           { return len(s) }
func (s UserMenuCategories) Less(i, j int) bool { return s[i].Name < s[j].Name }
func (s UserMenuCategories) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type UserMenu struct {
	Name        string
	Label       string
	UserName    string
	ElementName string
	NewWindow   bool //是否在新窗口中打开
}
type UserMenus []*UserMenu

func (s UserMenus) Len() int           { return len(s) }
func (s UserMenus) Less(i, j int) bool { return s[i].Label < s[j].Label }
func (s UserMenus) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

type Dept struct {
	Code     string
	Name     string
	Level    int64
	UserName string
}

func init() {
	gob.Register(&User{})
}
func (m *UserMenu) Url() string {
	return Sign("/"+m.Name, m.UserName)
}

//返回清爽的文本，去掉前面的序号
func (m *UserMenuCategory) ClearText() string {
	if menuClear.MatchString(m.Name) {
		return m.Name[3:]
	}
	return m.Name
}
func (m *UserMenu) ClearLabel() string {
	var str string
	if len(m.Label) == 0 {
		str = m.Name
	} else {
		str = m.Label
	}
	if menuClear.MatchString(str) {
		return str[3:]
	}
	return str
}
func (u *User) HasElement(eleName string) bool {
	for _, v := range u.Elements {
		if v.Name == eleName {
			return true
		}
	}
	return false
}
func (u *User) Sign(strUrl string) string {
	return Sign(strUrl, u.Name)
}
func (u *User) IsSign(ul *url.URL) bool {
	return IsSign(ul, u.Name)
}
func (u *User) ToRootDeptCodesAndSelf() []string {
	result := []string{u.Dept.Code}
	for _, v := range u.ToRootDept {
		result = append(result, v.Code)
	}
	return result
}

//切换处理地，设置Dept、NextLevelDept、ToRootDept
func (u *User) SwitchDept(db ddb.DB, deptCode string) {
	ds, err := queryDept(db, "code=?", "", deptCode)
	if err != nil {
		LOG.Panic(err)
	}
	u.Dept = ds[0]
	u.Dept.UserName = u.Name

	//找出直接下级的部门
	ds, err = queryDept(db, "code like ? and dlevel=?", "code", u.Dept.Code+"%", u.Dept.Level+1)
	if err != nil {
		LOG.Panic(err)
	}
	u.NextLevelDept = []*Dept{}

	for _, oneDept := range ds {
		oneDept.UserName = u.Name
		u.NextLevelDept = append(u.NextLevelDept, oneDept)
	}
	//如果不是根部门，则列出至根部门的路径
	u.ToRootDept = []*Dept{}
	if u.Dept.Code != u.RootDept {
		toRootCodes := []string{}
		for i := len(u.RootDept); i < len(u.Dept.Code); i++ {
			toRootCodes = append(toRootCodes, u.Dept.Code[:i])
		}
		str, p, err := data.In("code in(?) and dlevel<?", toRootCodes, u.Dept.Level)
		if err != nil {
			LOG.Panic(err)
		}

		ds, err = queryDept(db, str, "code", p...)
		if err != nil {
			LOG.Panic(err)
		}
		for _, row := range ds {
			row.UserName = u.Name
			u.ToRootDept = append(u.ToRootDept, row)
		}
	}
	return
}

//返回第0级菜单项的清单
func (u *User) MenuCategory0() UserMenuCategories {
	result := UserMenuCategories{}
	var find = func(name string) bool {
		for _, v := range result {
			if v.Name == name {
				return true
			}
		}
		return false
	}

	for _, v := range u.Elements {
		cs := strings.Split(v.Category, "/")

		if !find(cs[0]) {
			result = append(result, &UserMenuCategory{cs[0]})
		}

	}
	sort.Sort(result)
	return result
}

//返回第1级菜单项的清单
func (u *User) MenuCategory1(cate0 string) UserMenuCategories {
	result := UserMenuCategories{}
	var find = func(name string) bool {
		for _, v := range result {
			if v.Name == name {
				return true
			}
		}
		return false
	}

	for _, v := range u.Elements {
		cs := strings.Split(v.Category, "/")
		if len(cs) > 1 && cs[0] == cate0 && !find(cs[1]) {
			result = append(result, &UserMenuCategory{cs[1]})
		}
	}
	sort.Sort(result)
	return result

}

//返回菜单项的清单
func (u *User) Menus(cate ...string) []*UserMenu {
	result := UserMenus{}
	strCate := strings.Join(cate, "/")
	for _, v := range u.Elements {
		if v.Category == strCate {
			result = append(result, &UserMenu{v.Name, v.Label, u.Name, v.Name, v.NewWindow})
		}
	}
	sort.Sort(result)
	return result
}
func (d *Dept) IsParentOrSelf(code string) bool {
	return strings.HasPrefix(d.Code, code)
}
func (d *Dept) SwitchUrl(req *http.Request) string {
	u, _ := url.Parse("/switchdept")
	q := u.Query()
	q.Add("dept", d.Code)
	q.Add("next", req.URL.String())
	u.RawQuery = q.Encode()
	return Sign(u.String(), d.UserName)
}

func NewUser(db ddb.DB, name string) *User {
	dept, tag, err := queryUserDeptAndTag(db, name)
	if err != nil {
		LOG.Panic(err)
	}
	roles, err := queryUserRoles(db, name)
	if err != nil {
		LOG.Panic(err)
	}
	u := &User{
		Name:     name,
		Roles:    roles,
		RootDept: dept,
		Elements: []*UserElement{},
		Tag:      tag,
	}
	//找出所有的菜单项
	u.Elements, err = queryUserElements(db, name)
	if err != nil {
		LOG.Panic(err)
	}
	u.SwitchDept(db, u.RootDept)
	return u
}
func (u *User) HasRole(roleName string) bool {
	for _, v := range u.Roles {
		if v == roleName {
			return true
		}
	}
	return false
}
func (user *User) EncodeQueryValueB(str []byte) string {
	out := bytes.NewBuffer(nil)
	w := zlib.NewWriter(out)
	if _, err := w.Write(str); err != nil {
		LOG.Panic(err)
	}
	w.Close()

	return crypter.Encode(out.Bytes(), user.Name+"tq")
}
func (user *User) EncodeQueryValue(str string) string {
	return user.EncodeQueryValueB([]byte(str))
}
func (user *User) DecodeQueryValue(str string) string {
	if len(str) == 0 {
		return ""
	}
	in := bytes.NewBuffer(crypter.Decode(str, user.Name+"tq"))
	r, err := zlib.NewReader(in)
	if err != nil {
		LOG.Panic(err)
	}
	out := bytes.NewBuffer(nil)
	if _, err = io.Copy(out, r); err != nil {
		LOG.Panic(err)
	}
	r.Close()
	return out.String()
}
