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

let
  cfg = config.services.code-server;
  defaultUser = "code-server";
  defaultGroup = defaultUser;
in {
  options = {
    services.code-server = {
      enable = lib.mkEnableOption "code-server";

      package = lib.mkPackageOption pkgs "code-server" {
        example = ''
          pkgs.vscode-with-extensions.override {
            vscode = pkgs.code-server;
            vscodeExtensions = with pkgs.vscode-extensions; [
              bbenoist.nix
              dracula-theme.theme-dracula
            ];
          }
        '';
      };

      extraPackages = lib.mkOption {
        default = [ ];
        description = ''
          Additional packages to add to the code-server {env}`PATH`.
        '';
        example = lib.literalExpression "[ pkgs.go ]";
        type = lib.types.listOf lib.types.package;
      };

      extraEnvironment = lib.mkOption {
        type = lib.types.attrsOf lib.types.str;
        description = ''
          Additional environment variables to pass to code-server.
        '';
        default = { };
        example = { PKG_CONFIG_PATH = "/run/current-system/sw/lib/pkgconfig"; };
      };

      extraArguments = lib.mkOption {
        default = [ ];
        description = ''
          Additional arguments to pass to code-server.
        '';
        example = lib.literalExpression ''[ "--log=info" ]'';
        type = lib.types.listOf lib.types.str;
      };

      host = lib.mkOption {
        default = "localhost";
        description = ''
          The host name or IP address the server should listen to.
        '';
        type = lib.types.str;
      };

      port = lib.mkOption {
        default = 4444;
        description = ''
          The port the server should listen to.
        '';
        type = lib.types.port;
      };

      auth = lib.mkOption {
        default = "password";
        description = ''
          The type of authentication to use.
        '';
        type = lib.types.enum [ "none" "password" ];
      };

      hashedPassword = lib.mkOption {
        default = "";
        description = ''
          Create the password with: `echo -n 'thisismypassword' | npx argon2-cli -e`.
        '';
        type = lib.types.str;
      };

      user = lib.mkOption {
        default = defaultUser;
        example = "yourUser";
        description = ''
          The user to run code-server as.
          By default, a user named `${defaultUser}` will be created.
        '';
        type = lib.types.str;
      };

      group = lib.mkOption {
        default = defaultGroup;
        example = "yourGroup";
        description = ''
          The group to run code-server under.
          By default, a group named `${defaultGroup}` will be created.
        '';
        type = lib.types.str;
      };

      extraGroups = lib.mkOption {
        default = [ ];
        description = ''
          An array of additional groups for the `${defaultUser}` user.
        '';
        example = [ "docker" ];
        type = lib.types.listOf lib.types.str;
      };

      socket = lib.mkOption {
        default = null;
        example = "/run/code-server/socket";
        description = ''
          Path to a socket (bind-addr will be ignored).
        '';
        type = lib.types.nullOr lib.types.str;
      };

      socketMode = lib.mkOption {
        default = null;
        description = ''
           File mode of the socket.
        '';
        type = lib.types.nullOr lib.types.str;
      };

      userDataDir = lib.mkOption {
        default = null;
        description = ''
          Path to the user data directory.
        '';
        type = lib.types.nullOr lib.types.str;
      };

      extensionsDir = lib.mkOption {
        default = null;
        description = ''
          Path to the extensions directory.
        '';
        type = lib.types.nullOr lib.types.str;
      };

      proxyDomain = lib.mkOption {
        default = null;
        example = "code-server.lan";
        description = ''
          Domain used for proxying ports.
        '';
        type = lib.types.nullOr lib.types.str;
      };

      disableTelemetry = lib.mkOption {
        default = false;
        example = true;
        description = ''
          Disable telemetry.
        '';
        type = lib.types.bool;
      };

      disableUpdateCheck = lib.mkOption {
        default = false;
        example = true;
        description = ''
          Disable update check.
          Without this flag, code-server checks every 6 hours against the latest github release and
          then notifies you once every week that a new release is available.
        '';
        type = lib.types.bool;
      };

      disableFileDownloads = lib.mkOption {
        default = false;
        example = true;
        description = ''
          Disable file downloads from Code.
        '';
        type = lib.types.bool;
      };

      disableWorkspaceTrust = lib.mkOption {
        default = false;
        example = true;
        description = ''
          Disable Workspace Trust feature.
        '';
        type = lib.types.bool;
      };

      disableGettingStartedOverride = lib.mkOption {
        default = false;
        example = true;
        description = ''
          Disable the coder/coder override in the Help: Getting Started page.
        '';
        type = lib.types.bool;
      };

    };
  };

  config = lib.mkIf cfg.enable {
    systemd.services.code-server = {
      description = "Code server";
      wantedBy = [ "multi-user.target" ];
      wants = [ "network-online.target" ];
      after = [ "network-online.target" ];
      path = cfg.extraPackages;
      environment = {
        HASHED_PASSWORD = cfg.hashedPassword;
      } // cfg.extraEnvironment;
      serviceConfig = {
        ExecStart = ''
          ${lib.getExe cfg.package} \
            --auth=${cfg.auth} \
            --bind-addr=${cfg.host}:${toString cfg.port} \
          '' + lib.optionalString (cfg.socket != null) ''
            --socket=${cfg.socket} \
          '' + lib.optionalString (cfg.userDataDir != null) ''
            --user-data-dir=${cfg.userDataDir} \
          '' + lib.optionalString (cfg.extensionsDir != null) ''
            --extensions-dir=${cfg.extensionsDir} \
          '' + lib.optionalString (cfg.disableTelemetry == true) ''
            --disable-telemetry \
          '' + lib.optionalString (cfg.disableUpdateCheck == true) ''
            --disable-update-check \
          '' + lib.optionalString (cfg.disableFileDownloads == true) ''
            --disable-file-downloads \
          '' + lib.optionalString (cfg.disableWorkspaceTrust == true) ''
            --disable-workspace-trust \
          '' + lib.optionalString (cfg.disableGettingStartedOverride == true) ''
            --disable-getting-started-override \
          '' + lib.escapeShellArgs cfg.extraArguments;
        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
        RuntimeDirectory = cfg.user;
        User = cfg.user;
        Group = cfg.group;
        Restart = "on-failure";
      };
    };

    users.users."${cfg.user}" = lib.mkMerge [
      (lib.mkIf (cfg.user == defaultUser) {
        isNormalUser = true;
        description = "code-server user";
        inherit (cfg) group;
      })
      {
        packages = cfg.extraPackages;
        inherit (cfg) extraGroups;
      }
    ];

    users.groups."${defaultGroup}" = lib.mkIf (cfg.group == defaultGroup) { };
  };

  meta.maintainers = [ lib.maintainers.stackshadow ];
}
