package api

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"time"

	"github.com/google/uuid"
	"github.com/tidbcloud/account/central/utils"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/account/commons/database"
	accountpb "github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/account/internel/dto"
	"github.com/tidbcloud/account/internel/svc/invite"
	orgsvc "github.com/tidbcloud/account/internel/svc/org"
	"github.com/tidbcloud/account/internel/svc/token"
	"github.com/tidbcloud/account/internel/svc/user"
	"github.com/tidbcloud/account/thirdparty/account"
	"github.com/tidbcloud/tiinf/errs"

	"github.com/tidbcloud/account/central/events/publish"

	"github.com/tidbcloud/account/internel/svc/msp"

	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/account/admin/forms"
)

// CreateResalePartnerOrg
// @Summary CreateResalePartnerOrg
// @tags msp
// @Accept  json
// @Produce json
// @Param resaleOrg body forms.ResellerOrgReq true "resale org"
// @Success 200 {object} forms.ResellerOrgResp
// @Router /partner/resale/create [post]
func CreateResalePartnerOrg(c *gin.Context) {
	ctx := c.Request.Context()

	req := forms.ResellerOrgReq{}
	if err := c.ShouldBindJSON(&req); err != nil {
		tilog.Error(ctx, "ShouldBindJSON fail:", zap.Any("req", req), zap.Error(err))
		Fail(c, err, http.StatusBadRequest)
		return
	}
	tilog.Info(ctx, "CreateCppoOrg start", zap.Any("req", req))
	//email, _ := GetUserEmail(c)

	svc, _ := msp.GetMspSvc()

	mspOrg, err := svc.CreateResalePartnerOrg(ctx, req)
	if err != nil {
		tilog.Info(ctx, "CreateCppoOrg fail:", zap.Any("req", req), zap.Error(err))
		Fail(c, err, http.StatusBadRequest)
		return
	}

	param := &invite.InviteUserParam{
		EmailList:       []string{req.Email},
		OrgID:           mspOrg.ID.Uint64(),
		Role:            entity.RolePartnerAdmin,
		WithoutEmail:    true,
		WithoutAuditLog: true,
	}
	inviteSvc, _ := invite.GetInviteUserSvc()
	_, err = inviteSvc.InviteUser(ctx, param)
	if err != nil {
		tilog.Error(ctx, "CreateCppoOrg InviteUser error", zap.Any("param", param), zap.Any("err", err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	sessionSvc, _ := token.GetSession()
	inviteToken, err := sessionSvc.InsertUserInviteToken(database.DBConn.DB, 0, mspOrg.ID, token.InvitationVerificationMethod, mspOrg.Email, 30*24*time.Hour, true)
	if err != nil {
		tilog.Error(ctx, "CreateCppoOrg failed to create user invite token", zap.Any("orgId", mspOrg.ID), zap.String("email", mspOrg.Email), zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	userSvc, _ := user.GetUserSvc()
	createKeyToken, err := userSvc.CreateInviteToken(ctx, mspOrg.ID.Uint64(), mspOrg.Email, config.MspCreateKey)
	if err != nil {

		tilog.Error(ctx, "get createKeyToken fail:", zap.Any("req", req), zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	if err := sendResalePartnerRegisteredEmail(ctx, mspOrg.Email, createKeyToken.TokenStr, inviteToken, mspOrg.Name, mspOrg.ID.Uint64()); err != nil {
		tilog.Error(ctx, "sendEmail err", zap.Any("TenantID", mspOrg.ID.Uint64()), zap.Error(err))
	}

	if err := publish.PublishOrgEvent(ctx, dto.OrgEventData{OrgId: mspOrg.ID.Uint64(), OrgEventType: entity.EntityEventTypeCreate}); err != nil {
		tilog.Error(ctx, "PublishOrgEvent err", zap.Any("TenantID", mspOrg.ID.Uint64()), zap.Error(err))
	}

	resp := forms.ResellerOrgResp{
		OrgId: mspOrg.ID.Uint64(),
	}
	tilog.Info(ctx, "Create Resale Org  success", zap.Any("resaleOrg", mspOrg))
	Success(c, resp, http.StatusOK)
}

func sendResalePartnerRegisteredEmail(ctx context.Context, email, createKeyToken, inviteToken, orgName string, orgID uint64) error {
	logger := tilog.WithContextL(ctx)

	url := getCreateApiKeyUrl(ctx, createKeyToken)
	tilog.Info(ctx, "getCreateApiKeyUrl", zap.Any("url", url))

	inviteUrl := fmt.Sprintf("https://%s/invitation-verification?token=%s", utils.GetCentralBackendDns(), inviteToken)
	subject := "TiDB Cloud Resale Partner Registration"
	tilog.Info(ctx, "send Resale RegisteredEmail:"+subject)
	escapeOrgName := utils.EscapeStr(ctx, orgName)

	vars := map[string]string{
		"partnerName": escapeOrgName,
		"consoleURL":  inviteUrl,
		"url":         url,
	}
	_, err := account.GetHandler().SendEmail(ctx, &accountpb.SendEmailReq{
		OrgId:      orgID,
		Scene:      string(entity.NotificationSceneInvite),
		Identifier: uuid.New().String(),
		Subject:    subject,
		Emails:     []string{email},
		CcEmails:   nil,
		TemplateId: config.SendgridTemplateName2id[config.TNResalePartnerRegistered],

		TemplateVars: vars,
	})
	if err != nil {
		logger.Error("failed to send cppo registration email", zap.String("feature", "invitation"), zap.String("email", email), zap.Error(err))
		return errors.New("failed send email")
	}
	logger.Info("send Resale Partner registration email", zap.String("feature", "registration"), zap.String("email", email), zap.Time("time", time.Now().UTC()))

	return nil
}

// GetResaleCustomerSignupUrl
// @Summary GetResaleCustomerSignupUrl
// @tags msp
// @Accept  json
// @Produce json
// @Param org_id path string true "org_id"
// @Success 200 {string} string	"signup_url"
// @Router /partner/resale/{org_id}/signup-url [get]
func GetResaleCustomerSignupUrl(c *gin.Context) {
	ctx := c.Request.Context()

	req := forms.GetOrgIDReq{}
	if err := c.ShouldBindUri(&req); err != nil {
		tilog.Error(ctx, "ShouldBindUri fail:", zap.Any("req", req), zap.Error(err))
		Fail(c, err, http.StatusBadRequest)
		return
	}
	tilog.Info(ctx, "GetSignupUrl start", zap.Any("req", req))
	orgSvc, _ := orgsvc.GetOrgSvc()
	mspOrg, err := orgSvc.GetOrgByID(ctx, req.OrgID)
	if err != nil || mspOrg == nil {
		Fail(c, errs.NewBadRequest("org_id is invalid", nil), http.StatusBadRequest)
		return
	}
	if mspOrg.OrgTag != string(entity.OrgTagResalePartner) {
		Fail(c, errs.NewBadRequest("org id is invalid", nil), http.StatusBadRequest)
		return
	}

	userSvc, _ := user.GetUserSvc()
	registerToken, err := userSvc.CreateInviteToken(ctx, mspOrg.ID.Uint64(), mspOrg.Email, config.ResaleCustomerRegister)
	if err != nil {
		tilog.Error(ctx, "get registerToken fail:", zap.Any("req", req), zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	signupUrl := fmt.Sprintf("https://%s/signup?reseller=%s&third_party_account_provider=%s&third_party_account_token=%s", config.Global().ServerConfig.FrontendDNS, mspOrg.Name, entity.ResaleThirdPartyAccountProvider, registerToken.TokenStr)
	//signupUrl = html.UnescapeString(signupUrl)
	tilog.Info(ctx, "GetSignupUrl success", zap.Any("signup_url", signupUrl))
	//obj := forms.MspOrg{RegistrationLink: signupUrl}
	SuccessPureJSON(c, signupUrl, http.StatusOK)
}
