#!/bin/bash
# Install cert-manager for Citadel
# Provides automated TLS certificate provisioning and rotation
#
# What this installs:
#   - cert-manager controller, webhook, and cainjector (HA mode)
#   - ClusterIssuers: letsencrypt-staging, letsencrypt-prod, internal-ca
#   - Internal root CA certificate for service-to-service mTLS
#
# Prerequisites:
#   - K3s running (scripts/01-install-k3s.sh)
#   - Helm installed (bundled with Cilium install or standalone)
#   - kubectl configured

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "${SCRIPT_DIR}")"

# shellcheck source=scripts/common.sh
source "${SCRIPT_DIR}/common.sh"

CERT_MANAGER_VERSION="v1.14.4"
CERT_MANAGER_NAMESPACE="cert-manager"

log_info "Installing cert-manager ${CERT_MANAGER_VERSION} for Citadel..."

# ----- Prerequisites -----
require_kubectl || exit 1
require_helm || exit 1

# ----- Step 1: Add Jetstack Helm repository -----
log_info "Step 1: Adding Jetstack Helm repository..."

if helm repo list 2>/dev/null | grep -q jetstack; then
    log_info "Jetstack repo already added, updating..."
    helm repo update jetstack
else
    helm repo add jetstack https://charts.jetstack.io
    helm repo update
fi

log_success "Helm repository ready"

# ----- Step 2: Install cert-manager -----
log_info "Step 2: Installing cert-manager..."

if helm list -n "${CERT_MANAGER_NAMESPACE}" 2>/dev/null | grep -q cert-manager; then
    log_warn "cert-manager already installed. Upgrading..."
    helm upgrade cert-manager jetstack/cert-manager \
        --namespace "${CERT_MANAGER_NAMESPACE}" \
        --version "${CERT_MANAGER_VERSION}" \
        --values "${REPO_DIR}/security/certificates/cert-manager-install.yaml" \
        --wait --timeout 300s
else
    helm install cert-manager jetstack/cert-manager \
        --namespace "${CERT_MANAGER_NAMESPACE}" --create-namespace \
        --version "${CERT_MANAGER_VERSION}" \
        --values "${REPO_DIR}/security/certificates/cert-manager-install.yaml" \
        --wait --timeout 300s
fi

log_success "cert-manager Helm chart installed"

# ----- Step 3: Wait for webhook readiness -----
log_info "Step 3: Waiting for cert-manager webhook to become ready..."

# The webhook must be available before we can create any Certificate or Issuer
# resources. Without it, the API server rejects cert-manager CRDs.
kubectl wait --for=condition=Available deployment/cert-manager-webhook \
    -n "${CERT_MANAGER_NAMESPACE}" --timeout=120s

log_success "cert-manager webhook is ready"

# ----- Step 4: Verify all pods are running -----
log_info "Step 4: Verifying cert-manager pods..."

EXPECTED_DEPLOYMENTS=("cert-manager" "cert-manager-webhook" "cert-manager-cainjector")
ALL_READY=true

for deploy in "${EXPECTED_DEPLOYMENTS[@]}"; do
    if kubectl rollout status deployment/"${deploy}" -n "${CERT_MANAGER_NAMESPACE}" --timeout=60s; then
        log_success "${deploy} is running"
    else
        log_error "${deploy} is not ready"
        ALL_READY=false
    fi
done

if [ "${ALL_READY}" != true ]; then
    log_error "Not all cert-manager components are ready. Check logs:"
    log_error "  kubectl logs -n ${CERT_MANAGER_NAMESPACE} deployment/cert-manager"
    exit 1
fi

# ----- Step 5: Apply ClusterIssuers -----
log_info "Step 5: Applying ClusterIssuers..."

kubectl apply -f "${REPO_DIR}/security/certificates/cluster-issuers.yaml"

# Wait for the internal CA certificate to be issued
log_info "Waiting for internal CA certificate to be ready..."
kubectl wait --for=condition=Ready certificate/citadel-internal-ca \
    -n "${CERT_MANAGER_NAMESPACE}" --timeout=120s 2>/dev/null || {
    log_warn "Internal CA certificate not yet ready (may take a moment)"
    log_warn "Check status: kubectl describe certificate citadel-internal-ca -n ${CERT_MANAGER_NAMESPACE}"
}

log_success "ClusterIssuers applied"

# ----- Step 6: Verify ClusterIssuers -----
log_info "Step 6: Verifying ClusterIssuers..."

ISSUERS=("letsencrypt-staging" "letsencrypt-prod" "internal-ca" "citadel-selfsigned-bootstrap")
for issuer in "${ISSUERS[@]}"; do
    if kubectl get clusterissuer "${issuer}" &>/dev/null; then
        READY=$(kubectl get clusterissuer "${issuer}" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "Unknown")
        if [ "${READY}" = "True" ]; then
            log_success "ClusterIssuer '${issuer}' is Ready"
        else
            log_warn "ClusterIssuer '${issuer}' exists but is not Ready yet (status: ${READY})"
        fi
    else
        log_error "ClusterIssuer '${issuer}' not found"
    fi
done

# ----- Step 7: Smoke test with a self-signed certificate -----
log_info "Step 7: Running smoke test..."

# Create a test certificate using the internal CA
kubectl apply -f - <<'SMOKE_TEST_EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: citadel-cert-manager-smoke-test
  namespace: default
spec:
  secretName: citadel-cert-manager-smoke-test-tls
  duration: 1h
  renewBefore: 30m
  commonName: smoke-test.citadel.local
  dnsNames:
    - smoke-test.citadel.local
  issuerRef:
    name: citadel-selfsigned-bootstrap
    kind: ClusterIssuer
SMOKE_TEST_EOF

# Wait for the test certificate
if kubectl wait --for=condition=Ready certificate/citadel-cert-manager-smoke-test \
    -n default --timeout=60s 2>/dev/null; then
    log_success "Smoke test passed — certificate issued successfully"
    # Clean up
    kubectl delete certificate citadel-cert-manager-smoke-test -n default --ignore-not-found
    kubectl delete secret citadel-cert-manager-smoke-test-tls -n default --ignore-not-found
else
    log_warn "Smoke test certificate not ready within 60s"
    log_warn "This may be normal if the CA is still initializing"
    log_warn "Check: kubectl describe certificate citadel-cert-manager-smoke-test -n default"
    # Clean up anyway
    kubectl delete certificate citadel-cert-manager-smoke-test -n default --ignore-not-found 2>/dev/null || true
    kubectl delete secret citadel-cert-manager-smoke-test-tls -n default --ignore-not-found 2>/dev/null || true
fi

# ----- Summary -----
log_info ""
log_info "=========================================="
log_info "cert-manager Installation Complete"
log_info "=========================================="
log_info ""
log_info "Components:"
log_info "  cert-manager controller:  Running (HA: 2 replicas)"
log_info "  cert-manager webhook:     Running (HA: 2 replicas)"
log_info "  cert-manager cainjector:  Running (HA: 2 replicas)"
log_info ""
log_info "ClusterIssuers:"
log_info "  letsencrypt-staging:      For testing (not browser-trusted)"
log_info "  letsencrypt-prod:         For production (browser-trusted)"
log_info "  internal-ca:              For service-to-service mTLS"
log_info ""
log_info "Next steps:"
log_info "  1. Update the email in cluster-issuers.yaml for Let's Encrypt"
log_info "  2. Request a certificate:"
log_info "     kubectl apply -f security/certificates/certificate-templates.yaml"
log_info "  3. Verify certificate issuance:"
log_info "     kubectl get certificates -A"
log_info ""
log_info "Documentation: security/certificates/README.md"
log_info "=========================================="
