package handler

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

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

	coremodel "security-center-backend/internal/model"
	caasmmodel "security-center-backend/internal/modules/caasm/model"
	"security-center-backend/internal/modules/caasm/repository"
	"security-center-backend/internal/modules/caasm/service"
)

type recordingSyncJobEventRepo struct {
	listResult []caasmmodel.SyncJobEvent
	listOpts   repository.SyncJobEventListOptions
}

func (r *recordingSyncJobEventRepo) Create(ctx context.Context, event *caasmmodel.SyncJobEvent) error {
	return nil
}

func (r *recordingSyncJobEventRepo) List(ctx context.Context, syncJobID uint64, opts repository.SyncJobEventListOptions) ([]caasmmodel.SyncJobEvent, error) {
	r.listOpts = opts
	return append([]caasmmodel.SyncJobEvent(nil), r.listResult...), nil
}

func TestGetSyncJobEventsSupportsCursorQuery(t *testing.T) {
	gin.SetMode(gin.TestMode)
	repo := &recordingSyncJobEventRepo{
		listResult: []caasmmodel.SyncJobEvent{
			{Base: coremodel.Base{ID: 9}, Message: "latest"},
			{Base: coremodel.Base{ID: 8}, Message: "older"},
		},
	}
	h := &Handler{jobs: service.NewSyncJobService(nil, repo)}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	req := httptest.NewRequest(http.MethodGet, "/caasm/sync-jobs/77/events?limit=2&before_id=12", nil)
	c.Request = req
	c.Params = gin.Params{{Key: "id", Value: "77"}}

	h.GetSyncJobEvents(c)

	require.Equal(t, http.StatusOK, w.Code)
	var apiResp struct {
		Code    int                       `json:"code"`
		Message string                    `json:"message"`
		Data    []caasmmodel.SyncJobEvent `json:"data"`
	}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &apiResp))
	assert.Equal(t, 0, apiResp.Code)
	require.Len(t, apiResp.Data, 2)
	assert.Equal(t, uint64(9), apiResp.Data[0].ID)
	assert.Equal(t, uint64(8), apiResp.Data[1].ID)
	assert.Equal(t, 2, repo.listOpts.Limit)
	require.NotNil(t, repo.listOpts.BeforeID)
	assert.Equal(t, uint64(12), *repo.listOpts.BeforeID)
	assert.Nil(t, repo.listOpts.AfterID)
}

func TestGetSyncJobEventsRejectsConflictingCursors(t *testing.T) {
	gin.SetMode(gin.TestMode)
	h := &Handler{jobs: service.NewSyncJobService(nil, &recordingSyncJobEventRepo{})}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	req := httptest.NewRequest(http.MethodGet, "/caasm/sync-jobs/77/events?before_id=12&after_id=13", nil)
	c.Request = req
	c.Params = gin.Params{{Key: "id", Value: "77"}}

	h.GetSyncJobEvents(c)

	require.Equal(t, http.StatusOK, w.Code)
	var apiResp struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
	}
	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &apiResp))
	assert.Equal(t, http.StatusBadRequest, apiResp.Code)
	assert.Contains(t, apiResp.Message, "before_id and after_id cannot be used together")
}
