package api

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

	"github.com/google/uuid"
	"github.com/pingcap/errors"
	"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/svc/invite"
	"github.com/tidbcloud/account/internel/svc/token"
	"github.com/tidbcloud/account/internel/svc/user"
	"github.com/tidbcloud/account/thirdparty/account"

	"github.com/tidbcloud/account/internel/dto"

	"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"
)

// CreateMCPOResellerOrg
// @Summary CreateMCPOResellerOrg
// @tags msp
// @Accept  json
// @Produce json
// @Param MCPOOrg body forms.MCPOResellerOrgReq true "CreateMCPOResellerOrg"
// @Success 200 {object} forms.MCPOResellerOrgResp
// @Router /partner/mcpo/create [post]
func CreateMCPOResellerOrg(c *gin.Context) {
	ctx := c.Request.Context()

	req := forms.MCPOResellerOrgReq{}
	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, "CreateMCPOResellerOrg start", zap.Any("req", req))
	//email, _ := GetUserEmail(c)

	svc, _ := msp.GetMspSvc()

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

	param := &invite.InviteUserParam{
		EmailList:       []string{req.Email},
		OrgID:           mcpoOrg.ID.Uint64(),
		Role:            entity.RolePartnerAdmin,
		WithoutEmail:    true,
		WithoutAuditLog: true,
	}
	inviteSvc, _ := invite.GetInviteUserSvc()
	_, err = inviteSvc.InviteUser(ctx, param)
	if err != nil {
		tilog.Error(ctx, "CreateMCPOResellerOrg 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, mcpoOrg.ID, token.InvitationVerificationMethod, mcpoOrg.Email, 30*24*time.Hour, true)
	if err != nil {
		tilog.Error(ctx, "CreateMCPOResellerOrg failed to create user invite token", zap.Any("orgId", mcpoOrg.ID), zap.String("email", mcpoOrg.Email), zap.Error(err))
		Fail(c, err, http.StatusInternalServerError)
		return
	}
	userSvc, _ := user.GetUserSvc()
	createKeyToken, err := userSvc.CreateInviteToken(ctx, mcpoOrg.ID.Uint64(), mcpoOrg.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 := sendMCPORegisteredEmail(ctx, mcpoOrg.Email, createKeyToken.TokenStr, inviteToken, mcpoOrg.Name, mcpoOrg.ID.Uint64()); err != nil {
		tilog.Error(ctx, "sendEmail err", zap.Any("TenantID", mcpoOrg.ID.Uint64()), zap.Error(err))
	}

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

	resp := forms.MCPOResellerOrgResp{
		OrgId: mcpoOrg.ID.Uint64(),
	}
	tilog.Info(ctx, "CreateMCPOResellerOrg  success", zap.Any("MCPOOrg", mcpoOrg))
	Success(c, resp, http.StatusOK)
}

func sendMCPORegisteredEmail(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 := "GCP MCPO Partner of TiDB Cloud Registration"
	tilog.Info(ctx, "sendMCPORegisteredEmail:"+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.TNMCPOResellerRegistered],

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

	return nil
}
