locals {
  # Local variables used to reduce repetition
  node_username = "root"
  server_dns = join(".", ["bridge", var.hostname])
}

resource "tls_private_key" "global_key" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

resource "local_sensitive_file" "ssh_private_key_pem" {
  filename        = "${path.module}/id_rsa"
  content         = tls_private_key.global_key.private_key_pem
  file_permission = "0600"
}

resource "local_file" "ssh_public_key_openssh" {
  filename = "${path.module}/id_rsa.pub"
  content  = tls_private_key.global_key.public_key_openssh
}

resource "hcloud_network" "private" {
  name     = "${var.prefix}-private-network"
  ip_range = var.network_cidr
}

resource "hcloud_network_route" "egress" {
  network_id  = hcloud_network.private.id
  destination = "0.0.0.0/0"
  gateway     = "10.0.1.1"
}

resource "hcloud_network_subnet" "private" {
  type         = "cloud"
  network_id   = hcloud_network.private.id
  network_zone = var.network_zone
  ip_range     = var.network_ip_range
}

# Temporary key pair used for SSH access
resource "hcloud_ssh_key" "management_ssh_key" {
  name       = "${var.prefix}-management-ssh-key"
  public_key = tls_private_key.global_key.public_key_openssh
}

# HCloud Instance for creating a single node RKE cluster and installing the Rancher server
resource "hcloud_server" "management_server" {
  name        = "${var.prefix}-management-1"
  image       = "ubuntu-24.04"
  server_type = var.instance_type
  location    = var.hcloud_location
  ssh_keys = [hcloud_ssh_key.management_ssh_key.id]

  network {
    network_id = hcloud_network.private.id
  }

  user_data = file(format("%s/files/userdata.template", path.module))

  provisioner "remote-exec" {
    inline = [
      "echo 'Waiting for cloud-init to complete...'",
      "cloud-init status --wait > /dev/null",
      "echo 'Completed cloud-init!'",
    ]

    connection {
      type        = "ssh"
      host        = self.ipv4_address
      user        = local.node_username
      private_key = tls_private_key.global_key.private_key_pem
    }
  }

  depends_on = [
    hcloud_network_subnet.private
  ]

  lifecycle {
    ignore_changes = [ssh_keys, network, user_data]
  }
}

module "k3s" {
  source = "../../../modules/cluster/init-k3s"

  node_public_ip      = hcloud_server.management_server.ipv4_address
  node_internal_ip    = one(hcloud_server.management_server.network[*]).ip
  node_username       = local.node_username
  ssh_private_key_pem = tls_private_key.global_key.private_key_pem
}

# install traefik
module "traefik" {
  source = "../../../modules/traefik"

  k8s_config_path = module.k3s.kube_config_server_yaml
}

# install cert-manager
module "cert_manager" {
  source = "../../../modules/cert-manager"

  wait_on         = module.traefik.installed
  k8s_config_path = module.k3s.kube_config_server_yaml
}

# install hetzner
module "cert_manager_hetzner" {
  source = "../../../modules/cert-manager/hetzner"

  wait_on           = module.cert_manager.installed
  k8s_config_yaml = file(module.k3s.kube_config_server_yaml)
  tld               = "fourlights.dev"
  hetzner_api_token = var.hdns_token
}

# install letsencrypt
module "letsencrypt" {
  source = "../../../modules/letsencrypt"

  wait_on         = module.cert_manager_hetzner.installed
  k8s_config_path = module.k3s.kube_config_server_yaml
  extraSolvers = [module.cert_manager_hetzner.solver]
}

module "bridge-tls" {
  source = "../../../modules/cluster/tls"

  wait_on = module.letsencrypt.installed
  name    = "bridge"
  namespace = "cert-manager"    # TODO: Get from cert-manager module
  hosts = ["bridge.fourlights.dev", "*.bridge.fourlights.dev"]
  k8s_config_yaml = file(module.k3s.kube_config_server_yaml)
}

# install rancher
module "rancher" {
  source = "../../../modules/rancher"

  wait_on         = module.bridge-tls.installed
  k8s_config_path = module.k3s.kube_config_server_yaml

  server_dns = local.server_dns
}

# install minio
module "minio" {
  source = "../../../modules/minio"

  wait_on = module.rancher.installed
  k8s_config_yaml = file(module.k3s.kube_config_server_yaml)

  server_dns   = local.server_dns
  service_name = "storage"
  namespace    = "minio"

  admin        = true
  tls          = true
  ingressClass = "traefik"
}

# install vault
module "vault" {
  source = "../../../modules/vault"

  wait_on         = module.rancher.installed
  k8s_config_path = module.k3s.kube_config_server_yaml

  server_dns   = local.server_dns
  service_name = "vault"
  namespace    = "vault"

  aws = {
    access_key_id     = var.aws_access_key_id
    secret_access_key = var.aws_secret_access_key
    kms_key_id        = var.aws_kms_key_id
    region            = var.aws_region
  }

  ingress = {
    enabled   = true
    tls       = true
    className = "traefik"
    annotations = {
      "kubernetes.io/ingress.class" : "traefik"
      "cert-manager.io/cluster-issuer"                   = "letsencrypt"
      "traefik.ingress.kubernetes.io/router.entrypoints" = "web,websecure"
      "traefik.ingress.kubernetes.io/router.middlewares" = "default-redirect-to-https@kubernetescrd,default-preserve-host-headers@kubernetescrd"
    }
  }
}

# bootstrap
module "cluster-bootstrap" {
  source = "../../../modules/cluster/bootstrap"

  cluster = "management"
  wait_on = module.vault.installed

  vault_server     = module.vault.vault_uri
  vault_root_token = module.vault.vault_root_token

  minio_server     = module.minio.minio_server
  minio_access_key = module.minio.minio_access_key
  minio_secret_key = module.minio.minio_secret_key
}

# management
module "cluster-management" {
  source = "../../../modules/cluster/management"

  cluster = "management"

  minio_server     = module.minio.minio_server
  minio_access_key = module.cluster-bootstrap.minio_access_key
  minio_secret_key = module.cluster-bootstrap.minio_secret_key

  vault_server = module.vault.vault_uri
  vault_token  = module.cluster-bootstrap.vault_token

  k8s_config_yaml = file(module.k3s.kube_config_server_yaml)
  ssh_private_key = local_sensitive_file.ssh_private_key_pem.content
  ssh_public_key  = local_file.ssh_public_key_openssh.content

  hcloud_token      = var.hcloud_token
  hcloud_network_id = hcloud_network.private.id
  vault_secret_path = module.vault.vault_local_file
}

resource "vault_kv_secret_v2" "rancher" {
  mount               = "management"
  name                = "rancher"
  delete_all_versions = true

  data_json = jsonencode({
    token = module.rancher.rancher_server_admin_token
  })

  depends_on = [module.vault.installed]
}

# install mijn 365zon
module "mijn_365zon" {
  source = "../../../modules/mijn-365zon-nl"

  wait_on         = module.rancher.installed
  k8s_config_path = module.k3s.kube_config_server_yaml
}
