package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/envcompat"
	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/devsearch"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
	"github.com/tidbcloud/AgentTeam/internal/tidb"
)

const (
	defaultConnectTimeout                    = 5 * time.Second
	defaultMessageSearchBackfillBatchSize    = 1000
	defaultWorkspaceSearchEnsureReadyTimeout = 20 * time.Minute
	workspaceMessageSearchWorkspaceIDEnv     = "AGENTTEAM_WORKSPACE_MESSAGE_SEARCH_WORKSPACE_ID"
)

var ensureAllWorkspacesSearchReadyFn = func(ctx context.Context, store devsearch.SearchReadinessStore, batchSize int, logw *os.File) (devsearch.EnsureAllWorkspacesResult, error) {
	return devsearch.EnsureAllWorkspacesSearchReady(ctx, store, batchSize, logw)
}

func main() {
	ctx := context.Background()

	command := "workspace-message-search-status"
	if len(os.Args) > 1 {
		command = strings.TrimSpace(os.Args[1])
	}

	switch command {
	case "workspace-org-membership-backfill":
		if err := runWorkspaceOrgMembershipBackfill(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-org-membership-backfill failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-org-membership-verify":
		if err := runWorkspaceOrgMembershipVerify(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-org-membership-verify failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-placement-backfill":
		if err := runWorkspacePlacementBackfill(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-placement-backfill failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-placement-repair":
		if err := runWorkspacePlacementRepair(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-placement-repair failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-placement-verify":
		if err := runWorkspacePlacementVerify(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-placement-verify failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-placement-topology-verify":
		if err := runWorkspacePlacementTopologyVerify(ctx, os.Args[2:]); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-placement-topology-verify failed: %v\n", err)
			os.Exit(1)
		}
	case "channel-attachment-projection-backfill":
		if err := runChannelAttachmentProjectionBackfill(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin channel-attachment-projection-backfill failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-message-search-backfill":
		if err := runWorkspaceMessageSearchBackfill(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-message-search-backfill failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-message-search-finalize":
		if err := runWorkspaceMessageSearchFinalize(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-message-search-finalize failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-message-search-promote-ready":
		if err := runWorkspaceMessageSearchPromoteReady(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-message-search-promote-ready failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-message-search-status":
		if err := runWorkspaceMessageSearchStatus(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-message-search-status failed: %v\n", err)
			os.Exit(1)
		}
	case "workspace-message-search-ensure-ready":
		if err := runWorkspaceMessageSearchEnsureReady(ctx); err != nil {
			fmt.Fprintf(os.Stderr, "agentteam-tidb-admin workspace-message-search-ensure-ready failed: %v\n", err)
			os.Exit(1)
		}
	case "-h", "--help", "help":
		printUsage()
	default:
		printUsage()
		fmt.Fprintf(os.Stderr, "\nunknown command %q\n", command)
		os.Exit(1)
	}
}

func runWorkspaceMessageSearchBackfill(ctx context.Context) error {
	target, err := currentWorkspaceMessageSearchTarget()
	if err != nil {
		return err
	}
	batchSize, err := parseMessageSearchBackfillBatchSize()
	if err != nil {
		return err
	}
	repos, _, closeDB, err := openWorkspaceMessageSearchRepositories(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	var result message.ScopeSearchBackfillResult
	passes := 0
	processedRows := 0
	for {
		result, err = repos.Search.Backfill(ctx, target.workspaceID, "", "", batchSize, time.Now().UTC())
		if err != nil {
			return err
		}
		passes++
		processedRows += result.ProcessedRows
		if strings.TrimSpace(result.State.BackfillStatus) == "complete" {
			break
		}
	}
	fmt.Printf(
		"workspace message search backfill after: workspace=%s status=%s cursor=%d passes=%d processed=%d\n",
		target.workspaceID,
		result.State.BackfillStatus,
		result.State.BackfillCursorGlobalSeq,
		passes,
		processedRows,
	)
	return nil
}

func runWorkspaceMessageSearchFinalize(ctx context.Context) error {
	target, err := currentWorkspaceMessageSearchTarget()
	if err != nil {
		return err
	}
	repos, _, closeDB, err := openWorkspaceMessageSearchRepositories(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	state, err := repos.Search.Finalize(ctx, target.workspaceID, "", "", time.Now().UTC())
	if err != nil {
		return err
	}
	fmt.Printf(
		"workspace message search finalize after: workspace=%s status=%s read_ready=%t\n",
		target.workspaceID,
		state.FinalizeStatus,
		state.ReadReady,
	)
	return nil
}

func runWorkspaceMessageSearchPromoteReady(ctx context.Context) error {
	target, err := currentWorkspaceMessageSearchTarget()
	if err != nil {
		return err
	}
	repos, _, closeDB, err := openWorkspaceMessageSearchRepositories(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	state, err := repos.Search.PromoteReady(ctx, target.workspaceID, "", "", time.Now().UTC())
	if err != nil {
		return err
	}
	fmt.Printf(
		"workspace message search promote-ready after: workspace=%s finalize_status=%s read_ready=%t\n",
		target.workspaceID,
		state.FinalizeStatus,
		state.ReadReady,
	)
	return nil
}

func runWorkspaceMessageSearchStatus(ctx context.Context) error {
	target, err := currentWorkspaceMessageSearchTarget()
	if err != nil {
		return err
	}
	repos, _, closeDB, err := openWorkspaceMessageSearchRepositories(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	state, err := repos.Search.LoadReadiness(ctx, target.workspaceID, "", "")
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(os.Stdout)
	encoder.SetEscapeHTML(false)
	return encoder.Encode(workspaceMessageSearchStatus{
		WorkspaceID:             state.WorkspaceID,
		BackfillStatus:          state.BackfillStatus,
		BackfillCursorGlobalSeq: state.BackfillCursorGlobalSeq,
		FinalizeStatus:          state.FinalizeStatus,
		ReadReady:               state.ReadReady,
	})
}

func runWorkspaceMessageSearchEnsureReady(ctx context.Context) error {
	batchSize, err := parseMessageSearchBackfillBatchSize()
	if err != nil {
		return err
	}
	repos, queryer, closeDB, err := openWorkspaceMessageSearchRepositories(ctx)
	if err != nil {
		return err
	}
	defer closeDB()
	_ = repos

	ensureCtx, cancel := context.WithTimeout(ctx, defaultWorkspaceSearchEnsureReadyTimeout)
	defer cancel()
	store := devsearch.NewSQLSearchReadinessStore(queryer)
	result, ensureErr := ensureAllWorkspacesSearchReadyFn(ensureCtx, store, batchSize, os.Stderr)
	encoder := json.NewEncoder(os.Stdout)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(result); err != nil {
		return fmt.Errorf("encode workspace search ensure-ready result: %w", err)
	}
	if ensureErr != nil {
		return ensureErr
	}
	return nil
}

func runWorkspacePlacementBackfill(ctx context.Context, args []string) error {
	workspaceGroupID, err := parseWorkspacePlacementBackfillGroupID(args)
	if err != nil {
		return err
	}
	store, closeDB, err := openWorkspacePlacementStore(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	inserted, err := store.BackfillWorkspacePlacements(ctx, workspaceGroupID)
	if err != nil {
		return err
	}
	missing, err := store.CountWorkspacesMissingPlacement(ctx)
	if err != nil {
		return err
	}
	fmt.Printf("workspace placement backfill: workspace_group_id=%s inserted=%d missing_placement_count=%d\n", workspaceGroupID, inserted, missing)
	if missing != 0 {
		return fmt.Errorf("workspace placement backfill incomplete: missing_placement_count=%d", missing)
	}
	return nil
}

func runChannelAttachmentProjectionBackfill(ctx context.Context) error {
	databaseURL, err := currentDatabaseURL()
	if err != nil {
		return err
	}
	connectTimeout, err := parseConnectTimeout()
	if err != nil {
		return err
	}
	admin := tidb.NewAdmin()
	db, err := admin.Open(ctx, databaseURL, connectTimeout)
	if err != nil {
		return fmt.Errorf("connect core main-path database: %w", err)
	}
	defer func() { _ = db.Close() }()

	reconciled, err := tidb.BackfillChannelAttachmentProjection(ctx, db)
	if err != nil {
		return err
	}
	missing, orphan, err := tidb.ChannelAttachmentProjectionCompleteness(ctx, db)
	if err != nil {
		return err
	}
	fmt.Printf("channel attachment projection backfill: reconciled_messages=%d missing=%d orphan=%d\n", reconciled, missing, orphan)
	if missing != 0 || orphan != 0 {
		return fmt.Errorf("channel attachment projection cutover incomplete: missing=%d orphan=%d", missing, orphan)
	}
	return nil
}

func parseWorkspacePlacementBackfillGroupID(args []string) (string, error) {
	workspaceGroupID := strings.TrimSpace(os.Getenv("AGENTTEAM_WORKSPACE_GROUP_ID"))
	flagSet := flag.NewFlagSet("workspace-placement-backfill", flag.ContinueOnError)
	flagSet.StringVar(&workspaceGroupID, "workspace-group-id", workspaceGroupID, "workspace group id to assign missing placements")
	if err := flagSet.Parse(args); err != nil {
		return "", err
	}
	if flagSet.NArg() != 0 {
		return "", fmt.Errorf("unexpected workspace-placement-backfill arguments: %s", strings.Join(flagSet.Args(), " "))
	}
	workspaceGroupID = strings.TrimSpace(workspaceGroupID)
	if workspaceGroupID == "" {
		workspaceGroupID = config.DefaultWorkspaceGroupID
	}
	return workspaceGroupID, nil
}

type workspacePlacementRepairTarget struct {
	workspaceID      string
	workspaceGroupID string
}

type workspacePlacementRepairResult struct {
	WorkspaceID      string `json:"workspace_id"`
	WorkspaceGroupID string `json:"workspace_group_id"`
	State            string `json:"state"`
	PlacementEpoch   int64  `json:"placement_epoch"`
}

type workspacePlacementRepairer interface {
	RepairMissingWorkspacePlacement(context.Context, string, string) (workspace.WorkspacePlacement, error)
}

func runWorkspacePlacementRepair(ctx context.Context, args []string) error {
	target, err := parseWorkspacePlacementRepairArgs(args)
	if err != nil {
		return err
	}
	store, closeDB, err := openWorkspacePlacementStore(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	return writeWorkspacePlacementRepair(ctx, store, target, os.Stdout)
}

func parseWorkspacePlacementRepairArgs(args []string) (workspacePlacementRepairTarget, error) {
	target := workspacePlacementRepairTarget{
		workspaceGroupID: strings.TrimSpace(os.Getenv("AGENTTEAM_WORKSPACE_GROUP_ID")),
	}
	flagSet := flag.NewFlagSet("workspace-placement-repair", flag.ContinueOnError)
	flagSet.StringVar(&target.workspaceID, "workspace-id", "", "workspace id whose missing placement to repair")
	flagSet.StringVar(&target.workspaceGroupID, "workspace-group-id", target.workspaceGroupID, "active workspace group id to assign")
	if err := flagSet.Parse(args); err != nil {
		return workspacePlacementRepairTarget{}, err
	}
	if flagSet.NArg() != 0 {
		return workspacePlacementRepairTarget{}, fmt.Errorf("unexpected workspace-placement-repair arguments: %s", strings.Join(flagSet.Args(), " "))
	}
	target.workspaceID = strings.TrimSpace(target.workspaceID)
	target.workspaceGroupID = strings.TrimSpace(target.workspaceGroupID)
	if target.workspaceID == "" {
		return workspacePlacementRepairTarget{}, fmt.Errorf("workspace-placement-repair requires --workspace-id")
	}
	if target.workspaceGroupID == "" {
		return workspacePlacementRepairTarget{}, fmt.Errorf("workspace-placement-repair requires --workspace-group-id or AGENTTEAM_WORKSPACE_GROUP_ID")
	}
	return target, nil
}

func writeWorkspacePlacementRepair(ctx context.Context, repairer workspacePlacementRepairer, target workspacePlacementRepairTarget, output io.Writer) error {
	if repairer == nil {
		return fmt.Errorf("workspace placement repairer is not configured")
	}
	if output == nil {
		return fmt.Errorf("workspace placement repair output is not configured")
	}
	placement, err := repairer.RepairMissingWorkspacePlacement(ctx, target.workspaceID, target.workspaceGroupID)
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(output)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(workspacePlacementRepairResult{
		WorkspaceID:      placement.WorkspaceID,
		WorkspaceGroupID: placement.WorkspaceGroupID,
		State:            placement.State,
		PlacementEpoch:   placement.PlacementEpoch,
	}); err != nil {
		return fmt.Errorf("encode workspace placement repair result for workspace %s: %w", target.workspaceID, err)
	}
	return nil
}

type workspacePlacementVerifier interface {
	VerifyWorkspacePlacements(context.Context, string) (workspace.WorkspacePlacementVerification, error)
}

type workspacePlacementTopologyVerifier interface {
	VerifyWorkspacePlacementTopology(context.Context) (workspace.WorkspacePlacementTopologyVerification, error)
}

func runWorkspacePlacementVerify(ctx context.Context, args []string) error {
	workspaceGroupID, err := parseWorkspacePlacementVerifyGroupID(args)
	if err != nil {
		return err
	}
	store, closeDB, err := openWorkspacePlacementVerifyStore(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	return writeWorkspacePlacementVerification(ctx, store, workspaceGroupID, os.Stdout)
}

func runWorkspacePlacementTopologyVerify(ctx context.Context, args []string) error {
	if err := parseWorkspacePlacementTopologyVerifyArgs(args); err != nil {
		return err
	}
	store, closeDB, err := openWorkspacePlacementVerifyStore(ctx)
	if err != nil {
		return err
	}
	defer closeDB()

	return writeWorkspacePlacementTopologyVerification(ctx, store, os.Stdout)
}

func parseWorkspacePlacementTopologyVerifyArgs(args []string) error {
	flagSet := flag.NewFlagSet("workspace-placement-topology-verify", flag.ContinueOnError)
	if err := flagSet.Parse(args); err != nil {
		return err
	}
	if flagSet.NArg() != 0 {
		return fmt.Errorf("unexpected workspace-placement-topology-verify arguments: %s", strings.Join(flagSet.Args(), " "))
	}
	return nil
}

func parseWorkspacePlacementVerifyGroupID(args []string) (string, error) {
	workspaceGroupID := strings.TrimSpace(os.Getenv("AGENTTEAM_WORKSPACE_GROUP_ID"))
	flagSet := flag.NewFlagSet("workspace-placement-verify", flag.ContinueOnError)
	flagSet.StringVar(&workspaceGroupID, "workspace-group-id", workspaceGroupID, "workspace group id to verify")
	if err := flagSet.Parse(args); err != nil {
		return "", err
	}
	if flagSet.NArg() != 0 {
		return "", fmt.Errorf("unexpected workspace-placement-verify arguments: %s", strings.Join(flagSet.Args(), " "))
	}
	workspaceGroupID = strings.TrimSpace(workspaceGroupID)
	if workspaceGroupID == "" {
		return "", fmt.Errorf("workspace-placement-verify requires --workspace-group-id or AGENTTEAM_WORKSPACE_GROUP_ID")
	}
	return workspaceGroupID, nil
}

func writeWorkspacePlacementVerification(ctx context.Context, verifier workspacePlacementVerifier, workspaceGroupID string, output io.Writer) error {
	if verifier == nil {
		return fmt.Errorf("workspace placement verifier is not configured")
	}
	if output == nil {
		return fmt.Errorf("workspace placement verify output is not configured")
	}
	result, err := verifier.VerifyWorkspacePlacements(ctx, workspaceGroupID)
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(output)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(result); err != nil {
		return fmt.Errorf("encode workspace placement verify result for group %s: %w", result.WorkspaceGroupID, err)
	}
	return validateWorkspacePlacementVerification(result)
}

func writeWorkspacePlacementTopologyVerification(ctx context.Context, verifier workspacePlacementTopologyVerifier, output io.Writer) error {
	if verifier == nil {
		return fmt.Errorf("workspace placement topology verifier is not configured")
	}
	if output == nil {
		return fmt.Errorf("workspace placement topology verify output is not configured")
	}
	result, err := verifier.VerifyWorkspacePlacementTopology(ctx)
	if err != nil {
		return err
	}
	encoder := json.NewEncoder(output)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(result); err != nil {
		return fmt.Errorf("encode workspace placement topology verify result: %w", err)
	}
	return validateWorkspacePlacementTopologyVerification(result)
}

func validateWorkspacePlacementVerification(result workspace.WorkspacePlacementVerification) error {
	var failures []string
	if result.MissingPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("missing_placement_count=%d", result.MissingPlacementCount))
	}
	if result.NonDefaultPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("non_default_placement_count=%d", result.NonDefaultPlacementCount))
	}
	if result.InactivePlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("inactive_placement_count=%d", result.InactivePlacementCount))
	}
	if result.MalformedPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("malformed_placement_count=%d", result.MalformedPlacementCount))
	}
	if len(failures) == 0 {
		return nil
	}
	return fmt.Errorf(
		"workspace placement verify failed for group %s: total_workspace_count=%d placement_count=%d %s",
		result.WorkspaceGroupID,
		result.TotalWorkspaceCount,
		result.PlacementCount,
		strings.Join(failures, " "),
	)
}

func validateWorkspacePlacementTopologyVerification(result workspace.WorkspacePlacementTopologyVerification) error {
	var failures []string
	if result.MissingPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("missing_placement_count=%d", result.MissingPlacementCount))
	}
	if result.UnknownGroupPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("unknown_group_placement_count=%d", result.UnknownGroupPlacementCount))
	}
	if result.InactiveGroupPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("inactive_group_placement_count=%d", result.InactiveGroupPlacementCount))
	}
	if result.InactivePlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("inactive_placement_count=%d", result.InactivePlacementCount))
	}
	if result.MalformedPlacementCount != 0 {
		failures = append(failures, fmt.Sprintf("malformed_placement_count=%d", result.MalformedPlacementCount))
	}
	if result.ActiveMigrationCount != 0 {
		failures = append(failures, fmt.Sprintf("active_migration_count=%d", result.ActiveMigrationCount))
	}
	if len(failures) == 0 {
		return nil
	}
	return fmt.Errorf(
		"workspace placement topology verify failed: total_workspace_count=%d workspace_group_count=%d placement_count=%d %s",
		result.TotalWorkspaceCount,
		result.WorkspaceGroupCount,
		result.PlacementCount,
		strings.Join(failures, " "),
	)
}

type workspaceMessageSearchTarget struct {
	workspaceID string
}

type workspaceMessageSearchStatus struct {
	WorkspaceID             string `json:"workspace_id"`
	BackfillStatus          string `json:"backfill_status"`
	BackfillCursorGlobalSeq int64  `json:"backfill_cursor_global_seq"`
	FinalizeStatus          string `json:"finalize_status"`
	ReadReady               bool   `json:"read_ready"`
}

func currentWorkspaceMessageSearchTarget() (workspaceMessageSearchTarget, error) {
	target := workspaceMessageSearchTarget{
		workspaceID: strings.TrimSpace(os.Getenv(workspaceMessageSearchWorkspaceIDEnv)),
	}
	if target.workspaceID == "" {
		return workspaceMessageSearchTarget{}, fmt.Errorf("%s is required", workspaceMessageSearchWorkspaceIDEnv)
	}
	return target, nil
}

func openWorkspacePlacementStore(ctx context.Context) (*workspace.Store, func(), error) {
	databaseURL, err := currentDatabaseURL()
	if err != nil {
		return nil, nil, err
	}
	connectTimeout, err := parseConnectTimeout()
	if err != nil {
		return nil, nil, err
	}
	admin := tidb.NewAdmin()
	db, err := admin.Open(ctx, databaseURL, connectTimeout)
	if err != nil {
		return nil, nil, fmt.Errorf("connect core main-path database: %w", err)
	}
	if err := admin.RunStartupPreflight(ctx, db); err != nil {
		_ = db.Close()
		return nil, nil, err
	}
	return workspace.New(coredb.WrapSQLDB(db), workspace.Options{}), func() { _ = db.Close() }, nil
}

func openWorkspacePlacementVerifyStore(ctx context.Context) (*workspace.Store, func(), error) {
	databaseURL, err := currentDatabaseURL()
	if err != nil {
		return nil, nil, err
	}
	connectTimeout, err := parseConnectTimeout()
	if err != nil {
		return nil, nil, err
	}
	admin := tidb.NewAdmin()
	db, err := admin.Open(ctx, databaseURL, connectTimeout)
	if err != nil {
		return nil, nil, fmt.Errorf("connect core main-path database for workspace placement verify: %w", err)
	}
	return workspace.New(coredb.WrapSQLDB(db), workspace.Options{}), func() { _ = db.Close() }, nil
}

func openWorkspaceMessageSearchRepositories(ctx context.Context) (message.ScopeRepositories, coredb.QueryExecer, func(), error) {
	databaseURL, err := currentDatabaseURL()
	if err != nil {
		return message.ScopeRepositories{}, nil, nil, err
	}
	connectTimeout, err := parseConnectTimeout()
	if err != nil {
		return message.ScopeRepositories{}, nil, nil, err
	}
	admin := tidb.NewAdmin()
	db, err := admin.Open(ctx, databaseURL, connectTimeout)
	if err != nil {
		return message.ScopeRepositories{}, nil, nil, fmt.Errorf("connect core main-path database: %w", err)
	}
	if err := admin.RunStartupPreflight(ctx, db); err != nil {
		_ = db.Close()
		return message.ScopeRepositories{}, nil, nil, err
	}
	wrapped := coredb.WrapSQLDB(db)
	return message.NewScopeRepositories(wrapped), wrapped, func() { _ = db.Close() }, nil
}

func currentDatabaseURL() (string, error) {
	databaseURL, _, ok := envcompat.Lookup("AGENTTEAM_DATABASE_DSN", "AGENTTEAM_CORE_STORAGE_DSN")
	if !ok {
		return "", fmt.Errorf("AGENTTEAM_DATABASE_DSN is required")
	}
	return databaseURL, nil
}

func parseConnectTimeout() (time.Duration, error) {
	raw := strings.TrimSpace(os.Getenv("AGENTTEAM_DATABASE_CONNECT_TIMEOUT"))
	if raw == "" {
		return defaultConnectTimeout, nil
	}
	duration, err := time.ParseDuration(raw)
	if err != nil {
		return 0, fmt.Errorf("parse AGENTTEAM_DATABASE_CONNECT_TIMEOUT: %w", err)
	}
	if duration <= 0 {
		return 0, fmt.Errorf("AGENTTEAM_DATABASE_CONNECT_TIMEOUT must be positive")
	}
	return duration, nil
}

func parseMessageSearchBackfillBatchSize() (int, error) {
	raw := strings.TrimSpace(os.Getenv("AGENTTEAM_MESSAGE_SEARCH_BACKFILL_BATCH_SIZE"))
	if raw == "" {
		return defaultMessageSearchBackfillBatchSize, nil
	}
	size, err := strconv.Atoi(raw)
	if err != nil {
		return 0, fmt.Errorf("parse AGENTTEAM_MESSAGE_SEARCH_BACKFILL_BATCH_SIZE: %w", err)
	}
	if size <= 0 {
		return 0, fmt.Errorf("AGENTTEAM_MESSAGE_SEARCH_BACKFILL_BATCH_SIZE must be positive")
	}
	return size, nil
}

func printUsage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [workspace-org-membership-backfill|workspace-org-membership-verify|workspace-placement-backfill|workspace-placement-repair|workspace-placement-verify|workspace-placement-topology-verify|channel-attachment-projection-backfill|workspace-message-search-backfill|workspace-message-search-finalize|workspace-message-search-promote-ready|workspace-message-search-status|workspace-message-search-ensure-ready]\n", os.Args[0])
	fmt.Fprintln(os.Stderr)
	fmt.Fprintln(os.Stderr, "Commands:")
	fmt.Fprintln(os.Stderr, "  workspace-org-membership-backfill  repair missing owning-org memberships for active workspace human members")
	fmt.Fprintln(os.Stderr, "  workspace-org-membership-verify    read-only cutover gate for workspace owning-org membership")
	fmt.Fprintln(os.Stderr, "  workspace-placement-backfill       create default workspace_placements rows and verify missing_placement_count=0")
	fmt.Fprintln(os.Stderr, "  workspace-placement-repair         create one explicitly targeted missing workspace_placements row")
	fmt.Fprintln(os.Stderr, "  workspace-placement-verify         read-only production cutover gate for workspace_placements")
	fmt.Fprintln(os.Stderr, "  workspace-placement-topology-verify read-only multi-group topology gate for workspace_placements")
	fmt.Fprintln(os.Stderr, "  channel-attachment-projection-backfill idempotent backfill/catch-up for channel_attachment_projection (run after cutover)")
	fmt.Fprintln(os.Stderr, "  workspace-message-search-backfill      backfill workspace-global message search projection rows for one workspace")
	fmt.Fprintln(os.Stderr, "  workspace-message-search-finalize      finalize one workspace-global message search projection after backfill completes")
	fmt.Fprintln(os.Stderr, "  workspace-message-search-promote-ready mark one workspace-global message search projection read-ready")
	fmt.Fprintln(os.Stderr, "  workspace-message-search-status        print one workspace-global message search projection state as machine-readable JSON")
	fmt.Fprintln(os.Stderr, "  workspace-message-search-ensure-ready  ensure every workspace-global message search projection is ready and print a final JSON summary")
	fmt.Fprintln(os.Stderr)
	fmt.Fprintf(os.Stderr, "Set %s to target the workspace-global readiness row for low-level repair commands.\n", workspaceMessageSearchWorkspaceIDEnv)
}
