package idlereminder

import (
	"context"
	"fmt"

	"github.com/andygrunwald/go-jira"
	"github.com/tidbcloud/devops/entity/message"
	"github.com/tidbcloud/devops/pkg/infra/clients/jiracli"
	"github.com/tidbcloud/devops/pkg/infra/clients/larkcli"
	"github.com/tidbcloud/devops/pkg/infra/clients/larkcli/notify"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	"github.com/tidbcloud/devops/pkg/infra/log"
	msgapi "github.com/tidbcloud/devops/pkg/service/message/api"
)

const NotifyTargetBoth = "both"
const NotifyTargetAssignee = "assignee"
const NotifyTargetLeader = "leader"

type JiraSpecChecker struct {
	jiraCli jiracli.JiraClient
	msgSvc  msgapi.MessageServiceAPI
	larkCli *larkcli.LarkClient
	conf    config.JiraSpecConfig
}

func NewJiraSpecChecker(
	jiraCli jiracli.JiraClient,
	msgSvc msgapi.MessageServiceAPI,
	conf config.JiraSpecConfig,
	larkCli *larkcli.LarkClient,
) (*JiraSpecChecker, error) {

	return &JiraSpecChecker{
		jiraCli: jiraCli,
		msgSvc:  msgSvc,
		conf:    conf,
		larkCli: larkCli,
	}, nil
}

func (d *JiraSpecChecker) NotifyJiraTickets(ctx context.Context) error {
	logger := log.FromContext(ctx)

	logger.Info("start get jira alert tickets and notify leaders")

	tickets, err := d.getJiraLongTailTickets()
	if err != nil {
		return fmt.Errorf("get jira long tail tickets error: %w", err)
	}

	if d.conf.MailEnable {
		// Notify global configured email
		err = d.sendMail(ctx, tickets, d.conf.ReceiverEmails)
		if err != nil {
			logger.Error(err, "fail to send jira tickets email")
		}
	}

	logger.Info("get weekly oncall tickets and notify global mails done")

	assignMap, leaderMap := buildDifferentRoleMaps(tickets)
	if d.conf.NotifyTarget == NotifyTargetBoth || d.conf.NotifyTarget == NotifyTargetLeader {
		for leaderEmail, leaderList := range leaderMap {
			err = d.sendNotificationToOneUser(ctx, leaderEmail, leaderList)
			if err != nil {
				logger.Error(err, "fail to send notification to one user", "user email", leaderEmail)
			}
		}
	}
	if d.conf.NotifyTarget == NotifyTargetBoth || d.conf.NotifyTarget == NotifyTargetAssignee {
		for assigneeEmail, assigneeList := range assignMap {
			err = d.sendNotificationToOneUser(ctx, assigneeEmail, assigneeList)
			if err != nil {
				logger.Error(err, "fail to send notification to one user", "user email", assigneeEmail)
			}
		}
	}

	return nil
}

func buildDifferentRoleMaps(tickets []jira.Issue) (map[string][]jira.Issue, map[string][]jira.Issue) {
	assignMap := make(map[string][]jira.Issue)
	leaderMap := make(map[string][]jira.Issue)

	for _, ticket := range tickets {
		if ticket.Fields.Assignee == nil {
			continue
		}
		assignEmail := ticket.Fields.Assignee.EmailAddress

		if _, existAssignMap := assignMap[assignEmail]; !existAssignMap {
			assignMap[assignEmail] = []jira.Issue{ticket}
		} else {
			assignMap[assignEmail] = append(assignMap[assignEmail], ticket)
		}

		// get the leader email
		emailOk := false
		leaderEmail := ""
		if egLeader, exist := ticket.Fields.Unknowns[EG_LEADER_FIELD]; exist {
			leaderMap, ok := egLeader.(map[string]interface{})
			if ok {
				leaderEmail, emailOk = leaderMap["emailAddress"].(string)
			}
		}
		if emailOk && leaderEmail != "" {
			if _, existLeaderMap := leaderMap[leaderEmail]; !existLeaderMap {
				leaderMap[leaderEmail] = []jira.Issue{ticket}
			} else {
				leaderMap[leaderEmail] = append(leaderMap[leaderEmail], ticket)
			}
		}
	}
	return assignMap, leaderMap
}

func (d *JiraSpecChecker) sendNotificationToOneUser(ctx context.Context, emailAddr string, tickets []jira.Issue) error {
	logger := log.FromContext(ctx)
	if d.conf.NotifyTargetWithMail {
		err := d.sendMail(ctx, tickets, []string{emailAddr})
		if err != nil {
			logger.Error(err, "fail to send email to user", "emailAddr", emailAddr)
		}
	}
	users, err := d.larkCli.Contact.BetchGetUserIds(ctx, []string{emailAddr})
	if err != nil {
		return fmt.Errorf("fail to get user email: %s", emailAddr)
	}
	if len(users) == 0 {
		return fmt.Errorf("no user is found for email: %s", emailAddr)
	} else {
		userID := users[0].UserId
		o11yStr, ticketStr := buildLarkBody(tickets)
		_, err = d.larkCli.Notify.NotifyLongTailTicketsUnify(ctx, *userID, notify.NotifyLongTailTicketsUnifyParam{
			O11YBody:   o11yStr,
			TicketBody: ticketStr,
		})
		if err != nil {
			return fmt.Errorf("error when notify user alert by lark")
		}
	}
	return nil
}

func (d *JiraSpecChecker) getJiraLongTailTickets() ([]jira.Issue, error) {
	jql := d.conf.JQL
	options := &jira.SearchOptions{
		Expand:     "changelog",
		MaxResults: 100,
		Fields:     []string{"*all"},
	}
	tickets, err := d.jiraCli.SearchIssues(context.Background(), jql, &jiracli.SearchOptions{
		Expand:     options.Expand,
		MaxResults: options.MaxResults,
		Fields:     options.Fields,
	})
	if err != nil {
		return nil, err
	}
	return tickets.Issues, nil
}

func (d *JiraSpecChecker) sendMail(ctx context.Context, tickets []jira.Issue, emailAddr []string) error {
	emailBodyVars, err := buildEmailBodyVars(tickets)
	if err != nil {
		return fmt.Errorf("build jira long tail tickets body error: %w", err)
	}

	err = d.msgSvc.SendEmail(ctx, msgapi.SendEmailParam{
		Name: "Send jira Long Tail Alerts",
		Target: message.EmailTarget{
			Users: &message.EmailTargetUsers{
				Emails: emailAddr,
			},
		},
		Template: message.EmailTemplate{
			ID:        d.conf.TemplateID,
			Variables: emailBodyVars,
		},
		Reason:      "send jira long tail alert email to leaders",
		Applicant:   consts.InternalVirtualEmail,
		FromAddress: d.conf.EmailFromAddress,
		CCAdresses:  d.conf.CC,
		BCCAdresses: d.conf.BCC,
	})
	if err != nil {
		return fmt.Errorf("send jira long tail tickets email error: %w", err)
	}
	return nil
}
