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

let
  cfg = config.services.kasmweb;
in
{
  options.services.kasmweb = {
    enable = lib.mkEnableOption "kasmweb";

    networkSubnet = lib.mkOption {
      default = "172.20.0.0/16";
      type = lib.types.str;
      description = ''
        The network subnet to use for the containers.
      '';
    };

    postgres = {
      user = lib.mkOption {
        default = "kasmweb";
        type = lib.types.str;
        description = ''
          Username to use for the postgres database.
        '';
      };
      password = lib.mkOption {
        default = "kasmweb";
        type = lib.types.str;
        description = ''
          password to use for the postgres database.
        '';
      };
    };

    redisPassword = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        password to use for the redis cache.
      '';
    };

    defaultAdminPassword = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        default admin password to use.
      '';
    };

    defaultUserPassword = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        default user password to use.
      '';
    };

    defaultManagerToken = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        default manager token to use.
      '';
    };

    defaultGuacToken = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        default guac token to use.
      '';
    };

    defaultRegistrationToken = lib.mkOption {
      default = "kasmweb";
      type = lib.types.str;
      description = ''
        default registration token to use.
      '';
    };

    datastorePath = lib.mkOption {
      type = lib.types.str;
      default = "/var/lib/kasmweb";
      description = ''
        The directory used to store all data for kasmweb.
      '';
    };

    listenAddress = lib.mkOption {
      type = lib.types.str;
      default = "0.0.0.0";
      description = ''
        The address on which kasmweb should listen.
      '';
    };

    listenPort = lib.mkOption {
      type = lib.types.int;
      default = 443;
      description = ''
        The port on which kasmweb should listen.
      '';
    };

    sslCertificate = lib.mkOption {
      type = lib.types.nullOr lib.types.path;
      default = null;
      description = ''
        The SSL certificate to be used for kasmweb.
      '';
    };

    sslCertificateKey = lib.mkOption {
      type = lib.types.nullOr lib.types.path;
      default = null;
      description = ''
        The SSL certificate's key to be used for kasmweb. Make sure to specify
        this as a string and not a literal path, so that it is not accidentally
        included in your nixstore.
      '';
    };
  };

  config = lib.mkIf cfg.enable {

    systemd.services = {
      "init-kasmweb" = {
        wantedBy = [
          "docker-kasm_db.service"
        ];
        before = [
          "docker-kasm_db.service"
          "docker-kasm_redis.service"
          "docker-kasm_db_init.service"
          "docker-kasm_api.service"
          "docker-kasm_agent.service"
          "docker-kasm_manager.service"
          "docker-kasm_share.service"
          "docker-kasm_guac.service"
          "docker-kasm_proxy.service"
        ];
        serviceConfig = {
          Type = "oneshot";
          ExecStart = pkgs.substituteAll {
            src = ./initialize_kasmweb.sh;
            isExecutable = true;
            binPath = lib.makeBinPath [ pkgs.docker pkgs.openssl pkgs.gnused ];
            runtimeShell = pkgs.runtimeShell;
            kasmweb = pkgs.kasmweb;
            postgresUser = cfg.postgres.user;
            postgresPassword = cfg.postgres.password;
            inherit (cfg)
              datastorePath
              sslCertificate
              sslCertificateKey
              redisPassword
              defaultUserPassword
              defaultAdminPassword
              defaultManagerToken
              defaultRegistrationToken
              defaultGuacToken;
          };
        };
      };
    };

    virtualisation = {
      oci-containers.containers = {
        kasm_db = {
          image = "postgres:12-alpine";
          environment = {
            POSTGRES_PASSWORD = cfg.postgres.password;
            POSTGRES_USER = cfg.postgres.user;
            POSTGRES_DB = "kasm";
          };
          volumes = [
            "${cfg.datastorePath}/conf/database/data.sql:/docker-entrypoint-initdb.d/data.sql"
            "${cfg.datastorePath}/conf/database/:/tmp/"
            "kasmweb_db:/var/lib/postgresql/data"
          ];
          extraOptions = [ "--network=kasm_default_network" ];
        };
        kasm_db_init = {
          image = "kasmweb/api:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
            "kasmweb_api_data:/tmp"
          ];
          dependsOn = [ "kasm_db" ];
          entrypoint = "/bin/bash";
          cmd = [ "/opt/kasm/current/init_seeds.sh" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" ];
        };
        kasm_redis = {
          image = "redis:5-alpine";
          entrypoint = "/bin/sh";
          cmd = [
            "-c"
            "redis-server --requirepass ${cfg.redisPassword}"
          ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" ];
        };
        kasm_api = {
          image = "kasmweb/api:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
            "kasmweb_api_data:/tmp"
          ];
          dependsOn = [ "kasm_db_init" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host"  ];
        };
        kasm_manager = {
          image = "kasmweb/manager:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
          ];
          dependsOn = [ "kasm_db" "kasm_api" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only"];
        };
        kasm_agent = {
          image = "kasmweb/agent:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
            "/var/run/docker.sock:/var/run/docker.sock"
            "${pkgs.docker}/bin/docker:/usr/bin/docker"
            "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d"
          ];
          dependsOn = [ "kasm_manager" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ];
        };
        kasm_share = {
          image = "kasmweb/share:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
          ];
          dependsOn = [ "kasm_db" "kasm_redis" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ];
        };
        kasm_guac = {
          image = "kasmweb/kasm-guac:${pkgs.kasmweb.version}";
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/:/opt/kasm/current/"
          ];
          dependsOn = [ "kasm_db" "kasm_redis" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host" "--read-only" ];
        };
        kasm_proxy = {
          image = "kasmweb/nginx:latest";
          ports = [ "${cfg.listenAddress}:${toString cfg.listenPort}:443" ];
          user = "root:root";
          volumes = [
            "${cfg.datastorePath}/conf/nginx:/etc/nginx/conf.d:ro"
            "${cfg.datastorePath}/certs/kasm_nginx.key:/etc/ssl/private/kasm_nginx.key"
            "${cfg.datastorePath}/certs/kasm_nginx.crt:/etc/ssl/certs/kasm_nginx.crt"
            "${cfg.datastorePath}/www:/srv/www:ro"
            "${cfg.datastorePath}/log/nginx:/var/log/external/nginx"
            "${cfg.datastorePath}/log/logrotate:/var/log/external/logrotate"
          ];
          dependsOn = [ "kasm_manager" "kasm_api" "kasm_agent" "kasm_share"
          "kasm_guac" ];
          extraOptions = [ "--network=kasm_default_network" "--userns=host"
          "--network-alias=proxy"];
        };
      };
    };
  };
}