package serviceactor

import (
	"context"
	"database/sql"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

func TestDeleteEventDeliveriesBeforeIsBoundedAndScoped(t *testing.T) {
	t.Parallel()

	cutoff := time.Date(2026, 5, 30, 1, 2, 3, 0, time.UTC)
	db := &eventDeliveryRetentionCapture{
		rows: &eventDeliveryRetentionRows{ids: []string{"svced_old_1", "svced_old_2"}},
	}

	deleted, err := deleteEventDeliveriesBefore(context.Background(), db, EventDeliveryRetentionFilter{
		ServiceKind:      KindApp,
		SubscriptionKind: "pull_request",
		Cutoff:           cutoff,
		Limit:            eventDeliveryRetentionDeleteBatchMax + 100,
	})

	require.NoError(t, err)
	require.Equal(t, int64(2), deleted)
	require.Len(t, db.queries, 1)
	query := strings.Join(strings.Fields(db.queries[0]), " ")
	require.Contains(t, query, "FROM service_event_deliveries")
	require.Contains(t, query, "service_kind = $1")
	require.Contains(t, query, "subscription_kind = $2")
	require.Contains(t, query, "attempted_at < $3")
	require.Contains(t, query, "ORDER BY attempted_at ASC, id ASC")
	require.Contains(t, query, "LIMIT $4")
	require.Equal(t, []any{KindApp, "pull_request", cutoff, eventDeliveryRetentionDeleteBatchMax}, db.args[0])
	require.Len(t, db.execQueries, 1)
	deleteQuery := strings.Join(strings.Fields(db.execQueries[0]), " ")
	require.Contains(t, deleteQuery, "WITH delete_delivery_ids AS")
	require.Contains(t, deleteQuery, "SELECT $1 AS id")
	require.Contains(t, deleteQuery, "SELECT $2 AS id")
	require.Contains(t, deleteQuery, "DELETE FROM service_event_deliveries")
	require.Contains(t, deleteQuery, "id IN (SELECT id FROM delete_delivery_ids)")
	require.Equal(t, []any{"svced_old_1", "svced_old_2"}, db.execArgs[0])
}

type eventDeliveryRetentionCapture struct {
	queries     []string
	args        [][]any
	rows        coredb.Rows
	execQueries []string
	execArgs    [][]any
}

func (db *eventDeliveryRetentionCapture) Query(_ context.Context, query string, args ...any) (coredb.Rows, error) {
	db.queries = append(db.queries, query)
	db.args = append(db.args, append([]any(nil), args...))
	return db.rows, nil
}

func (db *eventDeliveryRetentionCapture) QueryRow(context.Context, string, ...any) coredb.Row {
	return eventDeliveryRetentionRow{err: sql.ErrNoRows}
}

func (db *eventDeliveryRetentionCapture) Exec(_ context.Context, query string, args ...any) (coredb.Result, error) {
	db.execQueries = append(db.execQueries, query)
	db.execArgs = append(db.execArgs, append([]any(nil), args...))
	rowsAffected := int64(1)
	if strings.Contains(query, "DELETE FROM service_event_deliveries") {
		rowsAffected = int64(len(args))
	}
	return eventDeliveryRetentionResult{rowsAffected: rowsAffected}, nil
}

type eventDeliveryRetentionRows struct {
	ids []string
	idx int
}

func (rows *eventDeliveryRetentionRows) Close() {}

func (rows *eventDeliveryRetentionRows) Err() error { return nil }

func (rows *eventDeliveryRetentionRows) Next() bool {
	return rows.idx < len(rows.ids)
}

func (rows *eventDeliveryRetentionRows) Scan(dest ...any) error {
	if rows.idx >= len(rows.ids) {
		return sql.ErrNoRows
	}
	if len(dest) != 1 {
		return sql.ErrNoRows
	}
	if target, ok := dest[0].(*string); ok {
		*target = rows.ids[rows.idx]
	}
	rows.idx++
	return nil
}

type eventDeliveryRetentionRow struct {
	err error
}

func (row eventDeliveryRetentionRow) Scan(...any) error {
	if row.err != nil {
		return row.err
	}
	return nil
}

type eventDeliveryRetentionResult struct {
	rowsAffected int64
}

func (result eventDeliveryRetentionResult) RowsAffected() int64 {
	return result.rowsAffected
}
