{ config, lib, pkgs, ... }:

let
  cfg = config.services.burrow.garage;
in
{
  options.services.burrow.garage = {
    enable = lib.mkEnableOption "Burrow Garage S3-compatible object storage";

    environmentFile = lib.mkOption {
      type = lib.types.path;
      default = "/var/lib/burrow/garage/env";
      description = "Host-local environment file containing Garage and S3 bootstrap secrets.";
    };

    region = lib.mkOption {
      type = lib.types.str;
      default = "garage";
      description = "S3 region name advertised by Garage.";
    };

    apiListenAddress = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1";
      description = "Address used by the Garage S3 API listener.";
    };

    apiPort = lib.mkOption {
      type = lib.types.port;
      default = 3900;
      description = "Port used by the Garage S3 API listener.";
    };

    webListenAddress = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1";
      description = "Address used by the Garage static-web listener.";
    };

    webPort = lib.mkOption {
      type = lib.types.port;
      default = 3902;
      description = "Port used by the Garage static-web listener.";
    };

    adminListenAddress = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1";
      description = "Address used by the Garage admin listener.";
    };

    adminPort = lib.mkOption {
      type = lib.types.port;
      default = 3903;
      description = "Port used by the Garage admin listener.";
    };

    zone = lib.mkOption {
      type = lib.types.str;
      default = "burrow-forge";
      description = "Garage layout zone assigned to the forge host.";
    };

    layoutCapacity = lib.mkOption {
      type = lib.types.str;
      default = "100GB";
      description = "Initial Garage layout capacity assigned to the forge host.";
    };

    s3RootDomain = lib.mkOption {
      type = lib.types.str;
      default = "s3.burrow.net";
      description = "Wildcard root domain used for bucket-style S3 API requests.";
    };

    webRootDomain = lib.mkOption {
      type = lib.types.str;
      default = "web.burrow.net";
      description = "Wildcard root domain used for bucket static website requests.";
    };

    apiDomain = lib.mkOption {
      type = lib.types.str;
      default = "objects.burrow.net";
      description = "Public S3 API reverse-proxy domain.";
    };

    enableApiProxy = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Expose the Garage S3 API through Caddy.";
    };

    enableWebProxy = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = "Expose the Garage static-web listener through Caddy wildcard routes.";
    };

    atticBucket = lib.mkOption {
      type = lib.types.str;
      default = "attic";
      description = "Garage bucket used by the Burrow Nix cache.";
    };

    releaseBucket = lib.mkOption {
      type = lib.types.str;
      default = "burrow-releases";
      description = "Garage bucket used for release artifacts.";
    };

    packageBucket = lib.mkOption {
      type = lib.types.str;
      default = "burrow-packages";
      description = "Garage bucket used for signed package repositories.";
    };
  };

  config = lib.mkIf cfg.enable {
    services.garage = {
      enable = true;
      package = pkgs.garage;
      environmentFile = cfg.environmentFile;
      settings = {
        metadata_dir = "/var/lib/garage/meta";
        data_dir = "/var/lib/garage/data";
        rpc_bind_addr = "127.0.0.1:3901";
        rpc_public_addr = "127.0.0.1:3901";
        replication_factor = 1;
        s3_api = {
          s3_region = cfg.region;
          api_bind_addr = "${cfg.apiListenAddress}:${toString cfg.apiPort}";
          root_domain = ".${cfg.s3RootDomain}";
        };
        s3_web = {
          bind_addr = "${cfg.webListenAddress}:${toString cfg.webPort}";
          root_domain = ".${cfg.webRootDomain}";
          index = "index.html";
        };
        admin.api_bind_addr = "${cfg.adminListenAddress}:${toString cfg.adminPort}";
      };
    };

    systemd.services.burrow-garage-env = {
      description = "Create Burrow Garage bootstrap environment";
      before = [ "garage.service" ];
      wantedBy = [ "garage.service" ];
      requiredBy = [ "garage.service" ];
      path = [
        pkgs.coreutils
        pkgs.gnugrep
        pkgs.openssl
      ];
      serviceConfig = {
        Type = "oneshot";
        User = "root";
        Group = "root";
        RemainAfterExit = true;
      };
      script = ''
        set -euo pipefail

        install -d -m 0700 "$(dirname ${lib.escapeShellArg cfg.environmentFile})"
        touch ${lib.escapeShellArg cfg.environmentFile}
        chmod 0600 ${lib.escapeShellArg cfg.environmentFile}
        chown root:root ${lib.escapeShellArg cfg.environmentFile}

        has_var() {
          grep -q "^$1=" ${lib.escapeShellArg cfg.environmentFile}
        }

        add_var() {
          local name="$1"
          local value="$2"
          if ! has_var "$name"; then
            printf '%s=%s\n' "$name" "$value" >> ${lib.escapeShellArg cfg.environmentFile}
          fi
        }

        add_var GARAGE_RPC_SECRET "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
        add_var GARAGE_ATTIC_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
        add_var GARAGE_ATTIC_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
        add_var GARAGE_RELEASE_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
        add_var GARAGE_RELEASE_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
        add_var GARAGE_PACKAGE_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
        add_var GARAGE_PACKAGE_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
        add_var GARAGE_BACKUP_ACCESS_KEY_ID "GK$(${pkgs.openssl}/bin/openssl rand -hex 12)"
        add_var GARAGE_BACKUP_SECRET_ACCESS_KEY "$(${pkgs.openssl}/bin/openssl rand -hex 32)"
        add_var ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64 "$(${pkgs.openssl}/bin/openssl genrsa -traditional 4096 | ${pkgs.coreutils}/bin/base64 -w0)"

        . ${lib.escapeShellArg cfg.environmentFile}
        add_var AWS_ACCESS_KEY_ID "$GARAGE_ATTIC_ACCESS_KEY_ID"
        add_var AWS_SECRET_ACCESS_KEY "$GARAGE_ATTIC_SECRET_ACCESS_KEY"
      '';
    };

    systemd.services.burrow-garage-bootstrap = {
      description = "Bootstrap Burrow Garage layout, keys, and buckets";
      after = [ "garage.service" ];
      requires = [ "garage.service" ];
      wantedBy = [ "multi-user.target" ];
      path = [
        config.services.garage.package
        pkgs.coreutils
        pkgs.gawk
        pkgs.gnugrep
      ];
      serviceConfig = {
        Type = "oneshot";
        User = "root";
        Group = "root";
      };
      script = ''
        set -euo pipefail
        set -a
        . ${lib.escapeShellArg cfg.environmentFile}
        set +a

        for _ in $(seq 1 60); do
          if garage status >/dev/null 2>&1; then
            break
          fi
          sleep 1
        done

        node_id="$(garage status | tail -n1 | awk '{ print $1 }')"
        if [ -n "$node_id" ]; then
          garage layout assign -c ${lib.escapeShellArg cfg.layoutCapacity} -z ${lib.escapeShellArg cfg.zone} "$node_id" >/dev/null 2>&1 || true
          garage layout apply --version 1 >/dev/null 2>&1 || true
        fi

        ensure_key() {
          local key_id="$1"
          local secret="$2"
          if ! garage key info "$key_id" >/dev/null 2>&1; then
            garage key import "$key_id" "$secret" --yes >/dev/null
          fi
        }

        ensure_bucket() {
          local bucket="$1"
          local key_id="$2"
          if ! garage bucket info "$bucket" >/dev/null 2>&1; then
            garage bucket create "$bucket" >/dev/null
          fi
          garage bucket allow --read --write --owner "$bucket" --key "$key_id" >/dev/null
        }

        ensure_bucket_reader() {
          local bucket="$1"
          local key_id="$2"
          garage bucket allow --read "$bucket" --key "$key_id" >/dev/null
        }

        ensure_key "$GARAGE_ATTIC_ACCESS_KEY_ID" "$GARAGE_ATTIC_SECRET_ACCESS_KEY"
        ensure_key "$GARAGE_RELEASE_ACCESS_KEY_ID" "$GARAGE_RELEASE_SECRET_ACCESS_KEY"
        ensure_key "$GARAGE_PACKAGE_ACCESS_KEY_ID" "$GARAGE_PACKAGE_SECRET_ACCESS_KEY"
        ensure_key "$GARAGE_BACKUP_ACCESS_KEY_ID" "$GARAGE_BACKUP_SECRET_ACCESS_KEY"

        ensure_bucket ${lib.escapeShellArg cfg.atticBucket} "$GARAGE_ATTIC_ACCESS_KEY_ID"
        ensure_bucket ${lib.escapeShellArg cfg.releaseBucket} "$GARAGE_RELEASE_ACCESS_KEY_ID"
        ensure_bucket ${lib.escapeShellArg cfg.packageBucket} "$GARAGE_PACKAGE_ACCESS_KEY_ID"
        ensure_bucket_reader ${lib.escapeShellArg cfg.atticBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
        ensure_bucket_reader ${lib.escapeShellArg cfg.releaseBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
        ensure_bucket_reader ${lib.escapeShellArg cfg.packageBucket} "$GARAGE_BACKUP_ACCESS_KEY_ID"
      '';
    };

    services.caddy.virtualHosts = lib.mkMerge [
      (lib.mkIf cfg.enableApiProxy {
        "${cfg.apiDomain}".extraConfig = ''
          encode gzip zstd
          reverse_proxy ${cfg.apiListenAddress}:${toString cfg.apiPort}
        '';
      })
      (lib.mkIf cfg.enableWebProxy {
        "*.${cfg.webRootDomain}".extraConfig = ''
          encode gzip zstd
          reverse_proxy ${cfg.webListenAddress}:${toString cfg.webPort}
        '';
      })
    ];
  };
}
