package backup

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"time"
)

// reimportDBDumps replays the captured per-app .sql dumps back into the app's now-running database
// container(s) — the F17 fix. The per-app backup captures a logical SQL dump (DumpOne →
// <stack>-<dbtype>.sql) but the legacy restore only repopulated Docker volume tars and NEVER replayed
// the dump, so DB-resident data (e.g. rows in a DB whose data dir is a bind mount, not a named volume)
// did not come back. This runs AFTER volume restore + stack bring-up, so the dump WINS over any
// volume-tar copy of the DB (the operator-chosen precedence: the consistent logical dump is authoritative).
//
// It uses the live container's OWN discovered credentials (DiscoveredDB), so no env threading is needed.
// A dump whose DB container is not found is logged and skipped; an actual import FAILURE is returned
// (surfaced, not swallowed) so a failed data restore cannot read as success.
func (m *Manager) reimportDBDumps(ctx context.Context, stackName, nsRoot string) (int, error) {
	dumpDir := AppDBDumpPath(nsRoot, stackName)
	entries, err := os.ReadDir(dumpDir)
	if err != nil {
		if os.IsNotExist(err) {
			return 0, nil // no DB dumps for this app
		}
		return 0, fmt.Errorf("reading db-dump dir: %w", err)
	}
	hasDump := false
	for _, e := range entries {
		if !e.IsDir() && filepath.Ext(e.Name()) == ".sql" {
			hasDump = true
			break
		}
	}
	if !hasDump {
		return 0, nil
	}

	discover := m.discoverDBs
	if discover == nil {
		discover = func(ctx context.Context) ([]DiscoveredDB, error) {
			return DiscoverDatabases(ctx, m.logger, m.isDebug(), m.knownStackNames())
		}
	}
	imp := m.importDBDump
	if imp == nil {
		imp = func(ctx context.Context, db DiscoveredDB, dumpPath string) error {
			return ImportDump(ctx, db, dumpPath, m.logger, m.isDebug())
		}
	}

	dbs, err := discover(ctx)
	if err != nil {
		return 0, fmt.Errorf("discovering DB containers for %s: %w", stackName, err)
	}

	var imported int
	for _, db := range dbs {
		if db.StackName != stackName {
			continue
		}
		// The dump for this DB is named "<stack>-<dbtype>.sql" (see DumpOne).
		dumpPath := filepath.Join(dumpDir, fmt.Sprintf("%s-%s.sql", stackName, db.DBType))
		if _, statErr := os.Stat(dumpPath); statErr != nil {
			continue // no dump for this particular DB engine
		}
		m.logger.Printf("[INFO] [backup] Restore %s: replaying DB dump into %s (%s)", stackName, db.ContainerName, db.DBType)
		if err := imp(ctx, db, dumpPath); err != nil {
			return imported, fmt.Errorf("importing %s dump for %s: %w", db.DBType, stackName, err)
		}
		imported++
	}

	if imported == 0 {
		m.logger.Printf("[WARN] [backup] Restore %s: a .sql dump exists but no matching running DB container was found — DB content NOT restored", stackName)
	} else {
		m.logger.Printf("[INFO] [backup] Restore %s: replayed %d DB dump(s)", stackName, imported)
	}
	return imported, nil
}

// reimportDBDumpsCtx is a small helper that runs reimportDBDumps with a bounded context so a stuck DB
// import cannot hang the restore indefinitely.
func (m *Manager) reimportDBDumpsCtx(stackName, nsRoot string) (int, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 35*time.Minute)
	defer cancel()
	return m.reimportDBDumps(ctx, stackName, nsRoot)
}
