package settings

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

	"security-center-backend/internal/model"
	"security-center-backend/internal/repository"
)

type fakeSettingsRepo struct {
	current *model.SystemSettings
}

func (f *fakeSettingsRepo) Get(ctx context.Context) (*model.SystemSettings, error) {
	if f.current == nil {
		return nil, repository.ErrSettingsVersionConflict
	}
	copy := *f.current
	return &copy, nil
}

func (f *fakeSettingsRepo) Create(ctx context.Context, settings *model.SystemSettings) (*model.SystemSettings, error) {
	copy := *settings
	f.current = &copy
	return &copy, nil
}

func (f *fakeSettingsRepo) Update(ctx context.Context, settings *model.SystemSettings, expectedVersion uint64) (*model.SystemSettings, error) {
	copy := *settings
	copy.Version = expectedVersion + 1
	f.current = &copy
	return &copy, nil
}

func TestUpdateTriggersAlertReconfigurerWhenNotificationsChange(t *testing.T) {
	ctx := context.Background()
	repo := &fakeSettingsRepo{
		current: defaultSettingsModel(),
	}
	svc := New(repo, nil, nil)

	var called int
	var received NotificationSettings
	svc.SetAlertReconfigurer(func(settings NotificationSettings) error {
		called++
		received = settings
		return nil
	})

	input := UpdateInput{
		Platform:       defaultPlatformSettings(),
		Authentication: defaultAuthenticationSettings(),
		Notifications:  defaultNotificationSettings(),
		DataSync:       defaultDataSyncSettings(),
		Version:        repo.current.Version,
	}
	input.Notifications.LarkAlerts = true
	input.Notifications.LarkAppID = "cli_xxx"

	updated, err := svc.Update(ctx, input)
	require.NoError(t, err)
	require.NotNil(t, updated)
	require.Equal(t, 1, called)
	require.True(t, received.LarkAlerts)
	require.Equal(t, "cli_xxx", received.LarkAppID)
}

func TestDataSyncSettingsDefaultFreshnessThresholds(t *testing.T) {
	settings := defaultDataSyncSettings()

	require.Equal(t, 24, settings.OrgSyncStaleAfterHours)
	require.Equal(t, 72, settings.OrgSyncExpiredAfterHours)
}

func TestLegacyDataSyncSettingsDecodeFillsFreshnessThresholds(t *testing.T) {
	entity := defaultSettingsModel()
	entity.DataSync = model.JSONB{
		"org_sync_enabled": false,
		"org_provider":     "feishu",
		"org_sync_cron":    "0 * * * *",
		"lark_app_id":      "",
		"lark_app_secret":  "",
	}

	settings := entityToSettings(entity)

	require.Equal(t, 24, settings.DataSync.OrgSyncStaleAfterHours)
	require.Equal(t, 72, settings.DataSync.OrgSyncExpiredAfterHours)
}

func TestUpdateRejectsInvalidOrgSyncFreshnessThresholds(t *testing.T) {
	tests := []struct {
		name          string
		staleHours    int
		expiredHours  int
		expectedError string
	}{
		{
			name:          "zero stale",
			staleHours:    0,
			expiredHours:  72,
			expectedError: "org_sync_stale_after_hours must be a positive integer",
		},
		{
			name:          "negative expired",
			staleHours:    24,
			expiredHours:  -1,
			expectedError: "org_sync_expired_after_hours must be a positive integer",
		},
		{
			name:          "stale equals expired",
			staleHours:    72,
			expiredHours:  72,
			expectedError: "org_sync_stale_after_hours must be less than org_sync_expired_after_hours",
		},
		{
			name:          "stale exceeds expired",
			staleHours:    168,
			expiredHours:  72,
			expectedError: "org_sync_stale_after_hours must be less than org_sync_expired_after_hours",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := context.Background()
			repo := &fakeSettingsRepo{current: defaultSettingsModel()}
			svc := New(repo, nil, nil)
			input := UpdateInput{
				Platform:       defaultPlatformSettings(),
				Authentication: defaultAuthenticationSettings(),
				Notifications:  defaultNotificationSettings(),
				DataSync:       defaultDataSyncSettings(),
				Version:        repo.current.Version,
			}
			input.DataSync.OrgSyncStaleAfterHours = tt.staleHours
			input.DataSync.OrgSyncExpiredAfterHours = tt.expiredHours

			updated, err := svc.Update(ctx, input)

			require.Nil(t, updated)
			require.ErrorIs(t, err, ErrInvalidSettings)
			require.Contains(t, err.Error(), tt.expectedError)
			require.Equal(t, uint64(1), repo.current.Version)
		})
	}
}

func TestUpdateTriggersOrgSyncReconfigurerWhenFreshnessThresholdsChange(t *testing.T) {
	ctx := context.Background()
	repo := &fakeSettingsRepo{current: defaultSettingsModel()}
	svc := New(repo, nil, nil)

	var called int
	var received DataSyncSettings
	svc.SetOrgSyncReconfigurer(func(settings DataSyncSettings) error {
		called++
		received = settings
		return nil
	})

	input := UpdateInput{
		Platform:       defaultPlatformSettings(),
		Authentication: defaultAuthenticationSettings(),
		Notifications:  defaultNotificationSettings(),
		DataSync:       defaultDataSyncSettings(),
		Version:        repo.current.Version,
	}
	input.DataSync.OrgSyncStaleAfterHours = 48
	input.DataSync.OrgSyncExpiredAfterHours = 168

	updated, err := svc.Update(ctx, input)

	require.NoError(t, err)
	require.NotNil(t, updated)
	require.Equal(t, 1, called)
	require.Equal(t, 48, received.OrgSyncStaleAfterHours)
	require.Equal(t, 168, received.OrgSyncExpiredAfterHours)
}
