#!/usr/bin/env herodo
// Pre-flight check: Ensure Kubernetes cluster is accessible
try {
    if !run("kubectl cluster-info").execute().success {
        print("SKIPPED: Kubernetes cluster is not accessible");
        exit(0);
    }
} catch(e) {
    print("SKIPPED: Kubernetes cluster is not accessible: " + e);
    exit(0);
}

//! Kubernetes Basic Operations
//! Demonstrates creating and managing Kubernetes resources using a fluent API
//! All resource names are automatically normalized using name_fix_kubernetes for compliance
//! You use a pod or deployment, pod is a one-off thing


let k = kubernetes();
print("=== Kubernetes State Before Reset ===");
print(k.md()); // show nicely formatted markdown with namespaces

print("\n=== Resetting Kubernetes ===");
k.reset();
print("After reset:");
print(k.md()); // should be empty after reset
print(k.ns_list());
k.ns_delete("default_app");


print("\n=== Creating namespace and resources in: default-app ===");
let ns = k.namespace("default-app"); // create or get namespace
ns.reset(); // reset all resources in namespace
print("Pods list (after reset): " + ns.pods_list());
print("Namespace markdown:");
print(ns.md()); // markdown representation of namespace


print("\n=== Creating ConfigMap: app-configmap ===");
ns.configmap("app-configmap")
    .set("app_secret", "value")
    .set("database_url", "postgres://localhost/mydb")
    .apply();

let cm_value = ns.configmap("app-configmap").get("app_secret");
print("ConfigMap created with app-secret = " + cm_value);


print("\n=== Creating Secret: app-secret ===");
ns.secret("app-secret")
    .set("username", "admin")
    .set("password", "secret123")
    .apply(); //there always need to be a apply() at end

let secret_value = ns.secret("app-secret").get("password");
print("Secret created with password = " + secret_value);

print("\n=== Creating Pod: app-pod ===");
let pod = ns.pod("app-pod")
    .image("nginx:latest")
    .env("ENV","production")
    .env("LOG_LEVEL","info")
    .env_from_configmap("app-configmap") // all configmap values become env vars
    .env_from_secret("app-secret") // all secret values become env vars
    .label("app", "default-app") //always key value pair
    .label("component", "web")
    .port(80)
    .memory(128, 512) //requested/limit always in MB
    .cpu(1000, 2000) //requested/limit always in CPU milli cores, 1000 means 1 core
    .readiness_http("/health", 80) //if not ready, then pod not in service, is not restarted, traffic is not routed to it
    .liveness_http("/health", 80).initial_delay(10).period(5).failure_threshold(3) //container killed & restarted if not answering
    .readiness_tcp(8080)
    .liveness_tcp(8080).initial_delay(10).period(5).failure_threshold(3) //the delay,period,failure_threshold is optional
    .restart("always") //enum: onfail,never,always
    .apply();

print("Pod 'app-pod' created successfully");


//TODO checks: memory between 100MB and 2000GB
//TODO checks: cpu betweeen 100 and 1000000

print("\n=== Creating ClusterIP Service: web-service ===");
// service for clusterip, traffic loadbalanced to pods
// Service gets an internal virtual IP
// Reachable only from inside the cluster
// DNS works only inside the cluster
// No node exposes a port
let service = ns.service("web-service") //clusterip is default no need to specify
    .selector("app", "default-app") //can find more than 1
    .expose(80).to_container(8080) //if to not specified then is same as port
    .expose(443).to_container(8443)
    .apply();

print("ClusterIP Service 'web-service' created successfully");


//service for nodeport, port is exposed on each node and then get's routed to selected pods
// Exposes ports on every node range (30000–32767)
print("\n=== Creating NodePort Service: web-service-nodeport ===");
let service = ns.service("web-service-nodeport").nodeport() //specifies is nodeport
    .selector("app", "default-app") //can find more than 1
    .label("myservice", "web1")     // labels the Service itself
    .expose(80).to_container(8080) //if to_container not specified then is same as expose, dns: web-service:80 → Pod:8080
    .expose(443).to_container(8443).via_node("30001") //if via_node not specified then is random given port between 30000-32767
    .apply();

print("NodePort Service 'web-service-nodeport' created successfully");

//TODO: when implementing this code in rust need to make sure we cannot expose ports 2x
//TODO: also check via_node is between 30000-32767, note kubernetes will give error if duplicate
//TODO: also check that there is a selector
//TODO: do our own check on kuberentes cluster that it selects at least 1 pod, otherwise give error (based on labels)


//special case for DNS, only useful in the cluster
print("\n=== Creating Headless Service: dns-service ===");
let service = ns.service("dns-service").headless()
    .label("app", "default-app") //can find more than 1
    .port(53).to(53).udp()
    .type("headless") //if more than one Pod matches the selector → DNS returns ALL Pod IPs (only relevant internal to cluster)
    .apply();

print("Headless Service 'dns-service' created successfully");


print("\n=== Resource Summary ===");
print("Pods: " + ns.pods_list());
print("Services: " + ns.services_list());
print("Secrets: " + ns.secrets_list());
print("ConfigMaps: " + ns.configmaps_list());

print("\n✅ Kubernetes example completed successfully!");
