package backup

import (
	"context"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func newReimportTestManager() *Manager {
	return &Manager{logger: log.New(io.Discard, "", 0)}
}

func writeDump(t *testing.T, nsRoot, stack string, dbType DBType) string {
	t.Helper()
	dir := AppDBDumpPath(nsRoot, stack)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		t.Fatal(err)
	}
	p := filepath.Join(dir, fmt.Sprintf("%s-%s.sql", stack, dbType))
	if err := os.WriteFile(p, []byte("-- dump\nDROP TABLE IF EXISTS t;\n"), 0o644); err != nil {
		t.Fatal(err)
	}
	return p
}

// TestReimportDBDumps_ImportsWhenDumpAndDBPresent asserts F17: when a captured .sql dump exists and a
// matching running DB is discovered, reimportDBDumps replays it. The PRE-FIX restore never called any
// import — this orchestration is the fix.
func TestReimportDBDumps_ImportsWhenDumpAndDBPresent(t *testing.T) {
	nsRoot := t.TempDir()
	wantPath := writeDump(t, nsRoot, "app", DBTypeMariaDB)

	m := newReimportTestManager()
	m.discoverDBs = func(ctx context.Context) ([]DiscoveredDB, error) {
		return []DiscoveredDB{
			{StackName: "other", DBType: DBTypePostgres, ContainerName: "other-db"},
			{StackName: "app", DBType: DBTypeMariaDB, ContainerName: "app-db", ContainerID: "cid"},
		}, nil
	}
	var gotPath string
	var gotDB DiscoveredDB
	m.importDBDump = func(ctx context.Context, db DiscoveredDB, dumpPath string) error {
		gotPath, gotDB = dumpPath, db
		return nil
	}

	n, err := m.reimportDBDumps(context.Background(), "app", nsRoot)
	if err != nil {
		t.Fatalf("reimportDBDumps: %v", err)
	}
	if n != 1 {
		t.Fatalf("imported = %d, want 1", n)
	}
	if gotPath != wantPath {
		t.Fatalf("imported path = %q, want %q", gotPath, wantPath)
	}
	if gotDB.ContainerName != "app-db" {
		t.Fatalf("imported into %q, want app-db (must match the stack's own DB)", gotDB.ContainerName)
	}
}

// TestReimportDBDumps_FailureSurfaces asserts an import failure is RETURNED, not swallowed (a failed
// data restore must not read as success).
func TestReimportDBDumps_FailureSurfaces(t *testing.T) {
	nsRoot := t.TempDir()
	writeDump(t, nsRoot, "app", DBTypeMariaDB)

	m := newReimportTestManager()
	m.discoverDBs = func(ctx context.Context) ([]DiscoveredDB, error) {
		return []DiscoveredDB{{StackName: "app", DBType: DBTypeMariaDB, ContainerName: "app-db"}}, nil
	}
	m.importDBDump = func(ctx context.Context, db DiscoveredDB, dumpPath string) error {
		return fmt.Errorf("boom")
	}

	if _, err := m.reimportDBDumps(context.Background(), "app", nsRoot); err == nil || !strings.Contains(err.Error(), "boom") {
		t.Fatalf("expected the import failure to surface, got %v", err)
	}
}

// TestReimportDBDumps_NoDumpNoImport asserts apps with no .sql dump never trigger discovery/import.
func TestReimportDBDumps_NoDumpNoImport(t *testing.T) {
	nsRoot := t.TempDir()
	m := newReimportTestManager()
	discoverCalled := false
	m.discoverDBs = func(ctx context.Context) ([]DiscoveredDB, error) {
		discoverCalled = true
		return nil, nil
	}
	m.importDBDump = func(ctx context.Context, db DiscoveredDB, dumpPath string) error {
		t.Fatal("importDBDump must not be called when there is no dump")
		return nil
	}
	n, err := m.reimportDBDumps(context.Background(), "app", nsRoot)
	if err != nil || n != 0 {
		t.Fatalf("reimportDBDumps with no dump = (%d, %v), want (0, nil)", n, err)
	}
	if discoverCalled {
		t.Fatalf("discovery should be skipped when there is no .sql dump")
	}
}

// TestReimportDBDumps_DumpButNoMatchingDB asserts that a dump with no matching running DB container is a
// non-fatal skip (logged), returning 0 imported and no error — the app is up, just no DB matched.
func TestReimportDBDumps_DumpButNoMatchingDB(t *testing.T) {
	nsRoot := t.TempDir()
	writeDump(t, nsRoot, "app", DBTypeMariaDB)
	m := newReimportTestManager()
	m.discoverDBs = func(ctx context.Context) ([]DiscoveredDB, error) {
		return []DiscoveredDB{{StackName: "different", DBType: DBTypeMariaDB}}, nil
	}
	m.importDBDump = func(ctx context.Context, db DiscoveredDB, dumpPath string) error {
		t.Fatal("must not import when no DB matches the stack")
		return nil
	}
	n, err := m.reimportDBDumps(context.Background(), "app", nsRoot)
	if err != nil || n != 0 {
		t.Fatalf("= (%d, %v), want (0, nil)", n, err)
	}
}
