package services

import (
	"encoding/json"
	"errors"
	"eta/eta_mobile/models"
	"eta/eta_mobile/models/company"
	"eta/eta_mobile/models/system"
	"eta/eta_mobile/utils"
	"fmt"
	"github.com/go-ldap/ldap"
	"io"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// SendAdminMobileVerifyCode 发送用户手机验证码
func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, err error) {
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("SendAdminMobileVerifyCode ErrMsg: %s", err.Error())
			utils.FileLog.Info(tips)
			fmt.Println(tips)
		}
	}()
	smsClient, e := NewSmsClient()
	if e != nil {
		err = fmt.Errorf("NewSmsClient err: %s", e.Error())
		return
	}

	verifyCode := utils.GetRandDigit(6)
	record := new(system.AdminVerifyCodeRecord)
	record.VerifyType = system.AdminVerifyCodeRecordTypeMobile
	record.Mobile = mobile
	record.Source = source
	record.Code = verifyCode
	record.ExpiredTime = time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute)
	record.CreateTime = time.Now().Local()
	record.ModifyTime = time.Now().Local()
	if e := record.Create(); e != nil {
		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
		return
	}

	var smsReq UserLoginSmsCodeReq
	smsReq.Mobile = mobile
	smsReq.TelAreaCode = areaCode
	smsReq.VerifyCode = verifyCode
	smsResult, e := smsClient.SendUserLoginCode(smsReq)
	if e != nil {
		err = fmt.Errorf("SendUserLoginCode err: %s", e.Error())
		return
	}
	ok = smsResult.Success

	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
	if !ok {
		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
	}
	record.RequestId = smsResult.RequestId
	cols := []string{"SendStatus", "RequestId"}
	if e := record.Update(cols); e != nil {
		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
	}
	return
}

// SendAdminEmailVerifyCode 发送用户邮箱验证码
func SendAdminEmailVerifyCode(source int, email string) (ok bool, err error) {
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("SendAdminEmailVerifyCode ErrMsg: %s", err.Error())
			utils.FileLog.Info(tips)
			fmt.Println(tips)
		}
	}()
	// 读取配置
	confMap, e := models.GetBusinessConf()
	if e != nil {
		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
		return
	}
	subjectConf := confMap[models.BusinessConfLoginEmailTemplateSubject]
	contentConf := confMap[models.BusinessConfLoginEmailTemplateContent]
	if subjectConf == "" {
		err = fmt.Errorf("请先配置邮件模版主题")
		return
	}
	if contentConf == "" {
		err = fmt.Errorf("请先配置邮件模版内容")
		return
	}
	verifyCode := utils.GetRandDigit(6)
	t := time.Now().Format("2006年01月02日")
	emailContent := contentConf
	emailContent = strings.Replace(emailContent, "{{VERIFY_CODE}}", verifyCode, 1)
	emailContent = strings.Replace(emailContent, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
	emailContent = strings.Replace(emailContent, "{{DATE_TIME}}", t, 1)

	// 验证码记录
	record := new(system.AdminVerifyCodeRecord)
	record.VerifyType = system.AdminVerifyCodeRecordTypeEmail
	record.Email = email
	record.Source = source
	record.Code = verifyCode
	record.ExpiredTime = time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute)
	record.CreateTime = time.Now().Local()
	record.ModifyTime = time.Now().Local()
	if e := record.Create(); e != nil {
		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
		return
	}

	var result string
	if confMap[models.BusinessConfEmailClient] == models.BusinessConfEmailClientSmtp {
		// 普通邮箱
		var emailReq SendEmailReq
		emailReq.Title = subjectConf
		emailReq.Content = emailContent
		emailReq.ToUser = append(emailReq.ToUser, email)
		ok, e = SendEmail(emailReq)
		if e != nil {
			err = fmt.Errorf("邮箱推送失败, Err: %s", e.Error())
			return
		}
	} else {
		// 默认阿里云邮箱
		// 读取发信人昵称配置...后面可以优化一下
		authKey := "english_report_email_conf"
		emailConf, e := company.GetConfigDetailByCode(authKey)
		if e != nil {
			err = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
			return
		}
		if emailConf.ConfigValue == "" {
			err = fmt.Errorf("邮件配置为空, 不可推送")
			return
		}
		conf := new(models.EnglishReportEmailConf)
		if e = json.Unmarshal([]byte(emailConf.ConfigValue), &conf); e != nil {
			err = fmt.Errorf("邮件配置有误, 不可推送")
			return
		}

		req := new(EnglishReportSendEmailRequest)
		req.Subject = subjectConf
		req.Email = email
		req.FromAlias = conf.FromAlias // 发信人昵称
		req.HtmlBody = emailContent

		aliEmail := new(AliyunEmail)
		o, r, e := aliEmail.SendEmail(req)
		if e != nil {
			err = fmt.Errorf("阿里云邮箱推送失败, Err: %s", e.Error())
			return
		}
		ok = o
		result = r
	}

	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
	if !ok {
		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
	}
	record.SendResult = result
	cols := []string{"SendStatus", "SendResult"}
	if e = record.Update(cols); e != nil {
		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
	}
	return
}

// LdapUserCheck AD域用户校验
func LdapUserCheck(userName, password string) (pass bool, err error) {
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("LdapUserCheck ErrMsg: %s", err.Error())
			utils.FileLog.Info(tips)
			fmt.Println(tips)
		}
	}()
	if userName == "" || password == "" {
		err = fmt.Errorf("账号密码有误")
		return
	}
	confMap, e := models.GetBusinessConf()
	if e != nil {
		err = fmt.Errorf("GetBusinessConf err: %s", e.Error())
		return
	}
	if confMap[models.BusinessConfLdapHost] == "" || confMap[models.BusinessConfLdapBase] == "" {
		err = fmt.Errorf("AD域配置有误")
		return
	}
	ldapPort, _ := strconv.Atoi(confMap[models.BusinessConfLdapPort])
	if ldapPort <= 0 {
		err = fmt.Errorf("AD域端口号有误, Port: %d", ldapPort)
		return
	}

	// 连接ldap
	addr := fmt.Sprintf("%s:%d", confMap[models.BusinessConfLdapHost], ldapPort)
	conn, e := ldap.Dial("tcp", addr)
	if e != nil {
		err = fmt.Errorf("ldap Dial err: %s", e.Error())
		return
	}
	defer conn.Close()

	// 绑定用户
	bindUserName := fmt.Sprintf("%s%s", userName, confMap[models.BusinessConfLdapBindUserSuffix])
	if e = conn.Bind(bindUserName, password); e != nil {
		err = fmt.Errorf("ldap Bind err: %s", e.Error())
		return
	}

	// 鉴权操作
	searchRequest := ldap.NewSearchRequest(
		confMap[models.BusinessConfLdapBase],
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		fmt.Sprintf(confMap[models.BusinessConfLdapUserFilter], userName),
		[]string{"dn"},
		nil,
	)
	//b, _ := json.Marshal(searchRequest)
	//fmt.Println("searchRequest: ", string(b))

	sr, e := conn.Search(searchRequest)
	if e != nil {
		err = fmt.Errorf("ldap Search err: %s", e.Error())
		return
	}

	// 验证结果
	if len(sr.Entries) != 1 {
		utils.FileLog.Info("ldap check fail: user does not exist or too many entries returned")
		return
	}
	pass = true
	return
}

// ThirdLogin
// @Description: 第三方登录（换取token）
// @author: Roc
// @datetime 2024-01-30 16:09:18
// @param req map[string]interface{}
// @return data GetCrmTokenData
// @return err error
// @return errMsg string
func ThirdLogin(req map[string]interface{}) (data GetCrmTokenData, err error, errMsg string) {
	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
		authCode, ok := req["code"]
		if !ok {
			errMsg = "参数有误"
			err = errors.New("参数缺失, code")
			return
		}
		authCodeStr := fmt.Sprint(authCode)
		if authCodeStr == "" {
			errMsg = "参数有误"
			err = errors.New("参数缺失, AuthCode")
			return
		}

		data, err = CodeLoginFromMiddleServer(authCodeStr)

		return
	}

	data, err, errMsg = ThirdCodeLoginFromMiddleServer(req)

	// 普通的第三方

	return
}

// ThirdCodeLoginFromMiddleServer
// @Description:  第三方登录（向桥接服务换取token）
// @author: Roc
// @datetime 2024-01-30 16:09:35
// @param param map[string]interface{}
// @return tokenResp GetCrmTokenData
// @return err error
func ThirdCodeLoginFromMiddleServer(param map[string]interface{}) (tokenResp GetCrmTokenData, err error, errMsg string) {
	if utils.EtaBridgeUrl == `` || utils.EtaBridgeLoginUrl == "" {
		errMsg = `未配置第三方登录的桥接服务地址`
		err = errors.New(errMsg)
		return
	}
	data, e := json.Marshal(param)
	if e != nil {
		err = fmt.Errorf("data json marshal err: %s", e.Error())
		return
	}

	body := io.NopCloser(strings.NewReader(string(data)))
	client := &http.Client{}
	req, e := http.NewRequest("POST", utils.EtaBridgeUrl+utils.EtaBridgeLoginUrl, body)
	if e != nil {
		err = fmt.Errorf("http create request err: %s", e.Error())
		return
	}

	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)

	checkToken := utils.MD5(utils.EtaBridgeAppNameEn + utils.EtaBridgeMd5Key)
	req.Header.Set("Authorization", checkToken)
	resp, e := client.Do(req)
	if e != nil {
		err = fmt.Errorf("http client do err: %s", e.Error())
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, e := io.ReadAll(resp.Body)
	if e != nil {
		err = fmt.Errorf("resp body read err: %s", e.Error())
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("resp body is empty")
		return
	}
	// 生产环境解密, 注意有个坑前后的双引号
	if utils.RunMode == "release" {
		str := string(b)
		str = strings.Trim(str, `"`)
		b = utils.DesBase64Decrypt([]byte(str), utils.EtaBridgeDesKey)
	}

	result := new(GetCrmTokenDataResp)
	if e = json.Unmarshal(b, &result); e != nil {
		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
		utils.FileLog.Info("第三方登录（向桥接服务换取token）:\n" + string(b))
		return
	}
	if result.Code != 200 {
		errMsg = result.Msg
		err = fmt.Errorf("result: %s", string(b))
		return
	}
	tokenResp = result.Data

	return
}

// ThirdLogout
// @Description: 第三方登出
// @author: Roc
// @datetime 2024-01-30 16:09:18
// @param req map[string]interface{}
// @return data GetCrmTokenData
// @return err error
func ThirdLogout(accessToken string) (err error) {
	if utils.EtaBridgeUrl == "" || utils.EtaBridgeLogoutUrl == "" {
		// 未配置第三方登出的桥接服务地址
		return
	}
	params := map[string]interface{}{
		"access_token": accessToken,
	}
	data, e := json.Marshal(params)
	if e != nil {
		err = fmt.Errorf("data json marshal err: %s", e.Error())
		return
	}

	body := io.NopCloser(strings.NewReader(string(data)))
	client := &http.Client{}
	req, e := http.NewRequest("POST", utils.EtaBridgeUrl+utils.EtaBridgeLogoutUrl, body)
	if e != nil {
		err = fmt.Errorf("http create request err: %s", e.Error())
		return
	}

	contentType := "application/json;charset=utf-8"
	req.Header.Set("Content-Type", contentType)
	checkToken := utils.MD5(utils.EtaBridgeAppNameEn + utils.EtaBridgeMd5Key)
	req.Header.Set("Authorization", checkToken)
	resp, e := client.Do(req)
	if e != nil {
		err = fmt.Errorf("http client do err: %s", e.Error())
		return
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, e := io.ReadAll(resp.Body)
	if e != nil {
		err = fmt.Errorf("resp body read err: %s", e.Error())
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("resp body is empty")
		return
	}
	// 生产环境解密, 注意有个坑前后的双引号
	if utils.RunMode == "release" {
		str := string(b)
		str = strings.Trim(str, `"`)
		b = utils.DesBase64Decrypt([]byte(str), utils.EtaBridgeDesKey)
	}

	result := new(GetCrmTokenDataResp)
	if e = json.Unmarshal(b, &result); e != nil {
		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
		return
	}
	if result.Code != 200 {
		err = fmt.Errorf("result: %s", string(b))
		return
	}

	return
}
