package usecase_test

import (
	"context"
	"fmt"
	"testing"

	"gateway/internal/model/notification/config"
	domentity "gateway/internal/model/notification/domain/entity"
	"gateway/internal/model/notification/domain/enum"
	domrepo "gateway/internal/model/notification/domain/repository"
	domtpl "gateway/internal/model/notification/domain/template"
	domusecase "gateway/internal/model/notification/domain/usecase"
	mocknotifrepo "gateway/internal/model/notification/mock/repository"
	"gateway/internal/model/notification/provider/email"
	"gateway/internal/model/notification/template"
	"gateway/internal/model/notification/usecase"

	"github.com/stretchr/testify/require"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.uber.org/mock/gomock"
)

func TestRetryWorker_ProcessJob_Success(t *testing.T) {
	ctrl := gomock.NewController(t)
	repo := mocknotifrepo.NewMockNotificationRepository(ctrl)

	docID := bson.NewObjectID()
	doc := &domentity.Notification{
		ID:       docID,
		TenantID: testTenantID,
		Status:   enum.NotifyStatusPending,
		Locale:   domtpl.LocaleZhTW,
		Attempts: 0,
	}
	repo.EXPECT().FindByID(gomock.Any(), testTenantID, docID.Hex()).Return(doc, nil)
	repo.EXPECT().UpdateDelivery(gomock.Any(), testTenantID, docID.Hex(), gomock.Any()).Return(nil)

	worker := usecase.NewRetryWorker(usecase.RetryWorkerParam{
		Repo:     repo,
		Renderer: template.NewRenderer(template.DefaultRegistry(), domtpl.LocaleZhTW),
		Email: email.NewChain(email.NewMockSender(
			email.WithMockName("mock"),
			email.WithMockMessageID("ok"),
		)),
		Config: config.Config{Email: config.EmailConfig{From: testEmailFrom}},
	})

	job := &domusecase.RetryJob{
		NotificationID: docID.Hex(),
		TenantID:       testTenantID,
		Channel:        enum.ChannelEmail,
		Kind:           enum.NotifyTenantWelcome,
		Target:         testEmailTarget,
		Locale:         domtpl.LocaleZhTW,
		Data:           map[string]any{varTenantName: testTenantName},
	}
	require.NoError(t, worker.ProcessJob(context.Background(), job))
}

func TestRetryWorker_ProcessJob_DropsToDLQ(t *testing.T) {
	ctrl := gomock.NewController(t)
	repo := mocknotifrepo.NewMockNotificationRepository(ctrl)
	dlq := mocknotifrepo.NewMockNotificationDLQRepository(ctrl)
	queue := mocknotifrepo.NewMockRetryQueue(ctrl)

	docID := bson.NewObjectID()
	doc := &domentity.Notification{
		ID:       docID,
		TenantID: testTenantID,
		Status:   enum.NotifyStatusRetrying,
		Locale:   domtpl.LocaleZhTW,
		Attempts: 4,
	}
	repo.EXPECT().FindByID(gomock.Any(), testTenantID, docID.Hex()).Return(doc, nil)
	repo.EXPECT().UpdateDelivery(gomock.Any(), testTenantID, docID.Hex(), gomock.Any()).DoAndReturn(
		func(_ context.Context, _, _ string, u *domrepo.NotificationDeliveryUpdate) error {
			require.Equal(t, enum.NotifyStatusDropped, u.Status)
			return nil
		},
	)
	dlq.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil)

	worker := usecase.NewRetryWorker(usecase.RetryWorkerParam{
		Repo:     repo,
		DLQ:      dlq,
		Queue:    queue,
		Renderer: template.NewRenderer(template.DefaultRegistry(), domtpl.LocaleZhTW),
		Email:    email.NewChain(email.NewMockSender(email.WithMockError(fmt.Errorf("fail")))),
		Config:   config.Config{Async: config.AsyncConfig{MaxRetry: 5}, Email: config.EmailConfig{From: testEmailFrom}},
	})

	job := &domusecase.RetryJob{
		NotificationID: docID.Hex(),
		TenantID:       testTenantID,
		Channel:        enum.ChannelEmail,
		Kind:           enum.NotifyTenantWelcome,
		Target:         testEmailTarget,
		Data:           map[string]any{varTenantName: testTenantName},
	}
	require.NoError(t, worker.ProcessJob(context.Background(), job))
}
