package cmd

import (
	"context"
	"errors"
	"fmt"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/tidbcloud/controlplane9/internal/byoc"
	"github.com/tidbcloud/controlplane9/internal/byoc/accountsetup"
	"github.com/tidbcloud/controlplane9/internal/host"
)

type byocRemoteKubectlRunner struct {
	selected      *host.Session
	localRepoRoot string

	remoteRepoMu   sync.Mutex
	remoteRepoRoot string
}

func newBYOCAWSDriver(
	selected *host.Session,
	localRepoRoot string,
	bundle *accountsetup.Bundle,
	closure *byoc.ImageClosure,
) (*byoc.AWSDriver, error) {
	if selected == nil || selected.Remote == nil {
		return nil, errors.New("BYOC remote Kubernetes runner requires persisted remote host metadata")
	}
	if strings.TrimSpace(selected.Remote.RemoteKubeconfigPath) == "" {
		return nil, errors.New("BYOC remote Kubernetes runner requires a remote kubeconfig path")
	}
	if _, err := host.SSHBaseArgs(selected); err != nil {
		return nil, fmt.Errorf("prepare BYOC remote Kubernetes runner: %w", err)
	}
	localRepoRoot = strings.TrimSpace(localRepoRoot)
	if localRepoRoot == "" {
		return nil, errors.New("BYOC remote Kubernetes runner requires a local repository root")
	}
	localRepoRoot, err := filepath.Abs(localRepoRoot)
	if err != nil {
		return nil, fmt.Errorf("resolve local BYOC repository root: %w", err)
	}
	return byoc.NewAWSDriver(byoc.AWSDriverOptions{
		Bundle:           bundle,
		ImageClosure:     closure,
		KubernetesRunner: &byocRemoteKubectlRunner{selected: selected, localRepoRoot: localRepoRoot},
		ImageRunner:      &byocRemoteImageRunner{selected: selected},
	})
}

// Run keeps the customer-authorized CLI on the developer machine while moving
// Kubernetes API traffic and manifest ingestion onto the persisted remote EC2
// host. The remote host already owns the k3d runtime and its kubeconfig.
//
//nolint:gocritic // Runner receives an immutable command contract by value.
func (r *byocRemoteKubectlRunner) Run(ctx context.Context, command byoc.Command) ([]byte, error) {
	if command.Name != "kubectl" {
		return nil, fmt.Errorf("BYOC remote Kubernetes runner refuses command %q", command.Name)
	}
	if command.Dir != "" || len(command.Env) != 0 {
		return nil, errors.New("BYOC remote Kubernetes runner refuses local directory or environment overrides")
	}
	if r.selected == nil || r.selected.Remote == nil {
		return nil, errors.New("BYOC remote Kubernetes runner is missing remote host metadata")
	}
	args := append([]string(nil), command.Args...)
	rewritten := false
	for index := 0; index < len(args); index++ {
		if args[index] != "--kubeconfig" {
			continue
		}
		if index+1 >= len(args) {
			return nil, errors.New("BYOC remote kubectl command has no kubeconfig value")
		}
		args[index+1] = r.selected.Remote.RemoteKubeconfigPath
		rewritten = true
		index++
	}
	if !rewritten {
		return nil, errors.New("BYOC remote kubectl command has no explicit kubeconfig")
	}
	for index, arg := range args {
		relative, err := filepath.Rel(r.localRepoRoot, arg)
		if err != nil || relative == "." || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
			continue
		}
		remoteRepoRoot, err := r.resolveRemoteRepoRoot(ctx)
		if err != nil {
			return nil, err
		}
		args[index] = path.Join(remoteRepoRoot, filepath.ToSlash(relative))
	}
	argv := append([]string{"kubectl"}, args...)
	return host.RunSSH(ctx, r.selected, command.Stdin, argv...)
}

func (r *byocRemoteKubectlRunner) resolveRemoteRepoRoot(ctx context.Context) (string, error) {
	r.remoteRepoMu.Lock()
	defer r.remoteRepoMu.Unlock()
	if r.remoteRepoRoot != "" {
		return r.remoteRepoRoot, nil
	}
	sourceSyncDestination := ""
	if r.selected.Execution != nil {
		sourceSyncDestination = strings.TrimSpace(r.selected.Execution.SourceSyncDestination)
	}
	if sourceSyncDestination == "" {
		sourceSyncDestination = strings.TrimSpace(r.selected.Remote.RemoteStateDir)
	}
	if sourceSyncDestination == "" {
		return "", errors.New("BYOC remote Kubernetes runner requires a remote source-sync destination")
	}
	sourceRoot := path.Join(sourceSyncDestination, "premium-build", "source")
	output, err := host.RunSSH(
		ctx,
		r.selected,
		nil,
		"find",
		sourceRoot,
		"-mindepth",
		"2",
		"-maxdepth",
		"2",
		"-type",
		"f",
		"-name",
		".cp9-source-ready",
		"-print",
	)
	if err != nil {
		return "", fmt.Errorf("resolve synced remote BYOC repository: %w", err)
	}
	markers := strings.Fields(string(output))
	if len(markers) != 1 {
		return "", fmt.Errorf("resolve synced remote BYOC repository: found %d ready source roots, want exactly one", len(markers))
	}
	r.remoteRepoRoot = path.Dir(markers[0])
	return r.remoteRepoRoot, nil
}
