package operator

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

	v1alpha1 "github.com/pfeifferj/container-mom-go/pkg/apis/containermom/v1alpha1"
	ctrl "sigs.k8s.io/controller-runtime"
)

// CreateOrUpdateRoute creates or updates an OpenShift route with proper annotations
func CreateOrUpdateRoute(ctx context.Context, deployment *v1alpha1.ContainerMomDeployment,
	routeName, serviceName, hostname, namespace string) error {
	log := ctrl.LoggerFrom(ctx)
	log.Info("Creating or updating route", "route", routeName, "host", hostname, "namespace", namespace)

	// First verify the service exists
	verifyServiceCmd := exec.Command("oc", "get", "service", serviceName, "-n", namespace)
	serviceOutput, serviceErr := verifyServiceCmd.CombinedOutput()
	if serviceErr != nil {
		log.Error(serviceErr, "Service does not exist, cannot create route",
			"service", serviceName,
			"namespace", namespace,
			"output", string(serviceOutput))

		// Wait briefly and retry once for the service
		log.Info("Waiting briefly for service to be ready before retry")
		time.Sleep(3 * time.Second)

		retryServiceCmd := exec.Command("oc", "get", "service", serviceName, "-n", namespace)
		retryOutput, retryErr := retryServiceCmd.CombinedOutput()
		if retryErr != nil {
			return fmt.Errorf("service %s/%s not found, cannot create route: %w, output: %s",
				namespace, serviceName, retryErr, string(retryOutput))
		}
	}

	// Check if route exists
	checkCmd := exec.Command("oc", "get", "route", routeName, "-n", namespace)
	output, err := checkCmd.CombinedOutput()
	log.Info("Route check output", "output", string(output), "error", err != nil)

	if err != nil {
		// Route doesn't exist, create it
		log.Info("Route doesn't exist, creating it",
			"namespace", namespace,
			"name", routeName,
			"hostname", hostname,
			"service", serviceName)

		// Create route YAML
		routeYaml := fmt.Sprintf(`
apiVersion: route.openshift.io/v1
kind: Route
metadata:
  name: %s
  namespace: %s
  labels:
    app: %s
    app.kubernetes.io/managed-by: container-mom
spec:
  host: %s
  to:
    kind: Service
    name: %s
    weight: 100
  port:
    targetPort: http
  tls:
    termination: edge
    insecureEdgeTerminationPolicy: Redirect
`, routeName, namespace, deployment.Name, hostname, serviceName)

		// Use oc apply with route YAML
		applyCmd := exec.Command("oc", "apply", "-f", "-")
		applyCmd.Stdin = strings.NewReader(routeYaml)
		applyOutput, applyErr := applyCmd.CombinedOutput()

		if applyErr != nil {
			log.Error(applyErr, "Failed to create route with yaml",
				"namespace", namespace,
				"name", routeName,
				"output", string(applyOutput))
			return fmt.Errorf("failed to create route: %w, output: %s", applyErr, string(applyOutput))
		}

		log.Info("Created route successfully",
			"namespace", namespace,
			"name", routeName,
			"hostname", hostname)
	} else {
		// Route exists, update it if needed
		log.Info("Route exists, checking if update needed",
			"namespace", namespace,
			"name", routeName,
			"hostname", hostname)

		// Get current hostname
		getCmd := exec.Command("oc", "get", "route", routeName, "-n", namespace, "-o", "jsonpath={.spec.host}")
		getOutput, getErr := getCmd.CombinedOutput()

		if getErr != nil {
			log.Error(getErr, "Failed to get route hostname",
				"namespace", namespace,
				"name", routeName,
				"output", string(getOutput))
			return fmt.Errorf("failed to get route hostname: %w, output: %s", getErr, string(getOutput))
		}

		currentHostname := strings.TrimSpace(string(getOutput))

		// Update if hostname differs
		if currentHostname != hostname {
			log.Info("Route hostname differs, updating",
				"namespace", namespace,
				"name", routeName,
				"currentHostname", currentHostname,
				"desiredHostname", hostname)

			patchData := fmt.Sprintf(`{"spec":{"host":"%s"}}`, hostname)
			patchCmd := exec.Command("oc", "patch", "route", routeName,
				"-n", namespace,
				"--type=merge",
				"-p", patchData)

			patchOutput, patchErr := patchCmd.CombinedOutput()
			if patchErr != nil {
				log.Error(patchErr, "Failed to update route hostname",
					"namespace", namespace,
					"name", routeName,
					"output", string(patchOutput))
				return fmt.Errorf("failed to update route hostname: %w, output: %s", patchErr, string(patchOutput))
			}

			log.Info("Updated route hostname successfully",
				"namespace", namespace,
				"name", routeName,
				"hostname", hostname)
		} else {
			log.Info("Route hostname already correct, no update needed",
				"namespace", namespace,
				"name", routeName,
				"hostname", hostname)
		}

		// Ensure route points to the correct service
		getServiceCmd := exec.Command("oc", "get", "route", routeName, "-n", namespace, "-o", "jsonpath={.spec.to.name}")
		getServiceOutput, getServiceErr := getServiceCmd.CombinedOutput()

		if getServiceErr != nil {
			log.Error(getServiceErr, "Failed to get route service",
				"namespace", namespace,
				"name", routeName,
				"output", string(getServiceOutput))
		} else {
			currentService := strings.TrimSpace(string(getServiceOutput))

			if currentService != serviceName {
				log.Info("Route service differs, updating",
					"namespace", namespace,
					"name", routeName,
					"currentService", currentService,
					"desiredService", serviceName)

				patchData := fmt.Sprintf(`{"spec":{"to":{"name":"%s"}}}`, serviceName)
				patchCmd := exec.Command("oc", "patch", "route", routeName,
					"-n", namespace,
					"--type=merge",
					"-p", patchData)

				patchOutput, patchErr := patchCmd.CombinedOutput()
				if patchErr != nil {
					log.Error(patchErr, "Failed to update route service",
						"namespace", namespace,
						"name", routeName,
						"output", string(patchOutput))
				} else {
					log.Info("Updated route service successfully",
						"namespace", namespace,
						"name", routeName,
						"service", serviceName)
				}
			}
		}
	}

	// Verify the route was created/updated properly
	verifyCmd := exec.Command("oc", "get", "route", routeName, "-n", namespace, "-o", "jsonpath={.spec.host}")
	verifyOutput, verifyErr := verifyCmd.CombinedOutput()
	if verifyErr != nil {
		log.Error(verifyErr, "Failed to verify route creation",
			"namespace", namespace,
			"name", routeName,
			"output", string(verifyOutput))
		return fmt.Errorf("failed to verify route creation: %w, output: %s", verifyErr, string(verifyOutput))
	}

	actualHostname := strings.TrimSpace(string(verifyOutput))
	log.Info("Route verification successful",
		"namespace", namespace,
		"name", routeName,
		"expectedHostname", hostname,
		"actualHostname", actualHostname,
		"match", actualHostname == hostname)

	return nil
}

// Helper function to build a hostname from domain settings
func BuildRouteHostname(domain v1alpha1.DomainSpec) string {
	if domain.UseWildcard {
		return "*." + domain.DomainName
	}
	if domain.Prefix != "" {
		return domain.Prefix + "." + domain.DomainName
	}
	return domain.DomainName
}

// Other route-related functions...
