import pytest
import subprocess
import os
import time

# Define constants for resource names and locations
NAMESPACE = "rlm-test-ns"
DEPLOYMENT_NAME = "rlm-test-deployment"
SERVICE_NAME = "rlm-test-service"
INGRESS_NAME = "rlm-test-ingress"
POD_LABEL = "app=rlm-test"

# Helper functions to interact with Kubernetes using kubectl
def kubectl_apply(yaml_file, namespace="default"):
    """Applies a Kubernetes YAML file using kubectl."""
    try:
        subprocess.run(
            ["kubectl", "apply", "-f", yaml_file, "-n", namespace],
            check=True,
            capture_output=True,
            text=True,
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error applying {yaml_file}: {e.stderr}")
        return False

def kubectl_delete(yaml_file, namespace="default"):
    """Deletes a Kubernetes YAML file using kubectl."""
    try:
        subprocess.run(
            ["kubectl", "delete", "-f", yaml_file, "-n", namespace],
            check=True,
            capture_output=True,
            text=True,
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error deleting {yaml_file}: {e.stderr}")
        return False

def kubectl_get(resource, name=None, namespace="default", output="yaml"):
    """Gets a Kubernetes resource using kubectl."""
    command = ["kubectl", "get", resource, "-n", namespace, "-o", output]
    if name:
        command.insert(2, name)
    try:
        result = subprocess.run(
            command,
            check=True,
            capture_output=True,
            text=True,
        )
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"Error getting {resource}: {e.stderr}")
        return None

def kubectl_wait_for_deployment(deployment_name, namespace="default", timeout=60):
    """Waits for a Kubernetes deployment to be ready."""
    try:
        subprocess.run(
            [
                "kubectl",
                "wait",
                "--for=condition=Available=True",
                f"deployment/{deployment_name}",
                f"-n={namespace}",
                f"--timeout={timeout}s",
            ],
            check=True,
            capture_output=True,
            text=True,
        )
        return True
    except subprocess.CalledProcessError as e:
        print(
            f"Error waiting for deployment {deployment_name}: {e.stderr}"
        )  # Print the error message
        return False

def kubectl_create_namespace(namespace):
    """Creates a Kubernetes namespace."""
    try:
        subprocess.run(
            ["kubectl", "create", "namespace", namespace],
            check=True,
            capture_output=True,
            text=True,
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error creating namespace {namespace}: {e.stderr}")
        return False

def kubectl_delete_namespace(namespace):
    """Deletes a Kubernetes namespace."""
    try:
        subprocess.run(
            ["kubectl", "delete", "namespace", namespace],
            check=True,
            capture_output=True,
            text=True,
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"Error deleting namespace {namespace}: {e.stderr}")
        return False

def kubectl_get_pods(namespace, label_selector=None):
    """Gets pods in a namespace, optionally filtered by a label selector."""
    command = ["kubectl", "get", "pods", "-n", namespace, "-o", "name"]
    if label_selector:
        command.extend(["-l", label_selector])
    try:
        result = subprocess.run(command, check=True, capture_output=True, text=True)
        return result.stdout.strip().split("\n")
    except subprocess.CalledProcessError as e:
        print(f"Error getting pods: {e.stderr}")
        return []

# Fixture to create and delete a namespace for each test
@pytest.fixture(scope="module")
def setup_namespace():
    """Creates and deletes a test namespace."""
    if not kubectl_create_namespace(NAMESPACE):
        pytest.fail(f"Failed to create namespace {NAMESPACE}")
    yield
    if not kubectl_delete_namespace(NAMESPACE):
        pytest.fail(f"Failed to delete namespace {NAMESPACE}")

# Test cases
def test_namespace_creation_deletion():
    """Tests the creation and deletion of a Kubernetes namespace."""
    test_namespace = "test-ns-temp"  # Use a temporary namespace name
    assert kubectl_create_namespace(test_namespace), f"Failed to create namespace {test_namespace}"
    assert kubectl_get("namespace", test_namespace), f"Namespace {test_namespace} not found after creation"
    assert kubectl_delete_namespace(test_namespace), f"Failed to delete namespace {test_namespace}"
    # Verify deletion by attempting to get the namespace (expecting an error or empty result)
    assert kubectl_get("namespace", test_namespace) is None, f"Namespace {test_namespace} still exists after deletion"


def test_deployment_creation_deletion(setup_namespace):
    """Tests the creation and deletion of a Kubernetes deployment."""
    deployment_file = "manifests/deployment.yaml" # Example path, adjust accordingly
    assert kubectl_apply(deployment_file, NAMESPACE), f"Failed to apply deployment from {deployment_file}"
    assert kubectl_wait_for_deployment(DEPLOYMENT_NAME, NAMESPACE), f"Deployment {DEPLOYMENT_NAME} not ready within timeout"
    assert kubectl_delete(deployment_file, NAMESPACE), f"Failed to delete deployment from {deployment_file}"

def test_service_creation_deletion(setup_namespace):
    """Tests the creation and deletion of a Kubernetes service."""
    service_file = "manifests/service.yaml" # Example path, adjust accordingly
    assert kubectl_apply(service_file, NAMESPACE), f"Failed to apply service from {service_file}"
    assert kubectl_get("service", SERVICE_NAME, NAMESPACE), f"Service {SERVICE_NAME} not found after creation"
    assert kubectl_delete(service_file, NAMESPACE), f"Failed to delete service from {service_file}"

def test_ingress_creation_deletion(setup_namespace):
    """Tests the creation and deletion of a Kubernetes ingress."""
    ingress_file = "manifests/ingress.yaml" # Example path, adjust accordingly
    assert kubectl_apply(ingress_file, NAMESPACE), f"Failed to apply ingress from {ingress_file}"
    assert kubectl_get("ingress", INGRESS_NAME, NAMESPACE), f"Ingress {INGRESS_NAME} not found after creation"
    assert kubectl_delete(ingress_file, NAMESPACE), f"Failed to delete ingress from {ingress_file}"

def test_pod_listing_by_label(setup_namespace):
    """Tests listing pods by label."""
    deployment_file = "manifests/deployment.yaml" # Example path, adjust accordingly
    kubectl_apply(deployment_file, NAMESPACE)
    kubectl_wait_for_deployment(DEPLOYMENT_NAME, NAMESPACE)  # Ensure deployment is ready
    pods = kubectl_get_pods(NAMESPACE, POD_LABEL)
    assert len(pods) > 0, f"No pods found with label {POD_LABEL}"
    kubectl_delete(deployment_file, NAMESPACE) # Cleanup the deployment

# Example manifest files (replace with your actual manifests)
# Create dummy manifest files for testing
if not os.path.exists("manifests"):
    os.makedirs("manifests")

with open("manifests/deployment.yaml", "w") as f:
    f.write(f"""
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {DEPLOYMENT_NAME}
  namespace: {NAMESPACE}
  labels:
    app: {DEPLOYMENT_NAME}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: {DEPLOYMENT_NAME}
  template:
    metadata:
      labels:
        app: {DEPLOYMENT_NAME}
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
""")

with open("manifests/service.yaml", "w") as f:
    f.write(f"""
apiVersion: v1
kind: Service
metadata:
  name: {SERVICE_NAME}
  namespace: {NAMESPACE}
spec:
  selector:
    app: {DEPLOYMENT_NAME}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
""")

with open("manifests/ingress.yaml", "w") as f:
    f.write(f"""
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {INGRESS_NAME}
  namespace: {NAMESPACE}
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: {SERVICE_NAME}
            port:
              number: 80
""")