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

// @Summary CreateCppoOrg
// @tags msp
// @Accept  json
// @Produce json
// @Param cppoOrg body forms.CppoOrgReq true "CreateCppoOrg"
// @Success 200 {object} forms.CppoOrgResp
// @Router /partner/cppo/create [post]
func CreateCppoOrg(c *gin.Context) {
	ctx := c.Request.Context()

	req := forms.CppoOrgReq{}
	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.CreateCppoOrg(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 := sendCppoRegisteredEmail(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.CppoOrgResp{
		OrgId: mspOrg.ID.Uint64(),
	}
	tilog.Info(ctx, "CreateCppoOrg  success", zap.Any("cppoOrg", mspOrg))
	Success(c, resp, http.StatusOK)
}

func sendCppoRegisteredEmail(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 := "AWS CPPO Partner of TiDB Cloud Registration"
	tilog.Info(ctx, "sendCppoRegisteredEmail:"+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.TNCppoRegistered],

		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 cppo registration email", zap.String("feature", "registration"), zap.String("email", email), zap.Time("time", time.Now().UTC()))

	return nil
}
