package backup

import (
	"errors"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/alexei/tinyforge/internal/store"
)

// newTestEngine spins up an isolated store + engine pair for tests.
// Each test gets its own tempdir so backup files do not collide.
func newTestEngine(t *testing.T) (*Engine, *store.Store, string) {
	t.Helper()
	dir := t.TempDir()
	dbPath := filepath.Join(dir, "tinyforge.db")
	st, err := store.New(dbPath)
	if err != nil {
		t.Fatalf("store.New: %v", err)
	}
	t.Cleanup(func() { _ = st.Close() })

	eng, err := New(st, dbPath, dir)
	if err != nil {
		t.Fatalf("backup.New: %v", err)
	}
	return eng, st, dbPath
}

func TestPrepareRestore_RejectsTinyFile(t *testing.T) {
	eng, st, _ := newTestEngine(t)

	// Plant a backup row with a tiny file masquerading as a backup.
	tinyPath := filepath.Join(eng.BackupDir(), "tinyforge-manual-junk.db")
	if err := os.WriteFile(tinyPath, []byte("hi"), 0o600); err != nil {
		t.Fatalf("write tiny: %v", err)
	}
	bk, err := st.CreateBackup(store.Backup{
		Filename:   "tinyforge-manual-junk.db",
		SizeBytes:  2,
		BackupType: "manual",
	})
	if err != nil {
		t.Fatalf("CreateBackup row: %v", err)
	}

	if _, err := eng.PrepareRestore(bk.ID); err == nil {
		t.Fatal("expected PrepareRestore to reject tiny file, got nil")
	} else if !strings.Contains(err.Error(), "suspiciously small") {
		t.Errorf("error = %v, want 'suspiciously small'", err)
	}
}

func TestPrepareRestore_RejectsNonSQLite(t *testing.T) {
	eng, st, _ := newTestEngine(t)

	// 200 bytes of non-SQLite garbage: passes the size check, fails
	// the header magic check.
	garbagePath := filepath.Join(eng.BackupDir(), "tinyforge-manual-bogus.db")
	junk := make([]byte, 200)
	for i := range junk {
		junk[i] = byte('x')
	}
	if err := os.WriteFile(garbagePath, junk, 0o600); err != nil {
		t.Fatalf("write junk: %v", err)
	}
	bk, err := st.CreateBackup(store.Backup{
		Filename:   "tinyforge-manual-bogus.db",
		SizeBytes:  int64(len(junk)),
		BackupType: "manual",
	})
	if err != nil {
		t.Fatalf("CreateBackup row: %v", err)
	}

	if _, err := eng.PrepareRestore(bk.ID); err == nil {
		t.Fatal("expected PrepareRestore to reject non-SQLite blob, got nil")
	} else if !strings.Contains(err.Error(), "header") {
		t.Errorf("error = %v, want header mismatch", err)
	}
}

func TestPrepareRestore_AcceptsValidVacuumInto(t *testing.T) {
	eng, _, _ := newTestEngine(t)

	// A fresh CreateBackup from the engine itself is, by construction,
	// a valid SQLite database — VACUUM INTO produces a clean copy.
	bk, err := eng.CreateBackup("manual")
	if err != nil {
		t.Fatalf("CreateBackup: %v", err)
	}
	path, err := eng.PrepareRestore(bk.ID)
	if err != nil {
		t.Fatalf("PrepareRestore on valid backup: %v", err)
	}
	if path == "" {
		t.Errorf("PrepareRestore returned empty path")
	}
}

func TestPrepareRestore_UnknownID(t *testing.T) {
	eng, _, _ := newTestEngine(t)

	_, err := eng.PrepareRestore("nonexistent-id")
	if err == nil {
		t.Fatal("expected error for unknown id, got nil")
	}
	if errors.Is(err, store.ErrNotFound) {
		// fine — wrapped through RestorePath
	}
}
