// Package deps prepares dev-mode dependencies: ensures web/node_modules,
// provisions TiDB via the active provider, ensures databases, runs migrations.
package deps

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"os/exec"
	"path/filepath"

	"github.com/tidbcloud/AgentTeam/dev/internal/config"
	"github.com/tidbcloud/AgentTeam/dev/internal/features"
	"github.com/tidbcloud/AgentTeam/dev/internal/migrate"
	"github.com/tidbcloud/AgentTeam/dev/internal/tidb"
	"github.com/tidbcloud/AgentTeam/x/proc"
)

var sweepRepoProcesses = func(repoRoot string) {
	proc.New(io.Discard).SweepRepo(repoRoot)
}

// sweepStalePlaygrounds is stubbable so tests can assert it is invoked
// without actually scanning/killing the host process table.
var sweepStalePlaygrounds = func(repoRoot string) {
	tidb.SweepStalePlaygrounds(repoRoot)
}

// Processes is a handle on dev dependencies that need cleanup at shutdown.
type Processes struct {
	repoRoot string
	provider tidb.Provider
}

// Prepare runs all dependency bootstrap steps and returns a Processes handle
// whose Stop must be called on shutdown.
func Prepare(ctx context.Context, repoRoot string, opts features.Options, cfg *config.Config) (*Processes, error) {
	cleanupStaleDevProcesses(repoRoot)
	if err := runScript(repoRoot, "scripts/ensure-web-node-modules.sh"); err != nil {
		return nil, err
	}
	provider, err := tidb.New(opts)
	if err != nil {
		return nil, err
	}
	if err := provider.Ensure(ctx, opts, cfg); err != nil {
		return nil, err
	}
	tidb.EnsureDatabases(opts, cfg)
	if err := migrate.Run(repoRoot, cfg); err != nil {
		return nil, err
	}
	if opts.UltraEnabled() {
		if err := ensureWorkspaceMessageSearchReady(ctx, repoRoot); err != nil {
			return nil, err
		}
	}
	return &Processes{repoRoot: repoRoot, provider: provider}, nil
}

// Stop tears down the prepared dependencies.
func (p *Processes) Stop() {
	if p == nil || p.provider == nil {
		return
	}
	slog.Info("== dependencies cleanup ==")
	p.provider.Stop()
}

func cleanupStaleDevProcesses(repoRoot string) {
	sweepStalePlaygrounds(repoRoot)
	sweepRepoProcesses(repoRoot)
}

func runScript(repoRoot, rel string) error {
	slog.Info("running script", "path", rel)
	cmd := exec.Command(filepath.Join(repoRoot, rel))
	cmd.Dir = repoRoot
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("%s: %w", rel, err)
	}
	return nil
}

func ensureWorkspaceMessageSearchReady(ctx context.Context, repoRoot string) error {
	cmd := exec.CommandContext(ctx, "go", "run", "./cmd/agentteam-tidb-admin", "workspace-message-search-ensure-ready")
	cmd.Dir = repoRoot
	cmd.Stdout = newPrefixWriter("tidb-admin")
	cmd.Stderr = newPrefixWriter("tidb-admin")
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("workspace-message-search-ensure-ready: %w", err)
	}
	return nil
}
