package controllers

import (
	"fmt"
	"net/url"
	"time"

	"SSO/enums"
	"SSO/models"
	"SSO/service"
	"SSO/utils"

	"github.com/astaxie/beego"
)

type LoginController struct {
	BaseController
}

// /login [get]
// @param app_type:int
// @param returnurl?:string
// @param relogin?:bool 是否强制重新登录
func (this *LoginController) PwdLogin() {
	token := this.Ctx.GetCookie("sso_token_cookie")
	returnURL := this.GetString("returnurl")
	appType, _ := this.GetInt("app_type")
	relogin, _ := this.GetBool("relogin", false)
	if relogin {
		token = ""
	}

	if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
		appType = 0
		returnURL = ""
	}
	utils.TraceLog("login token: %v", token)

	if len(token) == 0 {
		this.Data["app_type"] = appType
		this.Data["return_url"] = returnURL
		this.TplName = "new_main/login.html"
	} else {
		if len(returnURL) == 0 {
			this.Data["app_type"] = appType
			this.Data["return_url"] = returnURL
			this.TplName = "new_main/login.html"
		} else {
			utils.TraceLog("跳过登录直接验证token")
			url := this.appendTokenParamToReturnURL(returnURL, token)
			this.Redirect(url, 302)
		}
	}
}

// /login/code [get]
// @param app_type:int
// @param returnurl?:string
func (this *LoginController) CodeLogin() {
	token := this.Ctx.GetCookie("sso_token_cookie")
	returnURL := this.GetString("returnurl")
	appType, _ := this.GetInt("app_type")

	if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
		appType = 0
		returnURL = ""
	}
	utils.TraceLog("login token: %v", token)

	if len(token) != 0 && len(returnURL) != 0 {
		utils.TraceLog("跳过登录直接验证token")
		url := this.appendTokenParamToReturnURL(returnURL, token)
		this.Redirect(url, 302)

	} else {
		redisClient := service.RedisClient()
		defer redisClient.Close()
		req := this.Ctx.Request
		addr := utils.GetIP(req)
		cur_time := time.Now().Format("2006-01-02")
		_, err := redisClient.Get("ip:host_" + cur_time + "_" + addr).Result()
		if err != nil {
			redisClient.Set("ip:host_"+cur_time+"_"+addr, 0, time.Second*24*60*60)
		}

		//将客户端的ip加密传给前端,作为短信验证的密钥,来验证短信发送的IP地址
		aespass := utils.AESEncrypt(addr)
		this.Data["aespass"] = aespass

		this.Data["app_type"] = appType
		this.Data["return_url"] = returnURL
		this.TplName = "new_main/login_by_code.html"
	}
}

// /login/submit [post]
// @param app_type:int
// @param mobile:string
// @param password:string
// @param returnurl?:string
// 登录成功时,返回下一步跳转的链接:data.url
func (this *LoginController) PwdLoginSubmit() {
	appType, _ := this.GetInt("app_type")

	utils.TraceLog("login app_type=%v", appType)
	if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
		utils.TraceLog("login app_type not exist")
		appType = 0
	}
	mobile := this.GetString("mobile")
	password := this.GetString("password")
	if len(mobile) == 0 || len(password) == 0 {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
		this.ServeJSON()
		return
	}
	if service.IsPasswordRight(mobile, password) {
		token := utils.GenerateLoginToken(mobile)
		returnURL := this.GetString("returnurl")
		utils.TraceLog("login submit return url: %v", returnURL)
		url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
		if err != nil {
			this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
			this.ServeJSON()

		} else {
			if admin != nil {
				this.SetSession("admin_user", admin)
			}

			// 保存登录令牌
			expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
			this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")

			redisClient := service.RedisClient()
			defer redisClient.Close()
			share_session_id := this.Ctx.Input.CruSession.SessionID()
			utils.TraceLog("share_session_id: %v", share_session_id)
			this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
			redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)

			this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
				"url": url,
			})
			this.ServeJSON()
		}

	} else {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrPasswordWrong)
		this.ServeJSON()
	}
}

// /login/code/submit [post]
// @param app_type:int
// @param mobile:string
// @param code:string
// @param returnurl?:string
// 登录成功时,返回下一步跳转的链接:data.url
func (this *LoginController) CodeLoginSubmit() {
	appType, _ := this.GetInt("app_type")
	if url := service.GetAppURLWithAppType(appType); len(url) == 0 {
		appType = 0
	}
	mobile := this.GetString("mobile")
	code := this.GetString("code")
	if len(mobile) == 0 || len(code) == 0 {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
		this.ServeJSON()
		return
	}
	if !service.IsMobileRegister(mobile) {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
		this.ServeJSON()
		return
	}
	redisClient := service.RedisClient()
	defer redisClient.Close()
	cachedCode, err := redisClient.Get("code_msg_" + mobile).Result()
	if err != nil {
		this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
		this.ServeJSON()
		return

	} else {
		if code != cachedCode {
			this.Data["json"] = enums.MakeFailResponseJSONWithSGJErrorCode(enums.ErrorCodeAccountOrVerCodeWrong)
			this.ServeJSON()
			return
		} else {
			token := utils.GenerateLoginToken(mobile)
			returnURL := this.GetString("returnurl")
			url, err, admin := this.getRedirectURL(appType, mobile, returnURL, token)
			if err != nil {
				this.Data["json"] = enums.MakeFailResponseJSONWithSGJError(err)
				this.ServeJSON()

			} else {
				if admin != nil {
					this.SetSession("admin_user", admin)
				}

				// 保存登录令牌
				expiration, _ := beego.AppConfig.Int64("login_token_expiration_second")
				this.Ctx.SetCookie("sso_token_cookie", token, expiration, "/")

				share_session_id := this.Ctx.Input.CruSession.SessionID()
				utils.TraceLog("share_session_id: %v", share_session_id)
				this.Ctx.SetCookie("s", share_session_id, expiration, "/", beego.AppConfig.String("cookie_rootdomain"))
				redisClient.Set(fmt.Sprintf("sso_token_%v", share_session_id), token, time.Duration(expiration)*time.Second)

				// 清除验证码
				redisClient.Del("code_msg_" + mobile)

				this.Data["json"] = enums.MakeSuccessResponseJSON(map[string]interface{}{
					"url": url,
				})
				this.ServeJSON()
			}
		}
	}
}

func (this *LoginController) appendTokenParamToReturnURL(returnURL string, token string) string {
	urlStr, _ := url.QueryUnescape(returnURL)
	u, _ := url.Parse(urlStr)
	query := u.Query()
	query.Del("token")
	query.Add("token", token)
	u.RawQuery = query.Encode()
	return u.String()
}

func (this *LoginController) getRedirectURL(app_type int, mobile string, returnURL string, token string) (string, *enums.SGJError, *models.AdminUser) {
	utils.TraceLog("returnURL: %v", returnURL)
	admin := service.GetAdminUserWithMobile(mobile)
	if admin != nil {
		if admin.IsSuperAdmin { // 如果是超级管理员用户
			// 那么需要检查是否创建了机构和应用,如未创建,则前往创建
			// 如果都创建了,那么和普通管理员用户一样,需要经过其他判断得出下一步跳转的链接
			if didCreateOrg, findOrgErr := service.DidAdminUserCreateOrg(admin.Id); findOrgErr != nil {
				utils.ErrorLog("数据错误:查找mobile = %v的用户创建的机构时错误:%v", mobile, findOrgErr)
				return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
			} else {
				if didCreateOrg {
					if didCreateApp, findAppErr := service.DidAdminUserOrgCreateApp(admin.Id, app_type); findAppErr != nil {
						utils.ErrorLog("数据错误:查找mobile = %v的用户创建的应用时错误:%v", mobile, findOrgErr)
						return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
					} else {
						if !didCreateApp {
							url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.CreateApp"), token)
							return url, nil, admin
						}
					}

				} else {
					url := this.appendTokenParamToReturnURL(beego.URLFor("OrgController.Create"), token) // 前往创建机构的 url
					return url, nil, admin
				}
			}
		}
		if len(returnURL) != 0 {
			url := this.appendTokenParamToReturnURL(returnURL, token)
			return url, nil, admin
		}
		utils.TraceLog("login redirect url: app_type=%v", app_type)
		// 普通管理员用户
		// 如果有登录记录,则前往最近登录记录中的应用
		lastLoginLog, getLastLoginLogErr := service.GetAdminUserLastLoginLog(admin.Id, app_type)
		if getLastLoginLogErr != nil {
			utils.ErrorLog("数据错误:查找mobile = %v的用户最近一次登录记录时错误:%v", mobile, getLastLoginLogErr)
		}
		if lastLoginLog != nil {
			utils.TraceLog("%+v", lastLoginLog)
			_, getAppRoleErr := service.GetAppRole(admin.Id, lastLoginLog.OrgId, lastLoginLog.AppId)
			if getAppRoleErr != nil {
				utils.ErrorLog("数据错误:查找mobile = %v的用户的应用角色时错误:%v", mobile, getAppRoleErr)
			} else {
				url := service.GetAppURLWithAppType(int(lastLoginLog.AppType))
				if len(url) > 0 {
					return this.appendTokenParamToReturnURL(url, token), nil, admin
				}
			}
		}
		// 如果没有登录记录,则要前往其有权访问的、最高优先级的应用
		appType, getAppTypeErr := service.GetAdminUserPrioritizedAppType(admin.Id)
		if getAppTypeErr != nil {
			utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时错误:%v", mobile, getAppTypeErr)
			return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil

		} else {
			if appType == 0 {
				utils.ErrorLog("数据错误:查找mobile = %v的用户有权访问的应用的类型时的结果错误:%v", mobile, "在超级管理员已创建应用,并且已分配角色的情况下,不可能存在查找不到相关应用")
				return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
			} else {
				url := this.appendTokenParamToReturnURL(service.GetAppURLWithAppType(int(appType)), token)
				return url, nil, admin
			}
		}
	} else {
		utils.ErrorLog("数据错误:查找不到mobile = %v的用户", mobile)
		return "", &enums.SGJError{Code: enums.ErrorCodeDataException}, nil
	}
}

// /logout [get]
func (this *LoginController) Logout() {
	// 有 session.AdminUser 的话,销毁掉
	adminUserObj := this.GetSession("admin_user")
	redisClient := service.RedisClient()
	defer redisClient.Close()
	// 销毁 token
	this.Ctx.SetCookie("sso_token_cookie", "")
	redisClient.Del(fmt.Sprintf("sso_token_%v", this.Ctx.GetCookie("s")))
	if adminUserObj == nil {
		this.Redirect302(beego.URLFor("LoginController.Index"))
		return
	}
	adminUser := adminUserObj.(*models.AdminUser)
	this.DelSession("admin_user")
	redisClient.Del("sso_admin_user_info_" + adminUser.Mobile)

	loginLog := &models.AdminUserLoginLog{
		AdminUserId: adminUser.Id,
		OrgId:       0,
		AppId:       0,
		IP:          utils.GetIP(this.Ctx.Request),
		OperateType: 2,
		AppType:     0,
		CreateTime:  time.Now().Unix(),
	}
	if insertErr := service.InsertLoginLog(loginLog); insertErr != nil {
		utils.ErrorLog("为手机号为%v的用户插入一条登录记录失败:%v", adminUser.Mobile, insertErr)
	}
	// 通知其他子系统退出登录

	// 跳转首页
	this.Redirect302(beego.URLFor("LoginController.Index"))
}