package service

import (
	"time"

	"XT_New/models"
	"XT_New/utils"

	"github.com/jinzhu/gorm"
)

func GetRoles(orgID int64, appID int64, page int, count int) ([]*models.Role, int, error) {
	var roles []*models.Role
	var totalCount int
	err := readUserDb.Model(&models.Role{}).
		Where("org_id = ? AND app_id = ?", orgID, appID).
		Count(&totalCount).
		Order("ctime asc").Limit(count).Offset((page - 1) * count).
		Find(&roles).
		Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return make([]*models.Role, 0), 0, nil
		} else {
			return nil, 0, err
		}
	}
	return roles, totalCount, nil
}

func GetAppRole(orgID int64, appID int64, adminUserID int64) (*models.App_Role, error) {
	var appRole models.App_Role
	err := readUserDb.Model(models.App_Role{}).Where("org_id = ? and app_id = ? and admin_user_id = ?", orgID, appID, adminUserID).First(&appRole).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		} else {
			return nil, err
		}
	}
	return &appRole, nil
}

func CreateRole(adminUserID int64, orgID int64, appID int64, name string, intro string) (*models.Role, error) {
	role := models.Role{
		RoleName:     name,
		RoleIntro:    intro,
		Creator:      adminUserID,
		OrgId:        orgID,
		AppId:        appID,
		IsSuperAdmin: false,
		Status:       1,
		CreateTime:   time.Now().Unix(),
		ModifyTime:   time.Now().Unix(),
	}
	tx := writeUserDb.Begin()
	if err := tx.Create(&role).Error; err != nil {
		tx.Rollback()
		return nil, err
	}
	tx.Commit()
	return &role, nil
}

func GetRoleByRoleID(roleID int64) (*models.Role, error) {
	var role models.Role
	err := readUserDb.Model(&models.Role{}).Where("id = ?", roleID).First(&role).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	} else {
		return &role, nil
	}
}

func ModifyRole(role *models.Role) error {
	tx := writeUserDb.Begin()
	if err := tx.Save(role).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

// 拥有xx角色的管理员的数量
func RoleAdminUserCount(orgID int64, appID int64, roleID int64) (int, error) {
	var count int
	err := readUserDb.Model(models.App_Role{}).
		Where("org_id = ? AND app_id = ? AND role_id = ? AND status = 1", orgID, appID, roleID).
		Count(&count).
		Error
	if err != nil {
		return 0, err
	} else {
		return count, nil
	}
}

type AdminUserManageViewModel struct {
	AdminUserId int    `gorm:"admin_user_id" json:"user_id"`
	UserName    string `gorm:"user_name" json:"user_name"`
	RoleName    string `gorm:"role_name" json:"role_name"`
	UserTitle   int    `gorm:"user_title" json:"user_title"`
	Ip          string `gorm:"ip" json:"ip"`
	Ctime       int64  `gorm:"ctime" json:"last_login_time"`
	Status      int    `gorm:"status" json:"status"`

	// LastLoginTimeStr string `gorm:"-" json:"last_login_time_formatted"`
	TitleName string `gorm:"-" json:"title_name"`
}

func GetAdminUsersAndLoginInfo(orgID int64, appID int64, page int, count int) ([]*AdminUserManageViewModel, int, error) {
	if count <= 0 {
		return []*AdminUserManageViewModel{}, 0, nil
	}
	if page < 1 {
		page = 1
	}
	var viewModels []*AdminUserManageViewModel = make([]*AdminUserManageViewModel, 0)
	rows, err := readUserDb.Raw("SELECT u_a_r.admin_user_id, u_a_r.user_name, u_r.role_name, u_a_r.user_title, u_l.ip, u_l.ctime, u_a_r.status FROM sgj_user_admin_role AS u_a_r INNER JOIN sgj_user_role AS u_r ON u_a_r.org_id = u_r.org_id AND u_a_r.app_id = u_r.app_id AND u_r.id = u_a_r.role_id LEFT JOIN (SELECT * FROM (SELECT admin_user_id, org_id, app_id, ip, ctime FROM sgj_user_admin_login_log WHERE org_id = ? AND app_id = ? ORDER BY ctime DESC) AS t GROUP BY admin_user_id) AS u_l ON u_a_r.org_id = u_l.org_id AND u_a_r.app_id = u_l.app_id AND u_a_r.admin_user_id = u_l.admin_user_id WHERE u_a_r.org_id = ? AND u_a_r.app_id = ? GROUP BY u_a_r.admin_user_id LIMIT ? OFFSET ?;", orgID, appID, orgID, appID, count, (page-1)*count).Rows()
	defer rows.Close()
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return viewModels, 0, nil
		} else {
			return nil, 0, err
		}
	}
	for rows.Next() {
		var viewModel AdminUserManageViewModel
		readUserDb.ScanRows(rows, &viewModel)
		title, _ := models.UserTitle[viewModel.UserTitle]
		viewModel.TitleName = title
		// if viewModel.Ctime == 0 {
		// 	viewModel.LastLoginTimeStr = ""
		// } else {
		// 	loginTime := time.Unix(viewModel.Ctime, 0)
		// 	viewModel.LastLoginTimeStr = loginTime.Format("2006-01-02 15:04")
		// }
		viewModels = append(viewModels, &viewModel)
	}
	total := 0
	readUserDb.Table("sgj_user_admin_role as u_a_r").Joins("join sgj_user_role as u_r on u_r.org_id = u_a_r.org_id AND u_r.app_id = u_a_r.app_id AND u_r.id = u_a_r.role_id").Where("u_a_r.org_id = ? AND u_a_r.app_id = ?", orgID, appID).Count(&total)
	return viewModels, total, nil
}

func GetValidRoleCount(orgID int64, appID int64, superAdminUserID int64) (int, error) {
	var count int
	err := readUserDb.Model(models.Role{}).
		Where("org_id = ? AND app_id = ? AND creator = ? AND is_super_admin = 0 AND status = 1", orgID, appID, superAdminUserID).
		Count(&count).
		Error
	if err != nil {
		return 0, err
	} else {
		return count, nil
	}
}

func GetAllValidRoles(orgID int64, appID int64) ([]*models.Role, error) {
	var roles []*models.Role
	err := readUserDb.Model(models.Role{}).
		Where("org_id = ? AND app_id = ? AND status = 1", orgID, appID).
		Order("ctime asc").
		Find(&roles).
		Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return make([]*models.Role, 0), nil
		} else {
			return nil, err
		}
	}
	return roles, nil
}

func IsRoleExist(orgID int64, appID int64, roleID int64) (bool, error) {
	var count int
	err := readUserDb.Model(models.Role{}).Where("org_id = ? AND app_id = ? AND id = ? AND status = 1", orgID, appID, roleID).Count(&count).Error
	if err != nil {
		return false, err
	} else {
		return count > 0, nil
	}
}

type AdminUserEditViewModel struct {
	AdminUserId int64  `gorm:"admin_user_id" json:"user_id"`
	UserName    string `gorm:"user_name" json:"user_name"`
	Mobile      string `gorm:"mobile" json:"mobile"`
	UserType    int    `gorm:"user_type" json:"user_type"`
	UserTitle   int    `gorm:"user_title" json:"user_title"`
	Intro       string `gorm:"intro" json:"intro"`
	RoleId      int64  `gorm:"role_id" json:"role_id"`
}

func GetGeneralAdminUser(orgID int64, appID int64, user_id int64) (*AdminUserEditViewModel, error) {
	rows, err := readUserDb.Raw("SELECT u_a.mobile, u_a_r.admin_user_id, u_a_r.user_name, u_a_r.user_type, u_a_r.user_title, u_a_r.intro, u_a_r.role_id FROM sgj_user_admin AS u_a, sgj_user_admin_role AS u_a_r WHERE u_a.id = u_a_r.admin_user_id AND u_a.id = ? AND u_a_r.status = 1 AND u_a_r.org_id = ? AND u_a_r.app_id = ?;", user_id, orgID, appID).Rows()
	defer rows.Close()
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		} else {
			return nil, err
		}
	}
	if rows.Next() {
		var viewModel AdminUserEditViewModel
		err := readUserDb.ScanRows(rows, &viewModel)
		if err != nil {
			return nil, err
		} else {
			return &viewModel, nil
		}
	}
	return nil, nil
}

func SaveAppRole(appRole *models.App_Role) error {
	tx := writeUserDb.Begin()
	if err := tx.Model(&models.App_Role{}).Save(appRole).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

func IsMobileDidUsedAtApp(orgID int64, appID int64, mobile string) (bool, error) {
	var count int
	rows, err := readUserDb.Raw("select count(u_a.id) as count from sgj_user_admin as u_a, sgj_user_admin_role as u_a_r where u_a_r.org_id = ? and u_a_r.app_id = ? and u_a.mobile = ? and u_a.id = u_a_r.admin_user_id;", orgID, appID, mobile).Rows()
	defer rows.Close()
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return false, nil
		} else {
			return true, err
		}
	}
	if rows.Next() {
		rows.Scan(&count)
		return count > 0, nil
	}
	return true, nil
}

func IsUserSuperAdminWithMobile(mobile string) (bool, error) {
	var user models.AdminUser
	err := readUserDb.Where("mobile = ?", mobile).First(&user).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return false, nil
		} else {
			return false, err
		}
	}
	return user.IsSuperAdmin, nil
}

func CreateGeneralAdminUser(orgID int64, appID int64, mobile string, name string, userType int, userTitle int, intro string, roleID int64) (*models.AdminUser, string, error) {
	now := time.Now().Unix()
	tx := writeUserDb.Begin()
	var adminUser models.AdminUser
	err := readUserDb.Where("mobile = ? AND status = 1", mobile).First(&adminUser).Error
	password := ""
	if err != nil {
		if err != gorm.ErrRecordNotFound {
			return nil, "", err
		} else {
			password = utils.RandomNumberString(6)
			adminUser.Mobile = mobile
			adminUser.Password = utils.String2md5(password)
			adminUser.IsSuperAdmin = false
			adminUser.Status = 1
			adminUser.CreateTime = now
			adminUser.ModifyTime = now
			if createErr := tx.Create(&adminUser).Error; createErr != nil {
				tx.Rollback()
				return nil, "", createErr
			}
		}
	}
	app_role := models.App_Role{
		AdminUserId: adminUser.Id,
		OrgId:       orgID,
		AppId:       appID,
		RoleId:      roleID,
		Avatar:      "",
		UserName:    name,
		Intro:       intro,
		UserType:    int8(userType),
		UserTitle:   int8(userTitle),
		Status:      1,
		CreateTime:  now,
		ModifyTime:  now,
	}
	if createApp_RoleErr := tx.Create(&app_role).Error; createApp_RoleErr != nil {
		tx.Rollback()
		return nil, "", createApp_RoleErr
	}

	tx.Commit()
	return &adminUser, password, nil
}

type PurviewTreeViewModel struct {
	ID     int64                   `json:"id"`
	PID    int64                   `json:"pid"`
	Name   string                  `json:"name"`
	Childs []*PurviewTreeViewModel `json:"childs"`
}

func GetAllGeneralPurviewVMsProcessed() ([]*PurviewTreeViewModel, error) {
	var originPurviews []*models.Purview
	getPurviewErr := readUserDb.Model(models.Purview{}).Where("module = 3 AND status = 1 AND super_admin_exclusive = 0").Order("listorder asc").Order("id asc").Find(&originPurviews).Error
	if getPurviewErr != nil {
		return nil, getPurviewErr
	}
	// 加工这些规则：树形化
	purviewVMs := make([]*PurviewTreeViewModel, 0)
	pid_childs := make(map[int64][]*PurviewTreeViewModel)
	for _, purview := range originPurviews {
		// warning：下面这个算法只适用最多两层树形结构的菜单，对于两层以上的会丢失掉第三层及其以下的节点
		// 因为取出 originPurviews 的时候已经排过序了，所以顶级节点肯定最先处理，不需要担心子节点比父节点先处理
		pvm := &PurviewTreeViewModel{
			ID:   purview.Id,
			PID:  purview.Parentid,
			Name: purview.Name,
		}
		if purview.Parentid == 0 {
			purviewVMs = append(purviewVMs, pvm)
		} else {
			childs := pid_childs[purview.Parentid]
			if childs == nil {
				childs = make([]*PurviewTreeViewModel, 0)
			}
			childs = append(childs, pvm)
			pid_childs[purview.Parentid] = childs
		}
	}

	for _, vm := range purviewVMs {
		vm.Childs = pid_childs[vm.ID]
	}

	return purviewVMs, nil
}

func GetRolePurviewIds(roleID int64) (string, error) {
	var rolePurview models.RolePurview
	err := readUserDb.Where("role_id = ?", roleID).First(&rolePurview).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return "", nil
		} else {
			return "", err
		}
	}
	return rolePurview.PurviewIds, nil
}

func SaveRolePurviewIds(orgID int64, appID int64, roleID int64, purviewIds string) error {
	var rolePurview models.RolePurview
	getRPErr := readUserDb.Where("org_id = ? AND app_id = ? AND role_id = ?", orgID, appID, roleID).First(&rolePurview).Error
	if getRPErr != nil {
		if getRPErr == gorm.ErrRecordNotFound {
			rolePurview = models.RolePurview{
				RoleId:     roleID,
				OrgId:      orgID,
				AppId:      appID,
				Status:     1,
				CreateTime: time.Now().Unix(),
			}
		} else {
			return getRPErr
		}
	}
	rolePurview.PurviewIds = purviewIds
	rolePurview.ModifyTime = time.Now().Unix()
	tx := writeUserDb.Begin()
	if err := tx.Save(&rolePurview).Error; err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}

func ModifyAdminUserInfo(adminUserID int64, orgID int64, appID int64, name string, avatar string, newPassword string) error {
	tx := writeUserDb.Begin()
	editInfoErr := tx.Exec("update sgj_user_admin_role set user_name = ?, avatar = ?, mtime = ? where admin_user_id = ? and org_id = ?", name, avatar, time.Now().Unix(), adminUserID, orgID).Error
	if editInfoErr != nil {
		tx.Rollback()
		return editInfoErr
	}
	if len(newPassword) > 0 {
		editPwdErr := tx.Exec("update sgj_user_admin set password = ?, mtime = ? where id = ?", newPassword, time.Now().Unix(), adminUserID).Error
		if editPwdErr != nil {
			tx.Rollback()
			return editPwdErr
		}
	}
	tx.Commit()
	return nil
}
