package message

import (
	"context"
	"log"
	"os"
	"testing"

	"github.com/golang/mock/gomock"
	"github.com/smartystreets/goconvey/convey"
	"github.com/tidbcloud/account/internel/dao"
	"github.com/tidbcloud/account/internel/dao/entity"
	"github.com/tidbcloud/account/pkg/docker_db"
	mailv2 "github.com/tidbcloud/pkgv2/mail/v2"
	"gorm.io/gorm"
)

var db *gorm.DB

func init() {
	var err error
	db, err = docker_db.TryInitCentralDB(true)
	if err != nil {
		log.Printf("Failed to init docker db: %s", err)
		os.Exit(-1)
		return
	}
}

func TestMessageSvc_SendEmailByLabels(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockHub := dao.NewMockHub(ctrl)
	svc := NewMessageSvc(db, mockHub, mailv2.NewFakeMailFacade())
	ctx := context.TODO()
	var (
		orgID        = uint64(1)
		scene        = "scene"
		identifier   = "id1"
		subject      = "subject"
		toLabel      = entity.MailLabelOrgMembers
		ccLabels     = []string{entity.MailLabelOrgOwner}
		templateID   = "template_id"
		templateVars = map[string]string{}
	)

	convey.Convey("SendEmailByLabel", t, func() {
		convey.Convey("org not found", func() {
			mockHub.Mock.Org.EXPECT().GetOrgByOrgID(ctx, gomock.Any(), orgID).Return(nil, gorm.ErrRecordNotFound)
			emailReminder, err := svc.SendEmailByLabels(ctx, subject, orgID, scene, identifier, toLabel, ccLabels, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("invalid label", func() {
			mockHub.Mock.Org.EXPECT().GetOrgByOrgID(ctx, gomock.Any(), orgID).Return(&entity.Tenant{}, nil)
			emailReminder, err := svc.SendEmailByLabels(ctx, subject, orgID, scene, identifier, "you can never find me", ccLabels, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("list users by org_id and role failed", func() {
			mockHub.Mock.Org.EXPECT().GetOrgByOrgID(ctx, gomock.Any(), orgID).Return(&entity.Tenant{}, nil)
			mockHub.Mock.Cross.EXPECT().ListUsersByOrgIDAndRole(ctx, gomock.Any(), orgID, entity.RoleOrgMember, gomock.Any()).Return(nil, gorm.ErrRecordNotFound)
			emailReminder, err := svc.SendEmailByLabels(ctx, subject, orgID, scene, identifier, toLabel, ccLabels, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("list users by org_id and role got 0 emails", func() {
			mockHub.Mock.Org.EXPECT().GetOrgByOrgID(ctx, gomock.Any(), orgID).Return(&entity.Tenant{}, nil)
			mockHub.Mock.Cross.EXPECT().ListUsersByOrgIDAndRole(ctx, gomock.Any(), orgID, entity.RoleOrgMember, gomock.Any()).Return([]entity.User{}, nil)
			emailReminder, err := svc.SendEmailByLabels(ctx, subject, orgID, scene, identifier, toLabel, ccLabels, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})
	})
}

func TestMessageSvc_SendEmail(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockHub := dao.NewMockHub(ctrl)
	svc := NewMessageSvc(db, mockHub, mailv2.NewFakeMailFacade())
	ctx := context.TODO()
	var (
		orgID        = uint64(1)
		scene        = "scene"
		identifier   = "id1"
		subject      = "subject"
		emails       = []string{"emails"}
		templateID   = "template_id"
		templateVars = map[string]string{}
	)

	convey.Convey("SendEmail", t, func() {
		convey.Convey("get emails reminder failed", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrEmptySlice)
			emailReminder, err := svc.SendEmail(ctx, subject, orgID, scene, identifier, emails, nil, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("duplicated error", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(&entity.EmailReminder{Base: entity.Base{ID: 2}}, nil)
			emailReminder, err := svc.SendEmail(ctx, subject, orgID, scene, identifier, emails, nil, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("create emails reminder failed", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrRecordNotFound)
			mockHub.Mock.Message.EXPECT().CreateEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrRecordNotFound)
			emailReminder, err := svc.SendEmail(ctx, subject, orgID, scene, identifier, emails, nil, templateID, templateVars)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("send successfully", func() {
			mockHub.Mock.Org.EXPECT().GetOrgByOrgID(ctx, gomock.Any(), orgID).Return(&entity.Tenant{}, nil)
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrRecordNotFound)
			mockHub.Mock.Message.EXPECT().CreateEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(&entity.EmailReminder{}, nil)
			emailReminder, err := svc.SendEmail(ctx, subject, orgID, scene, identifier, emails, nil, templateID, templateVars)
			convey.So(err, convey.ShouldBeNil)
			convey.So(emailReminder, convey.ShouldNotBeNil)
		})
	})
}

func TestMessageSvc_HoldEmailUniqueID(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockHub := dao.NewMockHub(ctrl)
	svc := NewMessageSvc(db, mockHub, mailv2.NewFakeMailFacade())
	ctx := context.TODO()
	const (
		orgID      = uint64(1)
		scene      = "scene"
		identifier = "id1"
	)

	convey.Convey("Hold Email Unique ID", t, func() {
		convey.Convey("mail reminder exists", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(&entity.EmailReminder{}, nil)
			emailReminder, err := svc.HoldEmailUniqueID(ctx, orgID, scene, identifier)
			convey.So(err, convey.ShouldBeNil)
			convey.So(emailReminder, convey.ShouldNotBeNil)
		})

		convey.Convey("failed to get reminder", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrEmptySlice)
			emailReminder, err := svc.HoldEmailUniqueID(ctx, orgID, scene, identifier)
			convey.So(err, convey.ShouldNotBeNil)
			convey.So(emailReminder, convey.ShouldBeNil)
		})

		convey.Convey("create email reminder", func() {
			mockHub.Mock.Message.EXPECT().GetEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(nil, gorm.ErrRecordNotFound)
			mockHub.Mock.Message.EXPECT().CreateEmailReminder(ctx, gomock.Any(), orgID, scene, identifier).Return(&entity.EmailReminder{}, nil)
			emailReminder, err := svc.HoldEmailUniqueID(ctx, orgID, scene, identifier)
			convey.So(err, convey.ShouldBeNil)
			convey.So(emailReminder, convey.ShouldNotBeNil)
		})
	})
}

func TestMessageSvc_ClearEmailUniqueID(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockHub := dao.NewMockHub(ctrl)
	svc := NewMessageSvc(nil, mockHub, mailv2.NewFakeMailFacade())
	ctx := context.TODO()
	const (
		orgID      = uint64(1)
		scene      = "scene"
		identifier = "id1"
	)

	convey.Convey("Clear Email Unique ID", t, func() {
		mockHub.Mock.Message.EXPECT().DeleteEmailReminder(ctx, gomock.Any(), orgID, scene, identifier)
		err := svc.ClearEmailUniqueID(ctx, orgID, scene, identifier)
		convey.So(err, convey.ShouldBeNil)
	})
}
