package controllers

import (
	"encoding/base64"
	"fmt"
	"net/http"
	"strings"

	"git.rosy.net.cn/jx-callback/business/jxstore/cms"

	"git.rosy.net.cn/jx-callback/globals/api"

	"git.rosy.net.cn/baseapi/platformapi/weixinapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/auth2"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding"
	_ "git.rosy.net.cn/jx-callback/business/auth2/authprovider/mobile" // 强制导入mobile认证方式
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/password"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"github.com/astaxie/beego/server/web"
)

func GetComposedCode(c *web.Controller, code string) (composedCode string) {
	composedCode = code
	referer := c.Ctx.Request.Referer()
	index := strings.Index(referer, "//")
	if index > 0 {
		list := strings.Split(referer[index+2:], "/")
		if len(list) >= 2 {
			composedCode = weixin.ComposeJsCode(list[1], code)
		}
	}
	return composedCode
}

func GetComposedCode2(c *web.Controller, code string) (composedCode string) {
	composedCode = code
	referer := c.Ctx.Request.UserAgent()
	index := strings.Index(referer, "//")
	if index > 0 {
		list := strings.Split(referer[index+2:], "/")
		if len(list) >= 2 {
			composedCode = weixin.ComposeJsCode(list[1], code)
		}
	}
	return composedCode
}

type Auth2Controller struct {
	web.Controller
}

// @Title 生成captcha
// @Description 生成captcha
// @Param	width		formData		int		true	"图片宽"
// @Param	height		formData		int		true	"图片高"
// @Param	captchaLen	formData		int		false	captcha码长度"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /CreateCaptcha [post]
func (c *Auth2Controller) CreateCaptcha() {
	c.callCreateCaptcha(func(params *tAuth2CreateCaptchaParams) (retVal interface{}, errCode string, err error) {
		retVal, err = auth2.CreateCaptcha(params.Width, params.Height, params.CaptchaLen)
		return retVal, "", err
	})
}

// @Title 发送验证码
// @Description 发送验证码（captcha码与authToken二者必须至少有一个）
// @Param	captchaID		formData		string		false	"captcha码ID"
// @Param	captchaValue	formData		string		false	"captcha码值"
// @Param	authToken		formData		string		false	"之前的认证信息"
// @Param	authID			formData		string		true	"手机号或邮件"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /SendVerifyCode [post]
func (c *Auth2Controller) SendVerifyCode() {
	c.callSendVerifyCode(func(params *tAuth2SendVerifyCodeParams) (retVal interface{}, errCode string, err error) {
		_, _, err = auth2.SendVerifyCode(params.AuthToken, params.CaptchaID, params.CaptchaValue, params.AuthID)
		// if err == nil && authInfo != nil {
		// 	user, err2 := dao.GetUserByID(dao.GetDB(), "user_id", authInfo.GetID())
		// 	if err2 == nil && user.Type&(model.UserTypeBoss|model.UserTypeOperator) != 0 {
		// 		retVal = code
		// 	}
		// }
		return retVal, "", err
	})
}

// @Title 抖音解密接口，获取用户电话
// @Description 抖音解密接口，获取用户电话
// @Param	iv			formData		string		true 	"加密字符偏移量"
// @Param	sessionKey	formData		string		true	"加密key"
// @Param	msg			formData		string		true	"加密消息"
// @Param	loginType	formData		string		true	"登录类型兼容抖音登录/快手[tiktokmini/kuaishoumini]"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /TiktokDecrypt [post]
func (c *Auth2Controller) TiktokDecrypt() {
	c.callTiktokDecrypt(func(params *tAuth2TiktokDecryptParams) (interface{}, string, error) {
		phone, err := auth2.DecryptUserMsg(params.SessionKey, params.Iv, params.Msg, params.LoginType)
		isExist := false
		if phone != "" {
			if user, err := dao.GetUserByID(dao.GetDB(), "mobile", phone); err == nil {
				if user != nil {
					isExist = true
				} else {
					isExist = false
				}
			}
		}
		return map[string]interface{}{"mobile": phone, "isExist": isExist}, "", err
	})
}

// @Title 登录接口
// @Description 登录接口（微信与公众号登录不能直接调用此接口）
// @Param	authType	formData		string		true	"登录类型，当前支持[kuaishou:快手授权登录,localpass：本地账号密码，mobile：手机短信，wxqrcode:微信登录，weixinsns：微信公众号，weixinmini；小程序，wxnative：微信APP，ddstaff：钉钉企业，ddqrcode：钉钉扫码，alipaycode：支付宝小程序]"
// @Param	authSecret	formData		string		true	"不同登录类型的登录秘密，如果是localpass登录类型，是md5后的值（空串不要md5）"
// @Param	authID		formData		string		false	"登录ID，登录类型为localpass时依赖于authIDType，其它为相应登录类型的id"
// @Param	authIDType	formData		string		false	"只有在登录类型为localpass时，才有意义，分别为：userid2：用户名，email，mobile"
// @Param	cId			formData		string		false	"用户手机设备id,只有app登录时做消息通知才有意义!登录时更新此字段!"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /Login [post]
func (c *Auth2Controller) Login() {
	c.callLogin(func(params *tAuth2LoginParams) (retVal interface{}, errCode string, err error) {
		if params.AuthType == auth2.AuthTypeMobile {
			params.AuthIDType = auth2.UserIDMobile
		} else if params.AuthType == auth2.AuthTypeEmail {
			params.AuthIDType = auth2.UserIDEmail
		}
		if params.AuthType == weixin.AuthTypeMini {
			params.AuthSecret = GetComposedCode(&c.Controller, params.AuthSecret)
		}
		if params.AuthType == weixin.AuthTypeWxApp || params.AuthType == weixin.AuthTypeWxAppCaishi {
			//if configs, _ := dao.QueryConfigs(dao.GetDB(), "checkversion", model.ConfigTypeSys, ""); len(configs) > 0 {
			//	if params.Version == "" || configs[0].Value != params.Version {
			//		return nil, "", fmt.Errorf("当前APP版本过旧，数据显示有错误，请到'京西菜市'公众号下载最新版本APP！")
			//	}
			//}
			params.AuthSecret = GetComposedCode2(&c.Controller, params.AuthSecret)
		}
		ctx := auth2.NewContext(c.Ctx.ResponseWriter, c.Ctx.Request)
		authInfo, err := auth2.Login(ctx, params.AuthType, params.AuthID, params.AuthIDType, params.AuthSecret)
		// TODO 兼容没有取到authid2的错误
		if err == nil && authInfo.AuthBindInfo != nil {
			if authInfo.AuthBindInfo.AuthID2 == "" {
				authInfo.AuthBindInfo.AuthID2 = authInfo.AuthBindInfo.AuthID
			}
			retVal = authInfo
		}
		if params.CId != "" && authInfo != nil {
			err = cms.SetUserCId(authInfo.UserID, params.CId)
			if dao.IsNoRowsError(err) {
				err = nil
			}
		}
		return retVal, "", err
	})
}

// @Title 得到自己登录token的信息
// @Description 得到自己登录token的信息
// @Param	token		header		string	true	"认证token"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /GetTokenInfo [get]
func (c *Auth2Controller) GetTokenInfo() {
	c.callGetTokenInfo(func(params *tAuth2GetTokenInfoParams) (retVal interface{}, errCode string, err error) {
		retVal, err = auth2.GetTokenInfo(params.Token)
		if err == model.ErrTokenIsInvalid {
			errCode = model.ErrCodeTokenIsInvalid
		}
		return retVal, errCode, err
	})
}

// @Title 微信扫码认证回调接口
// @Description 微信扫码认证回调接口，自己不能直接调用
// @Param	code		query		string		true	"客户同意后得到的code"
// @Param	block		query		string		true	"回调地址"
// @Param	state		query		string		false	"微信回调的登录状态"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /WeixinOAuth2 [get]
func (c *Auth2Controller) WeixinOAuth2() {
	var (
		redirectURL string
		callResult  *CallResult
	)
	c.callWeixinOAuth2(func(params *tAuth2WeixinOAuth2Params) (retVal interface{}, errCode string, err error) {
		ctx := auth2.NewContext(c.Ctx.ResponseWriter, c.Ctx.Request)
		authInfo, err := auth2.Login(ctx, weixin.AuthTypeWeixin, params.State, "", params.Code)
		if err == nil {
			callResult = &CallResult{
				Code: model.ErrCodeSuccess,
				Data: string(utils.MustMarshal(authInfo)),
			}
		} else {
			callResult = &CallResult{
				Code: model.ErrCodeGeneralFailed,
				Desc: err.Error(),
			}
		}
		redirectURL = fmt.Sprintf("%s?info=%s", params.Block, base64.StdEncoding.EncodeToString(utils.MustMarshal(callResult)))
		return retVal, model.ErrorCodeIgnore, err
	})
	c.Redirect(redirectURL, http.StatusTemporaryRedirect)
}

// @Title 微信公众号认证回调接口
// @Description 微信公众号认证回调接口，自己不能直接调用
// @Param	code		query		string		true	"客户同意后得到的code"
// @Param	block		query		string		true	"回调地址"
// @Param	state		query		string		false	"微信回调的登录状态"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /WeixinMPOAuth2 [get]
func (c *Auth2Controller) WeixinMPOAuth2() {
	var (
		redirectURL string
		callResult  *CallResult
	)
	c.callWeixinMPOAuth2(func(params *tAuth2WeixinMPOAuth2Params) (retVal interface{}, errCode string, err error) {
		ctx := auth2.NewContext(c.Ctx.ResponseWriter, c.Ctx.Request)
		authInfo, err := auth2.Login(ctx, weixin.AuthTypeMP, params.State, "", params.Code)
		if err == nil {
			callResult = &CallResult{
				Code: model.ErrCodeSuccess,
				Data: string(utils.MustMarshal(authInfo)),
			}
		} else {
			callResult = &CallResult{
				Code: model.ErrCodeGeneralFailed,
				Desc: err.Error(),
			}
		}
		if params.Block != "" {
			if mobile := params.Block[strings.LastIndex(params.Block, "=")+1:]; mobile != "" {
				if user, err := dao.GetUserByID(dao.GetDB(), "mobile", mobile); err == nil {
					if user != nil {
						authInfo.AuthBindInfo.UserID = user.UserID
						authInfo.AuthBindInfo.AuthBind.UserID = user.UserID
						auth2.AddAuthBind(user, authInfo)
					} else {
						return retVal, errCode, fmt.Errorf("未查询到用户！mobile :%v", mobile)
					}
				}
			}
		}
		if strings.Contains(params.Block, "?") {
			params.Block = params.Block[:strings.LastIndex(params.Block, "?")]
		}
		redirectURL = fmt.Sprintf("%s?info=%s", params.Block, base64.StdEncoding.EncodeToString(utils.MustMarshal(callResult)))
		return retVal, model.ErrorCodeIgnore, err
	})
	c.Redirect(redirectURL, http.StatusTemporaryRedirect)
}

// @Title 钉钉认证回调接口
// @Description 钉钉认证回调接口，自己不能直接调用
// @Param	code		query		string		true	"客户同意后得到的code"
// @Param	block		query		string		false	"回调地址"
// @Param	state		query		string		false	"微信回调的登录状态"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /DingDingOAuth2 [get]
func (c *Auth2Controller) DingDingOAuth2() {
	var (
		redirectURL string
		callResult  *CallResult
	)
	c.callDingDingOAuth2(func(params *tAuth2DingDingOAuth2Params) (retVal interface{}, errCode string, err error) {
		if params.Block == "" {
			params.Block = params.State
			params.State = ""
		}
		ctx := auth2.NewContext(c.Ctx.ResponseWriter, c.Ctx.Request)
		authInfo, err := auth2.Login(ctx, dingding.AuthTypeQRCode, params.State, "", params.Code)
		if err == nil {
			callResult = &CallResult{
				Code: model.ErrCodeSuccess,
				Data: string(utils.MustMarshal(authInfo)),
			}
		} else {
			callResult = &CallResult{
				Code: model.ErrCodeGeneralFailed,
				Desc: err.Error(),
			}
		}
		if params.Block != "" {
			redirectURL = fmt.Sprintf("%s?info=%s", params.Block, base64.StdEncoding.EncodeToString(utils.MustMarshal(callResult)))
		}
		return retVal, model.ErrorCodeIgnore, err
	})
	if redirectURL != "" {
		c.Redirect(redirectURL, http.StatusTemporaryRedirect)
	}
}

// @Title 登出接口
// @Description 登出接口（此接口兼容V1的TOKEN）
// @Param	token			header		string	true	"认证token"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /Logout [delete]
func (c *Auth2Controller) Logout() {
	c.callLogout(func(params *tAuth2LogoutParams) (retVal interface{}, errCode string, err error) {
		if authInfo, ok := params.Ctx.GetLoginInfo().(*auth2.AuthInfo); ok {
			err = auth2.Logout(authInfo)
		} else {
			// err = auth.Logout(params.Token)
		}
		return nil, "", err
	})
}

// @Title 绑定认证方式
// @Description  绑定认证方式
// @Param	token		header		string		true	"认证token"
// @Param	authToken	formData	string		true	"之前通过login得到的新认证TOKEN"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /AddAuthBind [post]
func (c *Auth2Controller) AddAuthBind() {
	c.callAddAuthBind(func(params *tAuth2AddAuthBindParams) (retVal interface{}, errCode string, err error) {
		authInfo, err := params.Ctx.GetV2AuthInfo()
		if err == nil {
			newAuthInfo, err := auth2.GetTokenInfo(params.AuthToken)
			if err == nil {
				err = auth2.AddAuthBind(authInfo, newAuthInfo)
			}
		}
		return retVal, "", err
	})
}

// @Title 绑定认证方式
// @Description  绑定认证方式
// @Param	token		header		string		false	"认证token"
// @Param	authToken	formData	string		true	"之前通过login得到的新认证TOKEN"
// @Param	mobile		formData	string		true	"之前通过getuserbyminiinfo得到的"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /AddAuthBindWithMobile [post]
func (c *Auth2Controller) AddAuthBindWithMobile() {
	c.callAddAuthBindWithMobile(func(params *tAuth2AddAuthBindWithMobileParams) (retVal interface{}, errCode string, err error) {
		newAuthInfo, err := auth2.GetTokenInfo(params.AuthToken)
		if err != nil {
			return retVal, "", err
		}
		err = auth2.AddAuthBindWithMobile(newAuthInfo, params.Mobile)
		return retVal, "", err
	})
}

// @Title 删除认证方式
// @Description  删除认证方式
// @Param	token		header		string		true	"认证token"
// @Param	authType	query		string		true	"登录类型，参见Login的描述"
// @Param	authTypeID	query		string		false	"登录类型标识"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /RemoveAuthBind [delete]
func (c *Auth2Controller) RemoveAuthBind() {
	c.callRemoveAuthBind(func(params *tAuth2RemoveAuthBindParams) (retVal interface{}, errCode string, err error) {
		authInfo, err2 := params.Ctx.GetV2AuthInfo()
		if err = err2; err == nil {
			err = auth2.UnbindAuth(authInfo.GetID(), params.AuthType, params.AuthTypeID, params.Ctx.GetUserName())
		}
		return retVal, "", err
	})
}

// @Title 修改（或初始化）密码
// @Description 修改（或初始化）密码
// @Param	token			header		string	true	"认证token"
// @Param	oldPwd			query		string	false	"原密码md5，如果是重置或新设，为空"
// @Param	newPwd			query		string	true	"新密码md5"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /ChangePassword [put]
func (c *Auth2Controller) ChangePassword() {
	c.callChangePassword(func(params *tAuth2ChangePasswordParams) (retVal interface{}, errCode string, err error) {
		authInfo, err := params.Ctx.GetV2AuthInfo()
		if err == nil {
			err = password.AutherObj.ChangePassword(authInfo.GetID(), params.Ctx.GetUserName(), params.OldPwd, params.NewPwd)
		}
		return retVal, "", err
	})
}

// @Title 清除除参数token以外的这个人的tokenSendStoreMessage
// @Description 清除除参数token以外的这个人的token
// @Param	token			header		string	true	"认证token"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /DeletedTokenInfoWithoutParam [put]
func (c *Auth2Controller) DeletedTokenInfoWithoutParam() {
	c.callDeletedTokenInfoWithoutParam(func(params *tAuth2DeletedTokenInfoWithoutParamParams) (retVal interface{}, errCode string, err error) {
		newAuthInfo, err2 := auth2.GetTokenInfo(params.Token)
		if err = err2; err == nil {
			err = auth2.DeletedTokenInfoWithoutParam(newAuthInfo)
		}
		return retVal, "", err
	})
}

type UserInfoWithWeixin struct {
	Mobile  string `json:"mobile"`
	IsExist bool   `json:"isExist"`
}

// @Title 根据小程序jsCode查询用户信息
// @Description 根据小程序jsCode查询用户信息
// @Param	token			header				string		false	"认证token"
// @Param	data			formData			string		true	"加密数据"
// @Param	iv				formData			string		true	"iv"
// @Param	jsCode			formData			string		false	"小程序jsCode"
// @Param	authType		formData			string		false	"authType"
// @Param	appID			formData			string		false	"appID"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /GetUserByMiniInfo [post]
func (c *Auth2Controller) GetUserByMiniInfo() {
	c.callGetUserByMiniInfo(func(params *tAuth2GetUserByMiniInfoParams) (retVal interface{}, errCode string, err error) {
		authInfo := &auth2.AuthInfo{}
		if params.JsCode == "" {
			result := api.Cacher.Get(params.Token)
			utils.Map2StructByJson(result.(map[string]interface{}), &authInfo, false)
		}
		str := []string{
			params.AppID,
			params.JsCode,
		}
		jsCode := strings.Join(str, ",")
		if err == nil {
			decryptedDataBase64, err2 := weixin.AutherObjMini.DecryptData(authInfo, jsCode, params.Data, params.Iv)
			if err = err2; err == nil {
				var userInfo *weixinapi.MiniUserInfo
				if err = utils.UnmarshalUseNumber([]byte(decryptedDataBase64), &userInfo); err == nil {
					result := &UserInfoWithWeixin{}
					result.Mobile = userInfo.PhoneNumber
					if userInfo.PhoneNumber != "" {
						if user, err := dao.GetUserByID(dao.GetDB(), "mobile", userInfo.PhoneNumber); err == nil {
							if user != nil {
								result.IsExist = true
							} else {
								result.IsExist = false
							}
						}
					}
					retVal = result
				}
			}
		}
		return retVal, "", err
	})
}

// @Title 根据电话号码获取用户头肯
// @Description 根据电话号码获取用户头肯
// @Param	phoneNumber		query		string		true	"电话号码"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /GetTokenByPhone [get]
func (c *Auth2Controller) GetTokenByPhone() {
	c.callGetTokenByPhone(func(params *tAuth2GetTokenByPhoneParams) (retVal interface{}, errCode string, err error) {
		result := api.Cacher.Get(params.PhoneNumber)
		if result == nil {
			err = fmt.Errorf("暂未获取到此用户信息")
		}
		return result, "", err
	})
}
