package crd

import (
	"context"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/tidbcloud/controlplane9/internal/verbose"
)

// Dumper extracts CRD definitions and custom resource instances from a staging cluster.
type Dumper struct {
	// Kubeconfig is the path to the staging kubeconfig file.
	Kubeconfig string
	// Context is the kubectl context for the staging cluster.
	Context string

	// CommandRunner allows injecting a mock for testing.
	CommandRunner func(cmd *exec.Cmd) ([]byte, error)
}

// ListCRDs returns the names of all CRDs in the staging cluster whose API group
// matches one of the given groups. If groups is empty, all CRDs are returned.
func (d *Dumper) ListCRDs(ctx context.Context, groups []string) ([]string, error) {
	args := d.baseArgs("get", "crd", "-o", "jsonpath={.items[*].metadata.name}")
	cmd := exec.CommandContext(ctx, "kubectl", args...)

	output, err := d.runCommand(cmd)
	if err != nil {
		return nil, fmt.Errorf("list CRDs from staging: %w", err)
	}

	all := strings.Fields(strings.TrimSpace(string(output)))
	if len(groups) == 0 {
		return all, nil
	}

	var filtered []string
	for _, name := range all {
		for _, g := range groups {
			if strings.HasSuffix(name, "."+g) {
				filtered = append(filtered, name)
				break
			}
		}
	}
	return filtered, nil
}

// DumpCRD fetches a single CRD definition YAML from the staging cluster.
func (d *Dumper) DumpCRD(ctx context.Context, crdName string) ([]byte, error) {
	args := d.baseArgs("get", "crd", crdName, "-o", "yaml")
	cmd := exec.CommandContext(ctx, "kubectl", args...)

	log.Printf("crd: dumping CRD %s from staging", crdName)

	output, err := d.runCommand(cmd)
	if err != nil {
		return nil, fmt.Errorf("dump CRD %s: %w", crdName, err)
	}
	return output, nil
}

// DumpCRs fetches custom resource instances of a given resource type from the staging cluster.
// If namespace is empty, fetches across all namespaces.
func (d *Dumper) DumpCRs(ctx context.Context, resource, namespace string) ([]byte, error) {
	args := d.baseArgs("get", resource, "-o", "yaml")
	if namespace != "" {
		args = append(args, "-n", namespace)
	} else {
		args = append(args, "--all-namespaces")
	}

	cmd := exec.CommandContext(ctx, "kubectl", args...)

	log.Printf("crd: dumping CRs for %s from staging", resource)

	output, err := d.runCommand(cmd)
	if err != nil {
		return nil, fmt.Errorf("dump CRs for %s: %w", resource, err)
	}
	return output, nil
}

func (d *Dumper) baseArgs(args ...string) []string {
	var base []string
	if d.Kubeconfig != "" {
		base = append(base, "--kubeconfig", d.Kubeconfig)
	}
	if d.Context != "" {
		base = append(base, "--context", d.Context)
	}
	return append(base, args...)
}

func (d *Dumper) runCommand(cmd *exec.Cmd) ([]byte, error) {
	if d.CommandRunner != nil {
		return d.CommandRunner(cmd)
	}
	return verbose.RunCommand(cmd)
}
