package handler

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"security-center-backend/internal/config"
	coremodel "security-center-backend/internal/model"
	depservice "security-center-backend/internal/modules/department/service"
	"security-center-backend/internal/pkg/logger"
	settingsrepo "security-center-backend/internal/repository"
	settingssvc "security-center-backend/internal/service/settings"
)

type fakeSystemSettingsRepo struct {
	current *coremodel.SystemSettings
}

func (f *fakeSystemSettingsRepo) Get(ctx context.Context) (*coremodel.SystemSettings, error) {
	return f.current, nil
}

func (f *fakeSystemSettingsRepo) Create(ctx context.Context, settings *coremodel.SystemSettings) (*coremodel.SystemSettings, error) {
	f.current = settings
	return settings, nil
}

func (f *fakeSystemSettingsRepo) Update(ctx context.Context, settings *coremodel.SystemSettings, expectedVersion uint64) (*coremodel.SystemSettings, error) {
	f.current = settings
	return settings, nil
}

func TestLoadOrgSyncRuntimeConfigRejectsDisabledOrgSync(t *testing.T) {
	handler := New(nil, nil, settingsServiceWithDataSync(coremodel.JSONB{
		"org_sync_enabled": false,
		"org_sync_cron":    "0 * * * *",
		"lark_app_id":      "cli_test",
		"lark_app_secret":  "secret",
	}))

	_, err := handler.loadOrgSyncRuntimeConfig(context.Background())

	require.Error(t, err)
	cfgErr, ok := err.(orgSyncConfigError)
	require.True(t, ok)
	require.Equal(t, http.StatusBadRequest, cfgErr.status)
	require.Contains(t, cfgErr.message, "Org Sync is disabled")
}

func TestLoadOrgSyncManualConfigAllowsDisabledOrgSync(t *testing.T) {
	handler := New(nil, nil, settingsServiceWithDataSync(coremodel.JSONB{
		"org_sync_enabled": false,
		"org_sync_cron":    "",
		"lark_app_id":      "cli_test",
		"lark_app_secret":  "secret",
	}))

	cfg, err := handler.loadOrgSyncManualConfig(context.Background())

	require.NoError(t, err)
	require.Equal(t, "cli_test", cfg.LarkAppID)
	require.Equal(t, "secret", cfg.LarkAppSecret)
}

func TestLoadOrgSyncRuntimeConfigRejectsMissingCredentials(t *testing.T) {
	handler := New(nil, nil, settingsServiceWithDataSync(coremodel.JSONB{
		"org_sync_enabled": true,
		"org_sync_cron":    "0 * * * *",
		"lark_app_id":      "cli_test",
		"lark_app_secret":  "",
	}))

	_, err := handler.loadOrgSyncRuntimeConfig(context.Background())

	require.Error(t, err)
	cfgErr, ok := err.(orgSyncConfigError)
	require.True(t, ok)
	require.Equal(t, http.StatusBadRequest, cfgErr.status)
	require.Contains(t, cfgErr.message, "App ID and App Secret")
}

func TestReconfigureRuntimeLoadsCredentialsFromSettings(t *testing.T) {
	gin.SetMode(gin.TestMode)
	log := logger.New(config.LogConfig{Level: "error", Format: "text"})
	deptSvc := depservice.NewDepartmentService(nil, nil, nil, log)
	runtime := depservice.NewOrgSyncRuntimeControl(deptSvc, log)
	handler := New(deptSvc, runtime, settingsServiceWithDataSync(coremodel.JSONB{
		"org_sync_enabled": true,
		"org_sync_cron":    "0 * * * *",
		"lark_app_id":      "cli_test",
		"lark_app_secret":  "secret",
	}))
	router := gin.New()
	router.POST("/departments/runtime/reconfigure", handler.ReconfigureRuntime)

	req := httptest.NewRequest(http.MethodPost, "/departments/runtime/reconfigure", nil)
	recorder := httptest.NewRecorder()
	router.ServeHTTP(recorder, req)

	require.Equal(t, http.StatusOK, recorder.Code)
	var payload struct {
		Code int `json:"code"`
		Data struct {
			SchedulerRunning bool `json:"scheduler_running"`
			LarkConfigured   bool `json:"lark_configured"`
		} `json:"data"`
	}
	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &payload))
	require.Equal(t, 0, payload.Code)
	require.True(t, payload.Data.SchedulerRunning)
	require.True(t, payload.Data.LarkConfigured)
	runtime.Stop()
}

func settingsServiceWithDataSync(dataSync coremodel.JSONB) *settingssvc.Service {
	return settingssvc.New(&fakeSystemSettingsRepo{current: &coremodel.SystemSettings{
		Base:     coremodel.Base{ID: 1},
		Version:  1,
		DataSync: dataSync,
	}}, nil, nil)
}

var _ settingsrepo.SystemSettingsRepository = (*fakeSystemSettingsRepo)(nil)
