package user

import (
	"backend/internal/domain"
	"backend/internal/utils/email_template"
	errs "backend/pkg/library/errors"
	mbr "backend/pkg/member/domain/member"
	member "backend/pkg/member/domain/usecase"
	"backend/pkg/notification/domain/usecase"
	"backend/pkg/permission/domain/token"
	"bytes"
	"context"
	"fmt"
	"html/template"
	"regexp"
	"strings"

	"backend/internal/svc"
	"backend/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type RequestVerificationCodeLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// NewRequestVerificationCodeLogic 請求發送驗證碼 (用於驗證信箱/手機)
func NewRequestVerificationCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RequestVerificationCodeLogic {
	return &RequestVerificationCodeLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *RequestVerificationCodeLogic) RequestVerificationCode(req *types.RequestVerificationCodeReq) (resp *types.RespOK, err error) {
	acc := ""
	ct := mbr.GenerateCodeTypeEmail
	switch req.Purpose {
	case "email_verification":
		if !isValidEmail(req.Account) {
			return nil, errs.InputInvalidFormatError("email is invalid")
		}
		acc = req.Account
		// 1. TODO 討論 email 不可以再被使用
		// 2. TODO 討論 email 跟我帳號是不是一樣(如果是用自己的信箱註冊的話)
	case "phone_verification":
		phone, isPhone := normalizeTaiwanMobile(req.Account)
		if !isPhone {
			return nil, errs.InputInvalidFormatError("phone number is invalid")
		}
		acc = phone
		// TODO 討論號碼有被用過就不可以再被使用了
		ct = mbr.GenerateCodeTypePhone
	default:
		return &types.RespOK{}, errs.InputInvalidRangeError("")
	}

	uid := token.UID(l.ctx)
	// 限制三分鐘內只可以發送一次
	rk := domain.GenerateVerifyCodeRedisKey.With(
		fmt.Sprintf("%s-%s", uid, req.Purpose),
	).ToString()

	// 拿不到不會出錯，DB 壞掉才會
	get, err := l.svcCtx.Redis.GetCtx(l.ctx, rk)
	if err != nil {
		return nil, errs.DBErrorError("failed to connect to redis").Wrap(err)
	}
	if get != "" {
		// 已經發送過驗證碼，返回提示
		return nil, errs.SysTooManyRequestError("code already sent, please wait 3min for system to send again")
	}

	// 生成驗證碼
	vcode, err := l.svcCtx.AccountUC.GenerateRefreshCode(l.ctx, member.GenerateRefreshCodeRequest{
		LoginID:  acc,
		CodeType: ct,
	})
	if err != nil {
		return nil, err
	}

	// 取得用戶資訊
	info, err := l.svcCtx.AccountUC.GetUserInfo(l.ctx, member.GetUserInfoRequest{
		UID: uid,
	})
	if err != nil {
		return nil, err
	}

	nickname := generateMsgName(&info)
	switch ct {
	case mbr.GenerateCodeTypeEmail:
		body, title, err := email_template.GetEmailTemplate(email_template.Language(info.PreferredLanguage), email_template.BindingEmail)
		if err != nil {
			e := errs.ResNotFoundError("failed to get correct email template")
			return nil, e
		}

		tmpl, err := template.New("BindEmailBody").Parse(body)
		if err != nil {
			e := errs.ResInvalidFormatError("failed to get correct email template")
			return nil, e
		}

		emailParams := email_template.ForgetPasswordEmailReq{
			Username:   nickname,
			VerifyCode: vcode.Data.VerifyCode,
		}

		var buf bytes.Buffer
		if err := tmpl.Execute(&buf, emailParams); err != nil {
			e := errs.ResInvalidFormatError("failed to build data")
			return nil, e
		}

		err = l.svcCtx.DeliveryUC.SendEmail(l.ctx, usecase.MailReq{
			To:         []string{req.Account},
			From:       l.svcCtx.Config.SMTPConfig.Sender,
			SenderName: l.svcCtx.Config.SMTPConfig.SenderName,
			Subject:    title,
			Body:       buf.String(),
		})
		if err != nil {
			e := errs.SvcThirdPartyError("failed to send email").Wrap(err)

			return nil, e
		}
	case mbr.GenerateCodeTypePhone:
		//// 送出手機號碼
		//templateResp, err := l.svcCtx.NotificationUseCase.GetSMSTemplateByTypeID(
		//	l.ctx, notificationModule.Language(info.PreferredLanguage), notificationModule.BindingPhone)
		//if err != nil {
		//	return nil, err
		//}
		//
		//fmt.Println(fmt.Sprintf("%s:%s", templateResp.Body, vcode.Data.VerifyCode))
		////err = l.svcCtx.NotificationUseCase.SendMessage(l.ctx, notificationModule.SMSMessageRequest{
		////	PhoneNumber:    acc,
		////	RecipientName:  nickname,
		////	MessageContent: fmt.Sprintf("%s:%s", templateResp.Body, vcode),
		////})
		////if err != nil {
		////	return nil, err
		////}
	case mbr.GenerateCodeTypeNone:
	case mbr.GenerateCodeTypeForgetPassword:
	default:
		return &types.RespOK{}, errs.InputInvalidRangeError("")
	}

	// 設置 Redis 鍵，並設置 3 分鐘的過期時間
	status, err := l.svcCtx.Redis.SetnxExCtx(l.ctx, rk, vcode.Data.VerifyCode, 60*3)
	if err != nil || !status {
		// 純記錄，前面都已經成功，就不報錯了
		_ = errs.DBErrorErrorL(l.svcCtx.Logger,
			[]errs.LogField{
				{Key: "req", Val: req},
				{Key: "func", Val: "Redis.SetnxExCtx"},
				{Key: "err", Val: err.Error()},
			}, "failed to set redis expire").Wrap(err)
	}

	return &types.RespOK{}, nil
}

// 標準化號碼並驗證是否為合法台灣手機號碼
func normalizeTaiwanMobile(phone string) (string, bool) {
	// 移除空格
	phone = strings.ReplaceAll(phone, " ", "")

	// 移除 "+886" 並將剩餘部分標準化
	if strings.HasPrefix(phone, "+886") {
		phone = strings.TrimPrefix(phone, "+886")
		if !strings.HasPrefix(phone, "0") {
			phone = "0" + phone
		}
	}

	// 正則表達式驗證標準化後的號碼
	regex := regexp.MustCompile(`^(09\d{8})$`)
	if regex.MatchString(phone) {
		return phone, true
	}

	return "", false
}

// 驗證 Email 格式的函數
func isValidEmail(email string) bool {
	// 定義正則表達式
	regex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)

	return regex.MatchString(email)
}

// generateMsgName 取得寄信用的名稱
func generateMsgName(info *member.UserInfo) string {
	if info.FullName != nil {
		return *info.FullName
	}
	if info.Nickname != nil {
		return *info.Nickname
	}

	return info.UID
}
