package modules

import (
	"context"
	"strings"

	"gorm.io/gorm"

	alerthandler "security-center-backend/internal/modules/alerts/handler"
	alertrepo "security-center-backend/internal/modules/alerts/repository"
	alertservice "security-center-backend/internal/modules/alerts/service"
	deptservice "security-center-backend/internal/modules/department/service"
	"security-center-backend/internal/pkg/cache"
	"security-center-backend/internal/pkg/logger"
	settingsservice "security-center-backend/internal/service/settings"
)

type AlertsModule struct {
	Handler    *alerthandler.Handler
	StartHooks []StartHook
	StopHooks  []StopHook
	Configure  func(*settingsservice.Service, func() deptservice.RuntimeStatus)
}

type AlertsBuildInput struct {
	DB           *gorm.DB
	RuleRepo     alertrepo.RuleRepository
	RunRepo      alertrepo.RunRepository
	DeliveryRepo alertrepo.DeliveryRepository
	Settings     *settingsservice.Service
	Cache        *cache.Cache
	Logger       *logger.Logger
}

func BuildAlerts(input AlertsBuildInput) *AlertsModule {
	if input.RuleRepo == nil || input.RunRepo == nil || input.DeliveryRepo == nil {
		return &AlertsModule{}
	}

	alertSvc := alertservice.New(input.DB, input.RuleRepo, input.RunRepo, input.DeliveryRepo, input.Settings)
	alertSvc.SetLogger(input.Logger)
	runtime := alertservice.NewAlertRuntimeControl(alertSvc, input.RuleRepo, input.DB, input.Logger)
	slaReminderRuntime := alertservice.NewVulnerabilitySLAReminderRuntime(
		alertSvc,
		alertrepo.NewSLAReminderDeliveryRepository(input.DB),
		input.Logger,
	)

	alertsRuntimeCanRun := func(settings settingsservice.NotificationSettings) bool {
		if !settings.LarkAlerts {
			return false
		}
		hasWebhook := strings.TrimSpace(settings.LarkWebhook) != ""
		hasAppCredentials := strings.TrimSpace(settings.LarkAppID) != "" && strings.TrimSpace(settings.LarkAppSecret) != ""
		return hasWebhook || hasAppCredentials
	}

	slaReminderRuntimeCanRun := func(settings settingsservice.NotificationSettings) bool {
		if !settings.LarkAlerts || !settings.VulnerabilitySLARemindersEnabled {
			return false
		}
		return strings.TrimSpace(settings.LarkAppID) != "" && strings.TrimSpace(settings.LarkAppSecret) != ""
	}

	applyRuntimeConfig := func(ctx context.Context, settings settingsservice.NotificationSettings) error {
		if !settings.LarkAlerts {
			runtime.StopWithDiagnostic("lark alerts disabled")
			slaReminderRuntime.StopWithDiagnostic("lark alerts disabled")
			return nil
		}
		if err := alertSvc.EnsureDefaultVulnerabilitySubmissionRule(ctx); err != nil {
			return err
		}
		if !alertsRuntimeCanRun(settings) {
			runtime.StopWithDiagnostic("lark alerts enabled but no Lark app credentials or webhook are configured")
		} else {
			status := runtime.Status()
			if status.SchedulerRunning {
				if _, err := runtime.Refresh(); err != nil {
					return err
				}
			} else if _, err := runtime.Start(); err != nil {
				return err
			}
		}

		if !settings.VulnerabilitySLARemindersEnabled {
			slaReminderRuntime.StopWithDiagnostic("vulnerability SLA reminders disabled")
			return nil
		}
		if !slaReminderRuntimeCanRun(settings) {
			slaReminderRuntime.StopWithDiagnostic("vulnerability SLA reminders require Lark app credentials")
			return nil
		}
		status := slaReminderRuntime.Status()
		if status.SchedulerRunning {
			_, err := slaReminderRuntime.Refresh()
			return err
		}
		_, err := slaReminderRuntime.Start()
		return err
	}

	return &AlertsModule{
		Handler: alerthandler.New(alertSvc, runtime, input.Cache),
		Configure: func(settings *settingsservice.Service, provider func() deptservice.RuntimeStatus) {
			alertSvc.SetOrgSyncStatusProvider(provider)
			if settings == nil {
				return
			}

			ctx := context.Background()
			snapshot, err := settings.Get(ctx)
			if err == nil {
				_ = applyRuntimeConfig(ctx, snapshot.Notifications)
			}

			settings.SetAlertReconfigurer(func(notificationSettings settingsservice.NotificationSettings) error {
				return applyRuntimeConfig(context.Background(), notificationSettings)
			})
		},
		StopHooks: []StopHook{
			func(ctx context.Context) error {
				runtime.Stop()
				slaReminderRuntime.Stop()
				return nil
			},
		},
	}
}
