package appuser

import (
	"context"
	"encoding/json"
	"strings"
	"testing"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"
)

type recordingAppUserExportStore struct {
	job *model.AdminJob
}

func (s *recordingAppUserExportStore) CreateJob(job *model.AdminJob) error {
	copy := *job
	copy.ID = 81
	job.ID = copy.ID
	s.job = &copy
	return nil
}

func TestCreateExportJobPersistsFilterSnapshot(t *testing.T) {
	store := &recordingAppUserExportStore{}
	service := &Service{
		jobStore: store,
		jobsConfig: config.JobsConfig{
			Enabled:     true,
			MaxAttempts: 5,
		},
	}
	ctx := appctx.WithContext(context.Background(), "lalu")
	response, err := service.CreateExportJob(ctx, shared.Actor{UserID: 7, Username: "operator"}, listQuery{
		Country:       " ph ",
		Status:        "active",
		Keyword:       "1299324",
		SortBy:        "coin",
		SortDirection: "asc",
		Page:          9,
		PageSize:      10,
	})
	if err != nil {
		t.Fatalf("create export job: %v", err)
	}
	if response.JobID != 81 || response.SnapshotAtMs <= 0 || store.job == nil {
		t.Fatalf("response/job mismatch: response=%+v job=%+v", response, store.job)
	}
	if store.job.Type != AppUserExportJobType || store.job.MaxAttempts != 5 || store.job.CreatedBy != 7 {
		t.Fatalf("job metadata mismatch: %+v", store.job)
	}
	var payload appUserExportPayload
	if err := json.Unmarshal([]byte(store.job.PayloadJSON), &payload); err != nil {
		t.Fatalf("decode payload: %v", err)
	}
	if payload.AppCode != "lalu" || payload.Query.Country != "PH" || payload.Query.Status != "active" || payload.Query.Keyword != "1299324" {
		t.Fatalf("filter snapshot mismatch: %+v", payload)
	}
	if payload.Query.Page != 1 || payload.Query.PageSize != 100 || payload.Query.SnapshotAtMs != response.SnapshotAtMs {
		t.Fatalf("pagination snapshot mismatch: %+v", payload.Query)
	}
	if payload.Query.SortBy != "created_at" || payload.Query.SortDirection != "desc" {
		t.Fatalf("export must use stable database pagination instead of coin projection sort: %+v", payload.Query)
	}
}

func TestStableAppUserExportQueryForcesDescendingKeysetOrder(t *testing.T) {
	for _, query := range []listQuery{
		{SortBy: "created_at", SortDirection: "asc"},
		{SortBy: "coin", SortDirection: "asc"},
	} {
		stable := stableAppUserExportQuery(normalizeListQuery(query))
		if stable.SortBy != "created_at" || stable.SortDirection != "desc" {
			t.Fatalf("export order must match descending keyset: input=%+v stable=%+v", query, stable)
		}
	}
}

func TestAppUserExportWhereUsesSnapshotAndKeysetCursor(t *testing.T) {
	query := normalizeListQuery(listQuery{
		SnapshotAtMs:            1_000,
		ExportCursorCreatedAtMs: 900,
		ExportCursorUserID:      88,
	})
	whereSQL, args := appUserListWhereSQLAt("lalu", query, query.SnapshotAtMs)
	for _, fragment := range []string{
		"u.created_at_ms <= ?",
		"u.created_at_ms < ? OR (u.created_at_ms = ? AND u.user_id < ?)",
	} {
		if !strings.Contains(whereSQL, fragment) {
			t.Fatalf("export where SQL missing %q: %s", fragment, whereSQL)
		}
	}
	wantTail := []any{int64(1_000), int64(900), int64(900), int64(88)}
	if len(args) < len(wantTail) {
		t.Fatalf("export args too short: %+v", args)
	}
	for index, want := range wantTail {
		if got := args[len(args)-len(wantTail)+index]; got != want {
			t.Fatalf("export cursor arg[%d]=%v want=%v all=%+v", index, got, want, args)
		}
	}
}
