package k8s

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

	"deployment-manager/internal/events"
	"deployment-manager/internal/model"
)

type KubectlClient struct {
	namespace string
}

func NewKubectlClient(namespace string) *KubectlClient {
	if namespace == "" {
		namespace = "default"
	}
	return &KubectlClient{namespace: namespace}
}

func (k *KubectlClient) ApplyManifest(ctx context.Context, eventChan chan<- *events.Event, repoID int64, manifest string) error {
	cmd := "kubectl"
	args := []string{
		"apply",
		"-f", "-",
		"--namespace", k.namespace,
	}

	// Use kubectl with stdin for the manifest
	kubectlCmd := exec.CommandContext(ctx, cmd, args...)
	kubectlCmd.Stdin = strings.NewReader(manifest)

	stdout, _ := kubectlCmd.StdoutPipe()
	stderr, _ := kubectlCmd.StderrPipe()

	if err := kubectlCmd.Start(); err != nil {
		return fmt.Errorf("failed to start kubectl apply: %w", err)
	}

	// Stream output
	go streamOutput(stdout, eventChan, repoID, "kubectl")
	go streamOutput(stderr, eventChan, repoID, "kubectl")

	return kubectlCmd.Wait()
}

func (k *KubectlClient) DeleteResources(ctx context.Context, eventChan chan<- *events.Event, repoID int64, appName string) error {
	commands := [][]string{
		{"delete", "deployment", appName, "--namespace", k.namespace},
		{"delete", "service", appName, "--namespace", k.namespace},
		{"delete", "configmap", appName, "--namespace", k.namespace},
	}

	for _, args := range commands {
		cmd := "kubectl"
		if err := runKubectlCommand(ctx, eventChan, repoID, cmd, args...); err != nil {
			// Don't fail if resources don't exist
			if !strings.Contains(err.Error(), "not found") {
				return fmt.Errorf("failed to delete resource with args %v: %w", args, err)
			}
		}
	}

	return nil
}

func (k *KubectlClient) ScaleDeployment(ctx context.Context, eventChan chan<- *events.Event, repoID int64, appName string, replicas int) error {
	cmd := "kubectl"
	args := []string{
		"scale",
		"deployment", appName,
		"--replicas", fmt.Sprintf("%d", replicas),
		"--namespace", k.namespace,
	}

	return runKubectlCommand(ctx, eventChan, repoID, cmd, args...)
}

func (k *KubectlClient) GetDeploymentStatus(ctx context.Context, appName string) (string, error) {
	cmd := "kubectl"
	args := []string{
		"get", "deployment", appName,
		"--namespace", k.namespace,
		"-o", "jsonpath='{.status.readyReplicas}'",
	}

	output, err := exec.CommandContext(ctx, cmd, args...).Output()
	if err != nil {
		return "", fmt.Errorf("failed to get deployment status: %w", err)
	}

	return strings.Trim(string(output), "'"), nil
}

func runKubectlCommand(ctx context.Context, eventChan chan<- *events.Event, repoID int64, cmd string, args ...string) error {
	kubectlCmd := exec.CommandContext(ctx, cmd, args...)
	stdout, _ := kubectlCmd.StdoutPipe()
	stderr, _ := kubectlCmd.StderrPipe()

	if err := kubectlCmd.Start(); err != nil {
		return fmt.Errorf("failed to start kubectl command: %w", err)
	}

	go streamOutput(stdout, eventChan, repoID, "kubectl")
	go streamOutput(stderr, eventChan, repoID, "kubectl")

	return kubectlCmd.Wait()
}

func streamOutput(r interface{ Read([]byte) (int, error) }, eventChan chan<- *events.Event, repoID int64, source string) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		event := events.NewLogEvent(repoID, scanner.Text())
		event.Data["source"] = source
		eventChan <- event
	}
}

func GetAppName(repo *model.Repo) string {
	// Generate a consistent app name based on repo
	parts := strings.Split(strings.TrimSuffix(repo.RepoURL, ".git"), "/")
	repoName := parts[len(parts)-1]
	return fmt.Sprintf("repo-%d-%s", repo.ID, strings.ToLower(repoName))
}
