package task

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/go-logr/logr"
	"github.com/stretchr/testify/require"

	msgent "github.com/tidbcloud/devops/entity/message"
	"github.com/tidbcloud/devops/pkg/infra/clients/larkcli"
	"github.com/tidbcloud/devops/pkg/infra/config"
	msgapi "github.com/tidbcloud/devops/pkg/service/message/api"
)

type fakeEmailSvc struct {
	err error
}

func (f *fakeEmailSvc) SendEmail(context.Context, msgapi.SendEmailParam) error {
	return f.err
}

func (f *fakeEmailSvc) SendBulkEmails(
	context.Context,
	msgapi.SendBulkEmailsParam,
) ([]msgent.EmailTarget, []msgent.EmailTarget, error) {
	return nil, nil, f.err
}

func (f *fakeEmailSvc) ListEmailRecords(context.Context, msgapi.ListEmailRecordsParam) ([]*msgent.EmailRecord, int64, error) {
	return nil, 0, nil
}

func (f *fakeEmailSvc) GetEmailRecord(context.Context, uint64) (*msgent.EmailRecord, error) {
	return nil, nil
}

func testNotifyRequest(targets ...notifyTarget) taskNotifyRequest {
	return taskNotifyRequest{
		TaskID:        1,
		TaskName:      "task-1",
		CloudProvider: "aws",
		EOSVersion:    "1.31",
		Deadline:      time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC),
		Notice:        "notice",
		PageURL:       "https://example.com/tasks/1",
		Targets:       targets,
	}
}

func Test_emailNotifier_Send(t *testing.T) {
	logger := logr.Discard()
	target := notifyTarget{SalesOwnerEmail: "owner@example.com"}

	t.Run("no template", func(t *testing.T) {
		n := newEmailNotifier(logger, config.K8sUpgradeNotifyConfig{}, &fakeEmailSvc{})
		deliveries, err := n.Send(context.Background(), testNotifyRequest(target))
		require.Error(t, err)
		require.Nil(t, deliveries)
	})

	t.Run("no recipients", func(t *testing.T) {
		n := newEmailNotifier(logger, config.K8sUpgradeNotifyConfig{EmailTemplateID: "tpl"}, &fakeEmailSvc{})
		deliveries, err := n.Send(context.Background(), testNotifyRequest())
		require.Error(t, err)
		require.Nil(t, deliveries)
	})

	t.Run("send success", func(t *testing.T) {
		n := newEmailNotifier(logger, config.K8sUpgradeNotifyConfig{EmailTemplateID: "tpl"}, &fakeEmailSvc{})
		deliveries, err := n.Send(context.Background(), testNotifyRequest(target))
		require.NoError(t, err)
		require.Len(t, deliveries, 1)
		require.NoError(t, deliveries[0].Err)
		require.Equal(t, "batch(1)", deliveries[0].Target)
	})

	t.Run("send failed", func(t *testing.T) {
		sendErr := errors.New("sendgrid down")
		n := newEmailNotifier(logger, config.K8sUpgradeNotifyConfig{EmailTemplateID: "tpl"}, &fakeEmailSvc{err: sendErr})
		deliveries, err := n.Send(context.Background(), testNotifyRequest(target))
		require.NoError(t, err)
		require.Len(t, deliveries, 1)
		require.ErrorIs(t, deliveries[0].Err, sendErr)
	})
}

func Test_larkGroupNotifier_Send_preconditions(t *testing.T) {
	logger := logr.Discard()
	target := notifyTarget{BusinessRegion: "us"}
	req := testNotifyRequest(target)
	cfg := config.K8sUpgradeNotifyConfig{
		LarkCardTemplateID: "card-tpl",
		DefaultWebhook:     "https://open.feishu.cn/hook/default",
	}

	t.Run("no targets", func(t *testing.T) {
		n := newLarkGroupNotifier(logger, cfg, &larkcli.LarkClient{})
		deliveries, err := n.Send(context.Background(), testNotifyRequest())
		require.Error(t, err)
		require.Nil(t, deliveries)
	})

	t.Run("no lark client", func(t *testing.T) {
		n := newLarkGroupNotifier(logger, cfg, nil)
		deliveries, err := n.Send(context.Background(), req)
		require.Error(t, err)
		require.Nil(t, deliveries)
	})

	t.Run("no card template", func(t *testing.T) {
		n := newLarkGroupNotifier(logger, config.K8sUpgradeNotifyConfig{DefaultWebhook: cfg.DefaultWebhook}, &larkcli.LarkClient{})
		deliveries, err := n.Send(context.Background(), req)
		require.Error(t, err)
		require.Nil(t, deliveries)
	})

	t.Run("no webhook configured", func(t *testing.T) {
		n := newLarkGroupNotifier(logger, config.K8sUpgradeNotifyConfig{LarkCardTemplateID: cfg.LarkCardTemplateID}, &larkcli.LarkClient{})
		deliveries, err := n.Send(context.Background(), req)
		require.Error(t, err)
		require.Nil(t, deliveries)
	})
}

func Test_collectRecipientEmails(t *testing.T) {
	t.Run("from targets", func(t *testing.T) {
		emails := collectRecipientEmails([]notifyTarget{
			{SalesOwnerEmail: "a@example.com"},
			{SalesOwnerEmail: "a@example.com"},
			{TAMName: "b@example.com"},
			{TAMName: "not-an-email"},
		}, nil)
		require.Equal(t, []string{"a@example.com", "b@example.com"}, emails)
	})

	t.Run("region override ignores target recipients", func(t *testing.T) {
		emails := collectRecipientEmails([]notifyTarget{
			{BusinessRegion: "US", SalesOwnerEmail: "owner@example.com", TAMName: "tam@example.com"},
			{BusinessRegion: "eu", SalesOwnerEmail: "eu-owner@example.com"},
		}, map[string][]string{
			"us": {"team-us@pingcap.com"},
		})
		require.ElementsMatch(t, []string{"team-us@pingcap.com", "eu-owner@example.com"}, emails)
	})

	t.Run("region override is case insensitive", func(t *testing.T) {
		emails := collectRecipientEmails([]notifyTarget{
			{BusinessRegion: " EMEA ", SalesOwnerEmail: "owner@example.com"},
		}, map[string][]string{
			"emea": {"emea-team@pingcap.com", " emea-team@pingcap.com "},
		})
		require.Equal(t, []string{"emea-team@pingcap.com"}, emails)
	})
}

func Test_groupNotifyTargetsByRegion(t *testing.T) {
	byRegion := groupNotifyTargetsByRegion([]notifyTarget{
		{BusinessRegion: "US"},
		{BusinessRegion: "us"},
		{BusinessRegion: " EU "},
	})
	require.Len(t, byRegion["us"], 2)
	require.Len(t, byRegion["eu"], 1)
}
