package jobs

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/tidbcloud/account/commons/database"

	"github.com/go-resty/resty/v2"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/account/commons/config"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/account/thirdparty/salesforce"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

const (
	pocRecordTypeProductFeedback     = "Product Feedback"
	pocRecordTypeApplyForPoC         = "Apply for PoC"
	pocRecordTypeScheduleADemo       = "Schedule a demo"
	pocRecordTypeRequestTrialCredits = "Request free trial credits"
)

var client = resty.New()

type element struct {
	Tag  string `json:"tag"`
	Text struct {
		Content string `json:"content"`
		Tag     string `json:"tag"`
	} `json:"text"`
}

type sendFeishuMsg struct {
	MsgType string `json:"msg_type"`
	Card    struct {
		Config struct {
			WideScreenMode bool `json:"wide_screen_mode"`
			EnableForward  bool `json:"enable_forward"`
		} `json:"config"`
		Elements []element `json:"elements"`
		Header   struct {
			Title struct {
				Content string `json:"content"`
				Tag     string `json:"tag"`
			} `json:"title"`
		} `json:"header"`
	} `json:"card"`
}

func buildFeishuMsg(ctx context.Context, pocApplication entity.PocApplication) (*sendFeishuMsg, error) {
	pocExtraFields := pocApplication.PocExtraFields
	if pocExtraFields == nil {
		return nil, errs.NewBadRequest("no poc extra fields found", nil)
	}
	var (
		title   string
		content string
	)

	// https://jira.tidbcloud.com/browse/DM-7346
	switch pocExtraFields.PocRecordType {
	case pocRecordTypeApplyForPoC:
		title = "New POC application"
		content = fmt.Sprintf(
			"[%s] Received a new POC application from tenant %s (OrgId: %d, Email: %s)",
			pocApplication.Tenant.Country, pocApplication.Tenant.Name,
			pocApplication.Tenant.ID.Uint64(), pocApplication.Tenant.Email,
		)
	case pocRecordTypeScheduleADemo:
		title = "New DEMO application"
		content = fmt.Sprintf(
			"Received a new DEMO application from tenant %s (OrgId: %d, Email: %s)",
			pocApplication.Tenant.Name, pocApplication.Tenant.ID.Uint64(), pocApplication.Tenant.Email,
		)
	case pocRecordTypeRequestTrialCredits:
		title = "New Credits application"
		content = fmt.Sprintf(
			"Received a new Credits application from tenant %s (OrgId: %d, Email: %s)",
			pocApplication.Tenant.Name, pocApplication.Tenant.ID.Uint64(), pocApplication.Tenant.Email,
		)
	case pocRecordTypeProductFeedback:
		return nil, nil
	}

	var msg sendFeishuMsg
	msg.MsgType = "interactive"
	msg.Card.Config.WideScreenMode = true
	msg.Card.Config.EnableForward = true
	msg.Card.Header.Title.Tag = "plain_text"
	msg.Card.Header.Title.Content = title

	var ele element
	ele.Tag = "div"
	ele.Text.Tag = "lark_md"
	ele.Text.Content = content
	msg.Card.Elements = append(msg.Card.Elements, ele)

	return &msg, nil
}

func sendMsgToFeishu(ctx context.Context, pocApplication entity.PocApplication) {
	logger := tilog.WithContextL(ctx)

	msg, err := buildFeishuMsg(ctx, pocApplication)
	if err != nil {
		logger.Error("failed to build poc message", zap.Any("pocApplication", pocApplication))
		return
	}
	if msg == nil {
		logger.Info("no feishu msg built, ignore", zap.Any("pocApplication", pocApplication))
		return
	}

	url := config.Global().PocApplicationWebhook

	resp, err := client.R().SetBody(msg).Post(url)
	logger.Info("send poc application msg to feishu", zap.Int("status", resp.StatusCode()), zap.Error(err))
}

func buildPocApplicationParams(pocApplication entity.PocApplication) salesforce.PocApplication {
	params := salesforce.PocApplication{
		PortalID:   strconv.FormatUint(pocApplication.TenantID.Uint64(), 10),
		PocID:      strconv.FormatUint(pocApplication.ID.Uint64(), 10),
		PocResults: nil,
	}

	if pocApplication.PocExtraFields != nil {
		params.PocRecordType = pocApplication.PocExtraFields.PocRecordType
		params.SubTopic = pocApplication.PocExtraFields.SubTopic
		params.Message = pocApplication.PocExtraFields.Message
	}

	for _, question := range pocApplication.Questions {
		pocQuestion := salesforce.PocQuestion{
			QuestionNumber: int(question.Number),
			Type:           salesforce.PocQuestionTypeText, // default value
			Question:       question.Question,
			Options:        nil, // fill later
			Note:           question.Note,
		}

		switch question.Type {
		case entity.PocQuestionTypeRadio:
			pocQuestion.Type = salesforce.PocQuestionTypeRadio
		case entity.PocQuestionTypeCheckbox:
			pocQuestion.Type = salesforce.PocQuestionTypeCheckbox
		case entity.PocQuestionTypeText:
			pocQuestion.Type = salesforce.PocQuestionTypeText
		}

		if pocQuestion.Type == salesforce.PocQuestionTypeRadio || pocQuestion.Type == salesforce.PocQuestionTypeCheckbox {
			for _, option := range question.Options.Options {
				pocQuestion.Options = append(pocQuestion.Options, salesforce.PocQuestionOption{
					OptionName: option.Name,
					IsSelected: option.IsSelected,
				})
			}
		}

		params.PocResults = append(params.PocResults, pocQuestion)
	}
	return params
}

func syncPocApplication(ctx context.Context, pocApplicationID entity.ID) error {
	logger := tilog.WithContextL(ctx)

	var (
		pocApplication entity.PocApplication
		succeed        = false
	)
	err := database.Transaction(func(tx *gorm.DB) error {
		err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Model(&entity.PocApplication{}).Preload("Questions").
			Preload("Tenant").Where("id=?", pocApplicationID).First(&pocApplication).Error
		if err != nil {
			return err
		}
		if pocApplication.Synced {
			return nil
		}

		params := buildPocApplicationParams(pocApplication)
		err = salesforceClient.SyncPocApplication(ctx, params)
		if err != nil {
			return err
		}

		succeed = true

		return tx.Model(&pocApplication).Update("synced", true).Error
	})
	if err != nil {
		return err
	}

	if succeed {
		sendMsgToFeishu(ctx, pocApplication)
		logger.Info("send poc application msg to feishu", zap.Uint64("poc_application_id", pocApplicationID.Uint64()))
	}

	return nil
}

func SyncPocApplication(ctx context.Context, payload []byte) error {
	var req config.JobSyncPocApplicationSchema
	if json.Unmarshal(payload, &req) != nil {
		return errors.New("invalid payload")
	}

	pocApplicationID := entity.ID(req.PocApplicationID)

	salesforceConfig := config.Global().SalesforceConfig
	if !salesforceConfig.EnableSalesforce {
		tilog.Info(ctx, "SyncSalesforce is disabled", zap.Uint64("pocApplicationID", pocApplicationID.Uint64()))
		return nil
	}

	err := syncPocApplication(ctx, pocApplicationID)
	if err != nil {
		// use `salesforce` keyword to trigger pageduty alert
		tilog.Warn(ctx, "failed to sync poc application to salesforce", zap.String("feature", "poc"), zap.String("sentry", "salesforce"), zap.Uint64("pocApplicationID", pocApplicationID.Uint64()), zap.Error(err))
		return err
	}

	return nil
}
