resource "kubernetes_namespace" "website" {
  metadata {
    annotations = {
      name = "website"
    }

    labels = {
      name = "website"
    }

    name = "website"
  }
}

resource "kubernetes_service_account" "website_service_account" {

  metadata {
    name      = "website-app-sa"
    namespace = kubernetes_namespace.website.metadata[0].name
  }

  automount_service_account_token = false
}

resource "kubernetes_service" "website" {
  for_each = var.websites
  metadata {
    name      = format("website-%s", replace(each.key, ".", "-"))
    namespace = kubernetes_namespace.website.metadata[0].name
  }
  spec {
    port {
      name        = "http"
      port        = 80
      target_port = "http"
    }
    dynamic "port" {
      for_each = each.value.tls ? [1] : []
      content {
        name        = "https"
        port        = 443
        target_port = "https"
      }
    }
    selector = {
      app = format("website-%s", replace(each.key, ".", "-"))
    }

    type = "ClusterIP"
  }
}

resource "kubernetes_manifest" "internal_cert" {
  for_each = {
    for k, v in var.websites : k => v if v.tls == true
  }
  manifest = {
    apiVersion = "cert-manager.io/v1"
    kind       = "Certificate"
    metadata = {
      name      = format("website-%s-tls-internal", replace(each.key, ".", "-"))
      namespace = kubernetes_namespace.website.metadata[0].name
    }
    spec = {
      secretName = format("website-%s-tls-internal", replace(each.key, ".", "-"))
      issuerRef = {
        name = each.value.issuer
        kind = "ClusterIssuer"
      }
      commonName = each.value.domain_name
      dnsNames   = each.value.fqdn
    }
  }
}

resource "kubernetes_config_map" "nginx_config" {
  metadata {
    name      = "nginx-config"
    namespace = kubernetes_namespace.website.metadata[0].name
  }

  data = {
    "nginx.conf" = local.nginx_config
  }
}

resource "kubernetes_config_map" "nginx_default_config" {
  for_each = var.websites
  metadata {
    name      = format("website-%s-default", replace(each.key, ".", "-"))
    namespace = kubernetes_namespace.website.metadata[0].name
  }

  data = {
    "default.conf" = coalesce(each.value.config, templatefile("${path.module}/resources/conf.d/default.conf.tpl", {
      tls            = each.value.tls
      fqdn           = each.value.domain_name
      custom_snippet = each.value.custom_snippet
    }))
  }
}

resource "kubernetes_ingress_v1" "website_ingress" {
  for_each = var.websites
  metadata {
    name      = "website-ingress"
    namespace = kubernetes_namespace.website.metadata[0].name

    annotations = merge({
      "kubernetes.io/ingress.class"    = "public"
      "cert-manager.io/cluster-issuer" = "letsencrypt-prod"
      },
      each.value.tls ? {
        "nginx.org/ssl-services"                       = format("website-%s", replace(each.key, ".", "-"))
        "nginx.org/ssl-redirect"                       = "true"
        "nginx.org/redirect-to-https"                  = "true"
        "nginx.ingress.kubernetes.io/backend-protocol" = "HTTPS"
      } : {}
    )
  }
  spec {
    tls {
      hosts       = each.value.fqdn
      secret_name = format("website-%s-tls", replace(each.key, ".", "-"))
    }
    dynamic "rule" {
      for_each = each.value.fqdn
      content {
        host = rule.value
        http {
          path {
            backend {
              service {
                name = format("website-%s", replace(each.key, ".", "-"))
                port {
                  name = each.value.tls ? "https" : "http"
                }
              }
            }
          }
        }
      }
    }
  }
}

resource "kubernetes_deployment" "website" {
  for_each = var.websites

  metadata {
    name      = "website-app"
    namespace = kubernetes_namespace.website.metadata[0].name
  }
  spec {
    selector {
      match_labels = {
        app = format("website-%s", replace(each.key, ".", "-"))
      }
    }
    strategy {
      type = "RollingUpdate"
    }
    template {
      metadata {
        labels = {
          app = format("website-%s", replace(each.key, ".", "-"))
        }
        annotations = {
          "checksum/nginx" = local.nginx_config_checksum
          "checksum/default" = sha256(coalesce(each.value.config, templatefile("${path.module}/resources/conf.d/default.conf.tpl", {
            tls            = each.value.tls
            fqdn           = each.value.domain_name
            custom_snippet = each.value.custom_snippet
            }))
          )
        }
      }
      spec {
        service_account_name = kubernetes_service_account.website_service_account.metadata[0].name

        container {
          name              = "website"
          image             = "nginx:${var.tag}"
          image_pull_policy = "Always"

          resources {
            requests = {
              memory = "100Mi"
            }
          }

          readiness_probe {
            http_get {
              path = "/"
              port = 8080
            }
            initial_delay_seconds = 5
            period_seconds        = 10
          }

          liveness_probe {
            http_get {
              path = "/"
              port = 8080
            }
            initial_delay_seconds = 5
            period_seconds        = 10
          }

          security_context {
            run_as_user                = 1000
            run_as_group               = 1000
            allow_privilege_escalation = false
            read_only_root_filesystem  = true
          }

          port {
            container_port = 8080
            name           = "http"
          }

          dynamic "port" {
            for_each = each.value.tls ? [443] : []
            content {
              container_port = 8443
              name           = "https"
            }
          }
          volume_mount {
            name       = "nginx"
            mount_path = "/etc/nginx/nginx.conf"
            sub_path   = "nginx.conf"
            read_only  = true
          }
          volume_mount {
            name       = "default"
            mount_path = "/etc/nginx/conf.d/default.conf"
            sub_path   = "default.conf"
            read_only  = true
          }
          volume_mount {
            name       = "tmp"
            mount_path = "/tmp"
          }
          volume_mount {
            name       = "website"
            mount_path = "/tmp/site"
            read_only  = true
          }
          dynamic "volume_mount" {
            for_each = each.value.tls ? [443] : []
            content {
              name       = "tls"
              mount_path = "/etc/ssl/certs/private"
              read_only  = true
            }
          }
        }
        volume {
          name = "nginx"
          config_map {
            name = kubernetes_config_map.nginx_config.metadata[0].name
          }
        }
        volume {
          name = "default"
          config_map {
            name = kubernetes_config_map.nginx_default_config[each.key].metadata[0].name
          }
        }
        volume {
          name = "tmp"
          empty_dir {
            medium = "Memory"
          }
        }
        volume {
          name = "website"
          host_path {
            path = format("%s/%s", local.websites_path, each.key)
          }
        }
        dynamic "volume" {
          for_each = each.value.tls ? [443] : []
          content {
            name = "tls"
            secret {
              secret_name = kubernetes_manifest.internal_cert[each.key].manifest["metadata"]["name"]
              items {
                key  = "tls.crt"
                path = "tls.crt"
              }
              items {
                key  = "tls.key"
                path = "tls.key"
              }
            }
          }
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [spec[0].template[0].metadata[0].annotations["kubectl.kubernetes.io/restartedAt"]]
  }
}
