terraform {
  required_version = ">= 1.0"

  required_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.60"
    }
  }
}

provider "hcloud" {
  token = var.hcloud_token
}

resource "hcloud_ssh_key" "deploy" {
  name       = "trails-cool-deploy"
  public_key = var.ssh_public_key
}

resource "hcloud_server" "trails" {
  name        = "trails-cool"
  server_type = "cx23"
  image       = "ubuntu-24.04"
  location    = "fsn1"
  ssh_keys    = [hcloud_ssh_key.deploy.id]

  labels = {
    project = "trails-cool"
  }

  user_data = <<-EOF
    #!/bin/bash
    set -euo pipefail

    # Install Docker
    curl -fsSL https://get.docker.com | sh

    # Install Docker Compose plugin
    apt-get install -y docker-compose-plugin

    # Create app directory
    mkdir -p /opt/trails-cool

    # Harden SSH
    cat > /etc/ssh/sshd_config.d/99-hardening.conf <<'SSHD'
    PasswordAuthentication no
    X11Forwarding no
    AllowTcpForwarding no
    AllowAgentForwarding no
    MaxAuthTries 3
    ClientAliveInterval 300
    ClientAliveCountMax 2
    SSHD
    systemctl reload ssh
  EOF

  # Hetzner Cloud provider's user_data hash algorithm changed in 1.45+,
  # causing spurious replacement diffs on every plan even when the script
  # is unchanged. user_data only runs on first boot anyway — changes after
  # provisioning have no effect — so we pin the attribute to ignore_changes.
  lifecycle {
    ignore_changes = [user_data]
  }
}

resource "hcloud_firewall" "trails" {
  name = "trails-cool"

  rule {
    direction  = "in"
    protocol   = "tcp"
    port       = "22"
    source_ips = ["0.0.0.0/0", "::/0"]
  }

  rule {
    direction  = "in"
    protocol   = "tcp"
    port       = "80"
    source_ips = ["0.0.0.0/0", "::/0"]
  }

  rule {
    direction  = "in"
    protocol   = "tcp"
    port       = "443"
    source_ips = ["0.0.0.0/0", "::/0"]
  }
}

resource "hcloud_firewall_attachment" "trails" {
  firewall_id = hcloud_firewall.trails.id
  server_ids  = [hcloud_server.trails.id]
}

# Private network bridging the flagship (Hetzner Cloud) to the dedicated
# BRouter host (Hetzner Robot) via vSwitch. Both sides must be in the same
# location (fsn1 / eu-central).
#
# Hetzner requires Cloud servers to attach to a regular "cloud" subnet;
# the "vswitch" subnet is VLAN-only and cannot host Cloud NICs. The two
# subnets live side by side in the same network and Hetzner routes between
# them automatically.
#
#   10.0.0.0/16    — overall private network
#   10.0.0.1       — network gateway (reserved by Hetzner; not usable)
#   10.0.0.0/24    — cloud subnet (hosts Cloud server NICs)
#   10.0.0.2       — flagship (assigned below)
#   10.0.1.0/24    — vSwitch subnet (bridged to Robot VLAN)
#   10.0.1.10      — dedicated BRouter host (configured on the Robot side
#                    as a VLAN sub-interface, not managed by Terraform)

resource "hcloud_network" "private" {
  name     = "trails-cool"
  ip_range = "10.0.0.0/16"

  labels = {
    project = "trails-cool"
  }
}

resource "hcloud_network_subnet" "cloud" {
  network_id   = hcloud_network.private.id
  type         = "cloud"
  network_zone = "eu-central"
  ip_range     = "10.0.0.0/24"
}

resource "hcloud_network_subnet" "vswitch" {
  network_id   = hcloud_network.private.id
  type         = "vswitch"
  network_zone = "eu-central"
  ip_range     = "10.0.1.0/24"
  vswitch_id   = var.vswitch_id
}

resource "hcloud_server_network" "trails" {
  server_id  = hcloud_server.trails.id
  network_id = hcloud_network.private.id
  ip         = "10.0.0.2"

  depends_on = [hcloud_network_subnet.cloud]
}

# Reverse DNS

resource "hcloud_rdns" "trails_ipv4" {
  server_id  = hcloud_server.trails.id
  ip_address = hcloud_server.trails.ipv4_address
  dns_ptr    = "trails.cool"
}

resource "hcloud_rdns" "trails_ipv6" {
  server_id  = hcloud_server.trails.id
  ip_address = hcloud_server.trails.ipv6_address
  dns_ptr    = "trails.cool"
}

# DNS

resource "hcloud_zone_rrset" "root_a" {
  zone = "trails.cool"
  name = "@"
  type = "A"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv4_address }]
}

resource "hcloud_zone_rrset" "root_aaaa" {
  zone = "trails.cool"
  name = "@"
  type = "AAAA"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv6_address }]
}

resource "hcloud_zone_rrset" "planner_a" {
  zone = "trails.cool"
  name = "planner"
  type = "A"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv4_address }]
}

resource "hcloud_zone_rrset" "planner_aaaa" {
  zone = "trails.cool"
  name = "planner"
  type = "AAAA"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv6_address }]
}

# Staging — persistent journal at staging.trails.cool, persistent planner
# at planner.staging.trails.cool, and PR previews at pr-<N>.staging.trails.cool
# (covered by the wildcard). All resolve to the flagship; Caddy routes per-host.

resource "hcloud_zone_rrset" "staging_a" {
  zone = "trails.cool"
  name = "staging"
  type = "A"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv4_address }]
}

resource "hcloud_zone_rrset" "staging_aaaa" {
  zone = "trails.cool"
  name = "staging"
  type = "AAAA"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv6_address }]
}

resource "hcloud_zone_rrset" "staging_wildcard_a" {
  zone = "trails.cool"
  name = "*.staging"
  type = "A"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv4_address }]
}

resource "hcloud_zone_rrset" "staging_wildcard_aaaa" {
  zone = "trails.cool"
  name = "*.staging"
  type = "AAAA"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv6_address }]
}

# Internal wildcard — all *.internal.trails.cool resolves to the server.
# Caddy handles routing per-subdomain. No individual DNS entries needed
# for internal services (Grafana, Prometheus, etc.)
resource "hcloud_zone_rrset" "internal_wildcard_a" {
  zone = "trails.cool"
  name = "*.internal"
  type = "A"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv4_address }]
}

resource "hcloud_zone_rrset" "internal_wildcard_aaaa" {
  zone = "trails.cool"
  name = "*.internal"
  type = "AAAA"
  ttl  = 300
  records = [{ value = hcloud_server.trails.ipv6_address }]
}

resource "hcloud_zone_rrset" "www_cname" {
  zone = "trails.cool"
  name = "www"
  type = "CNAME"
  ttl  = 300
  records = [{ value = "trails.cool." }]
}

# Fastmail Email

resource "hcloud_zone_rrset" "mx_root" {
  zone = "trails.cool"
  name = "@"
  type = "MX"
  ttl  = 3600
  records = [
    { value = "10 in1-smtp.messagingengine.com." },
    { value = "20 in2-smtp.messagingengine.com." },
  ]
}

resource "hcloud_zone_rrset" "mx_wildcard" {
  zone = "trails.cool"
  name = "*"
  type = "MX"
  ttl  = 3600
  records = [
    { value = "10 in1-smtp.messagingengine.com." },
    { value = "20 in2-smtp.messagingengine.com." },
  ]
}

resource "hcloud_zone_rrset" "mx_mail" {
  zone = "trails.cool"
  name = "mail"
  type = "MX"
  ttl  = 3600
  records = [
    { value = "10 in1-smtp.messagingengine.com." },
    { value = "20 in2-smtp.messagingengine.com." },
  ]
}

resource "hcloud_zone_rrset" "mail_a" {
  zone = "trails.cool"
  name = "mail"
  type = "A"
  ttl  = 3600
  records = [{ value = "103.168.172.65" }]
}

resource "hcloud_zone_rrset" "spf" {
  zone = "trails.cool"
  name = "@"
  type = "TXT"
  ttl  = 3600
  records = [{ value = "\"v=spf1 include:spf.messagingengine.com ?all\"" }]
}

resource "hcloud_zone_rrset" "dmarc" {
  zone = "trails.cool"
  name = "_dmarc"
  type = "TXT"
  ttl  = 3600
  records = [{ value = "\"v=DMARC1; p=none;\"" }]
}

resource "hcloud_zone_rrset" "dkim_fm1" {
  zone = "trails.cool"
  name = "fm1._domainkey"
  type = "CNAME"
  ttl  = 3600
  records = [{ value = "fm1.trails.cool.dkim.fmhosted.com." }]
}

resource "hcloud_zone_rrset" "dkim_fm2" {
  zone = "trails.cool"
  name = "fm2._domainkey"
  type = "CNAME"
  ttl  = 3600
  records = [{ value = "fm2.trails.cool.dkim.fmhosted.com." }]
}

resource "hcloud_zone_rrset" "dkim_fm3" {
  zone = "trails.cool"
  name = "fm3._domainkey"
  type = "CNAME"
  ttl  = 3600
  records = [{ value = "fm3.trails.cool.dkim.fmhosted.com." }]
}

resource "hcloud_zone_rrset" "dkim_mesmtp" {
  zone = "trails.cool"
  name = "mesmtp._domainkey"
  type = "CNAME"
  ttl  = 3600
  records = [{ value = "mesmtp.trails.cool.dkim.fmhosted.com." }]
}

resource "hcloud_zone_rrset" "srv_autodiscover" {
  zone = "trails.cool"
  name = "_autodiscover._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 443 autodiscover.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_caldav" {
  zone = "trails.cool"
  name = "_caldav._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 0 0 ." }]
}

resource "hcloud_zone_rrset" "srv_caldavs" {
  zone = "trails.cool"
  name = "_caldavs._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 443 d5979255.caldav.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_carddav" {
  zone = "trails.cool"
  name = "_carddav._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 0 0 ." }]
}

resource "hcloud_zone_rrset" "srv_carddavs" {
  zone = "trails.cool"
  name = "_carddavs._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 443 d5979255.carddav.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_imap" {
  zone = "trails.cool"
  name = "_imap._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 0 0 ." }]
}

resource "hcloud_zone_rrset" "srv_imaps" {
  zone = "trails.cool"
  name = "_imaps._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 993 imap.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_jmap" {
  zone = "trails.cool"
  name = "_jmap._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 443 api.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_pop3" {
  zone = "trails.cool"
  name = "_pop3._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 0 0 ." }]
}

resource "hcloud_zone_rrset" "srv_pop3s" {
  zone = "trails.cool"
  name = "_pop3s._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "10 1 995 pop.fastmail.com." }]
}

resource "hcloud_zone_rrset" "srv_submission" {
  zone = "trails.cool"
  name = "_submission._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 0 0 ." }]
}

resource "hcloud_zone_rrset" "srv_submissions" {
  zone = "trails.cool"
  name = "_submissions._tcp"
  type = "SRV"
  ttl  = 3600
  records = [{ value = "0 1 465 smtp.fastmail.com." }]
}

# Outputs

output "server_ip" {
  value = hcloud_server.trails.ipv4_address
}

output "domain" {
  value = "trails.cool"
}

output "planner_domain" {
  value = "planner.trails.cool"
}

output "flagship_private_ip" {
  value       = hcloud_server_network.trails.ip
  description = "Private IP of the flagship on the vSwitch network (reachable from the dedicated BRouter host)"
}
