package sync_test

import (
	"context"
	"io"
	"log/slog"
	"os"
	"testing"

	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/stretchr/testify/require"

	"git.fabledsword.com/bvandeusen/minstrel/internal/db"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
	"git.fabledsword.com/bvandeusen/minstrel/internal/dbtest"
	"git.fabledsword.com/bvandeusen/minstrel/internal/sync"
)

func testPool(t *testing.T) *pgxpool.Pool {
	t.Helper()
	if testing.Short() {
		t.Skip("skipping integration test in -short mode")
	}
	dsn := os.Getenv("MINSTREL_TEST_DATABASE_URL")
	if dsn == "" {
		t.Skip("MINSTREL_TEST_DATABASE_URL not set")
	}
	if err := db.Migrate(dsn, slog.New(slog.NewTextHandler(io.Discard, nil))); err != nil {
		t.Fatalf("migrate: %v", err)
	}
	pool, err := pgxpool.New(context.Background(), dsn)
	if err != nil {
		t.Fatalf("pool: %v", err)
	}
	t.Cleanup(pool.Close)
	dbtest.ResetDB(t, pool)
	return pool
}

func TestLogChange_WritesRowInTx(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()

	tx, err := pool.Begin(ctx)
	require.NoError(t, err)
	defer func() { _ = tx.Rollback(ctx) }()

	require.NoError(t, sync.LogChange(ctx, tx, sync.EntityArtist, "artist-123", sync.OpUpsert))
	require.NoError(t, tx.Commit(ctx))

	q := dbq.New(pool)
	rows, err := q.GetLibraryChangesSince(ctx, dbq.GetLibraryChangesSinceParams{ID: 0, Limit: 10})
	require.NoError(t, err)
	require.NotEmpty(t, rows)
	last := rows[len(rows)-1]
	require.Equal(t, "artist", last.EntityType)
	require.Equal(t, "artist-123", last.EntityID)
	require.Equal(t, "upsert", last.Op)
}

func TestLogChange_RollbackDoesNotPersist(t *testing.T) {
	pool := testPool(t)
	ctx := context.Background()

	q := dbq.New(pool)
	cursorBefore, err := q.GetMaxLibraryChangeCursor(ctx)
	require.NoError(t, err)

	tx, err := pool.Begin(ctx)
	require.NoError(t, err)
	require.NoError(t, sync.LogChange(ctx, tx, sync.EntityAlbum, "album-456", sync.OpDelete))
	require.NoError(t, tx.Rollback(ctx))

	cursorAfter, err := q.GetMaxLibraryChangeCursor(ctx)
	require.NoError(t, err)
	require.Equal(t, cursorBefore, cursorAfter, "rollback must not advance cursor")
}

// Pure unit tests — run even with -short.
func TestEncodeLikeID(t *testing.T) {
	require.Equal(t, "u1:t2", sync.EncodeLikeID("u1", "t2"))
}

func TestEncodePlaylistTrackID(t *testing.T) {
	require.Equal(t, "p1:t2", sync.EncodePlaylistTrackID("p1", "t2"))
}
