package notify

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	relent "github.com/tidbcloud/devops/entity/release"
	larkutil "github.com/tidbcloud/devops/pkg/infra/clients/larkcli/util"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	timeutil "github.com/tidbcloud/devops/pkg/infra/utils/time"
)

type ReleaseTopicParamChangeItem struct {
	ChangeItem  *relent.ReleaseTicket
	NotifyUsers container.Set[string]
}

func (m ReleaseTopicParamChangeItem) ToLine() string {
	urlTxt := larkutil.LinkInText(m.ChangeItem.Name, "https://tidb.atlassian.net/browse/%s", m.ChangeItem.Name)

	atUsers := make([]string, 0, len(m.NotifyUsers))
	for larkID := range m.NotifyUsers.All() {
		atUsers = append(atUsers, larkutil.AtUserInCard(larkID))
	}

	output := urlTxt
	if len(atUsers) != 0 {
		output = fmt.Sprintf("%s %s", output, strings.Join(atUsers, ""))
	}

	return output
}

type ReleaseTopicParam struct {
	ChangeName      string
	ChangeLink      string
	ApplicationName string
	WindowStartAt   time.Time
	WindowEndAt     time.Time
	RiskLevel       relent.RiskLevel
	ChangeType      relent.ChangeType

	ChangeItems []ReleaseTopicParamChangeItem
	Applicant   LarkUser
}

func (m ReleaseTopicParam) Message(cards MessageCardSet) (larkutil.IMMessage, error) {
	changeItemLines := []string{}
	for _, item := range m.ChangeItems {
		changeItemLines = append(changeItemLines, item.ToLine())
	}
	releaseDateStr := m.WindowStartAt.In(timeutil.ChinaTimeZone).Format("2006/01/02")
	releaseTimeStr := m.WindowStartAt.In(timeutil.ChinaTimeZone).Format("2006/01/02 15:04:05") + " - " + m.WindowEndAt.In(timeutil.ChinaTimeZone).Format("2006/01/02 15:04:05")

	atApplicantText := m.Applicant.Email
	if m.Applicant.LarkID != nil {
		atApplicantText = larkutil.AtUserInCard(*m.Applicant.LarkID)
	}

	changeType := string(m.ChangeType)
	headerColor := "blue"
	switch m.ChangeType {
	case relent.ChangeTypeSprint:
		changeType = "Sprint"
		headerColor = "blue"
	case relent.ChangeTypeHotfix:
		changeType = "Hotfix"
		headerColor = "yellow"

	}

	return larkutil.IMMessageCardTemplate{
		MType: "template",
		Data: larkutil.IMMessageCardTemplateData{
			ID: cards.ReleaseNotification,
			Vars: map[string]string{
				"change_name":      m.ChangeName,
				"application_name": m.ApplicationName,
				"release_date":     releaseDateStr,
				"release_time":     releaseTimeStr,
				"risk_level":       string(m.RiskLevel),
				"change_items":     larkutil.FormatItems(changeItemLines, "  ", "- "),
				"at_applicant":     atApplicantText,
				"change_link":      m.ChangeLink,
				"change_type":      changeType,
				"header_color":     headerColor,
			},
		},
	}, nil
}

type ReleaseDeploymentParam struct {
	Order      relent.ChangeDeployOrder
	Regions    []relent.RegionName
	DeployedAt *time.Time
	Method     relent.ChangeDeployMethod
	Info       relent.ChangeDeployInfo
}

func (m ReleaseDeploymentParam) Message(cards MessageCardSet) (larkutil.IMMessage, error) {
	var content string
	switch m.Method {
	case relent.ChangeDeployMethodArgoCD:
		cd_links := []string{}
		if m.Info.ArgoCD != nil {
			for _, pr := range m.Info.ArgoCD.DeployPRs {
				link := larkutil.LinkInText(fmt.Sprintf("%s/%s#%d", pr.RepoOwner, pr.RepoName, pr.PRNumber),
					"https://github.com/%s/%s/pull/%d", pr.RepoOwner, pr.RepoName, pr.PRNumber)
				cd_links = append(cd_links, link)
			}
		}
		content = larkutil.FormatItems(cd_links, "  ", "- ")
		content = fmt.Sprintf("%s: %s", content, m.Info.Description)
	case relent.ChangeDeployMethodManual:
		content = m.Info.Description
	}

	regionsLine := []string{} // 4 regions per line
	for i := 0; i < len(m.Regions); i += 4 {
		end := i + 4
		if end > len(m.Regions) {
			end = len(m.Regions)
		}
		regions := container.TransformSlice(m.Regions[i:end], container.ToString[relent.RegionName])
		regionsLine = append(regionsLine, strings.Join(regions, ", "))
	}

	return larkutil.IMMessageCardTemplate{
		MType: "template",
		Data: larkutil.IMMessageCardTemplateData{
			ID: cards.ReleaseDeploymentNotification,
			Vars: map[string]string{
				"deploy_order": strconv.Itoa(m.Order.Int()),
				"method":       string(m.Method),
				"content":      content,
				"regions":      strings.Join(regionsLine, "\n  "),
			},
		},
	}, nil
}

type ReleaseCloseParam struct {
	Result  relent.ChangeResult
	Summary string
}

func (m ReleaseCloseParam) Message(cards MessageCardSet) (larkutil.IMMessage, error) {
	return larkutil.IMMessageCardTemplate{
		MType: "template",
		Data: larkutil.IMMessageCardTemplateData{
			ID: cards.ReleaseCloseNotification,
			Vars: map[string]string{
				"result":  string(m.Result),
				"summary": m.Summary,
			},
		},
	}, nil
}

func (c *client) ReleaseTopic(ctx context.Context, chatID string, param ReleaseTopicParam) (*string /*message id*/, error) {
	return c.SendMessage(ctx, ReceiveTypeChat, chatID, param)
}

func (c *client) ReleaseDeployment(ctx context.Context, msgID string, param ReleaseDeploymentParam) (*string /*message id*/, error) {
	return c.ReplyMessage(ctx, msgID, param, true)
}

func (c *client) ReleaseClose(ctx context.Context, msgID string, param ReleaseCloseParam) (*string /*message id*/, error) {
	return c.ReplyMessage(ctx, msgID, param, true)
}
