package library

import (
	"context"
	"errors"
	"io"
	"log/slog"
	"os"
	"path/filepath"
	"testing"

	"github.com/jackc/pgx/v5/pgtype"
	"github.com/jackc/pgx/v5/pgxpool"

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

func newPool(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 seedTrack(t *testing.T, pool *pgxpool.Pool, filePath string) (dbq.Track, dbq.Album, dbq.Artist) {
	t.Helper()
	q := dbq.New(pool)
	artist, err := q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{
		Name: "Delete Test Artist", SortName: "Delete Test Artist",
	})
	if err != nil {
		t.Fatalf("artist: %v", err)
	}
	album, err := q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{
		Title: "Delete Test Album", SortTitle: "Delete Test Album",
		ArtistID: artist.ID,
	})
	if err != nil {
		t.Fatalf("album: %v", err)
	}
	track, err := q.UpsertTrack(context.Background(), dbq.UpsertTrackParams{
		Title: "Delete Test Track", AlbumID: album.ID, ArtistID: artist.ID,
		DurationMs: 1000, FilePath: filePath, FileSize: 100, FileFormat: "mp3",
	})
	if err != nil {
		t.Fatalf("track: %v", err)
	}
	return track, album, artist
}

func TestDeleteTrackFile_HappyPath(t *testing.T) {
	pool := newPool(t)
	q := dbq.New(pool)

	dir := t.TempDir()
	path := filepath.Join(dir, "track.mp3")
	if err := os.WriteFile(path, []byte("payload"), 0o644); err != nil {
		t.Fatalf("write file: %v", err)
	}
	track, album, _ := seedTrack(t, pool, path)

	if err := DeleteTrackFile(context.Background(), pool, track.ID); err != nil {
		t.Fatalf("DeleteTrackFile: %v", err)
	}

	if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) {
		t.Errorf("file still exists: %v", err)
	}
	if _, err := q.GetTrackByID(context.Background(), track.ID); err == nil {
		t.Errorf("track row still exists")
	}
	// Album row preserved (other tracks may reference it).
	if _, err := q.GetAlbumByID(context.Background(), album.ID); err != nil {
		t.Errorf("album row vanished: %v", err)
	}
}

func TestDeleteTrackFile_FileAlreadyGoneSucceeds(t *testing.T) {
	pool := newPool(t)
	q := dbq.New(pool)

	track, _, _ := seedTrack(t, pool, "/no/such/file/anywhere.mp3")

	if err := DeleteTrackFile(context.Background(), pool, track.ID); err != nil {
		t.Fatalf("DeleteTrackFile with missing file: %v", err)
	}
	if _, err := q.GetTrackByID(context.Background(), track.ID); err == nil {
		t.Errorf("track row still exists")
	}
}

func TestDeleteTrackFile_NotFoundReturnsErr(t *testing.T) {
	pool := newPool(t)

	var bogus pgtype.UUID
	bogus.Bytes = [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
	bogus.Valid = true

	err := DeleteTrackFile(context.Background(), pool, bogus)
	if !errors.Is(err, ErrTrackNotFound) {
		t.Errorf("err = %v, want ErrTrackNotFound", err)
	}
}
