package main

import (
	"context"
	"errors"
	"log/slog"
	"os/signal"
	"syscall"

	queueapp "wucher/internal/app/queueing"
	appworker "wucher/internal/app/worker"
	"wucher/internal/config"
	"wucher/internal/shared/pkg/logger"
	mysqlrepo "wucher/internal/repository/mysql"
	"wucher/internal/resilience"
	"wucher/internal/service"
)

func runMailWorker(appCfg config.AppConfig) error {
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	appLogger := logger.New(appCfg.Logging).With("component", "worker", "job", "email")
	resCfg := appCfg.Resilience
	emailCfg := appCfg.Email

	queueCfg := appCfg.Queue
	sqsCfg := appCfg.SQS
	db, err := mysqlrepo.Connect(appCfg.MySQL)
	if err != nil {
		return err
	}
	authRepo := mysqlrepo.NewAuthRepository(db)

	sqsBreaker := resilience.NewExecutor("sqs", resCfg.SQS, appLogger, resilience.ClassifySQSError)
	consumer, serializer, err := queueapp.NewEmailConsumer(ctx, queueCfg, sqsCfg, sqsBreaker)
	if err != nil {
		return err
	}
	idempotency := queueapp.NewIdempotencyStore(db, queueCfg)
	var dispatcher *appworker.OutboxDispatcher
	if queueCfg.OutboxEnabled {
		rawProducer, err := queueapp.NewQueueProducer(ctx, sqsCfg, sqsBreaker, "outbox_dispatcher")
		if err != nil {
			return err
		}
		dispatcher = appworker.NewOutboxDispatcher(queueCfg, authRepo, rawProducer, appLogger)
	}

	emailSender, err := buildEmailSender(ctx, emailCfg, appCfg.SES, resCfg, appLogger)
	if err != nil {
		return err
	}
	handler := service.NewEmailDeliveryHandler(emailSender, appLogger)
	runner := appworker.NewRunner(queueCfg, consumer, serializer, handler, idempotency, dispatcher, appLogger)

	appLogger.Info("worker started",
		"backend", "sqs",
		"email_provider", emailCfg.Provider,
		"monitor_addr", queueCfg.WorkerMonitorAddr,
		"outbox_enabled", queueCfg.OutboxEnabled,
	)
	if err := runner.Start(ctx); err != nil {
		return err
	}
	appLogger.Info("worker stopped")
	return nil
}

func buildEmailSender(
	ctx context.Context,
	emailCfg config.EmailDeliveryConfig,
	sesCfg config.SESConfig,
	resCfg config.ResilienceConfig,
	appLogger *slog.Logger,
) (service.EmailSender, error) {
	switch emailCfg.Provider {
	case "", "ses":
		sesBreaker := resilience.NewExecutor("ses", resCfg.SES, appLogger, resilience.ClassifySESError)
		sender, err := service.NewSESSender(ctx, sesCfg)
		if err != nil {
			return nil, err
		}
		sender = sender.WithExecutor(sesBreaker)
		sender = sender.WithMetricsComponent("email_worker")
		if !sender.Configured() {
			return nil, errors.New("ses is not configured")
		}
		return sender, nil
	case "mock":
		sender := service.NewMockEmailSender(emailCfg.MockDir)
		if !sender.Configured() {
			return nil, errors.New("mock email sender is not configured")
		}
		return sender, nil
	default:
		return nil, errors.New("unsupported email provider: " + emailCfg.Provider)
	}
}
