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

let
  cfg = config.services.cockpit;
  inherit (lib) types mkEnableOption mkOption mkIf mdDoc literalMD mkPackageOptionMD;
  settingsFormat = pkgs.formats.ini {};
in {
  options = {
    services.cockpit = {
      enable = mkEnableOption (mdDoc "Cockpit");

      package = mkPackageOptionMD pkgs "Cockpit" {
        default = [ "cockpit" ];
      };

      settings = lib.mkOption {
        type = settingsFormat.type;

        default = {};

        description = mdDoc ''
          Settings for cockpit that will be saved in /etc/cockpit/cockpit.conf.

          See the [documentation](https://cockpit-project.org/guide/latest/cockpit.conf.5.html), that is also available with `man cockpit.conf.5` for details.
        '';
      };

      port = mkOption {
        description = mdDoc "Port where cockpit will listen.";
        type = types.port;
        default = 9090;
      };

      openFirewall = mkOption {
        description = mdDoc "Open port for cockpit.";
        type = types.bool;
        default = false;
      };
    };
  };
  config = mkIf cfg.enable {

    # expose cockpit-bridge system-wide
    environment.systemPackages = [ cfg.package ];

    # allow cockpit to find its plugins
    environment.pathsToLink = [ "/share/cockpit" ];

    # generate cockpit settings
    environment.etc."cockpit/cockpit.conf".source = settingsFormat.generate "cockpit.conf" cfg.settings;

    security.pam.services.cockpit = {};

    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];

    # units are in reverse sort order if you ls $out/lib/systemd/system
    # all these units are basically verbatim translated from upstream

    # Translation from $out/lib/systemd/system/systemd-cockpithttps.slice
    systemd.slices.system-cockpithttps = {
      description = "Resource limits for all cockpit-ws-https@.service instances";
      sliceConfig = {
        TasksMax = 200;
        MemoryHigh = "75%";
        MemoryMax = "90%";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.socket
    systemd.sockets."cockpit-wsinstance-https@" = {
      unitConfig = {
        Description = "Socket for Cockpit Web Service https instance %I";
        BindsTo = [ "cockpit.service" "cockpit-wsinstance-https@%i.service" ];
        # clean up the socket after the service exits, to prevent fd leak
        # this also effectively prevents a DoS by starting arbitrarily many sockets, as
        # the services are resource-limited by system-cockpithttps.slice
        Documentation = "man:cockpit-ws(8)";
      };
      socketConfig = {
        ListenStream = "/run/cockpit/wsinstance/https@%i.sock";
        SocketUser = "root";
        SocketMode = "0600";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-https@.service
    systemd.services."cockpit-wsinstance-https@" = {
      description = "Cockpit Web Service https instance %I";
      bindsTo = [ "cockpit.service"];
      path = [ cfg.package ];
      documentation = [ "man:cockpit-ws(8)" ];
      serviceConfig = {
        Slice = "system-cockpithttps.slice";
        ExecStart = "${cfg.package}/libexec/cockpit-ws --for-tls-proxy --port=0";
        User = "root";
        Group = "";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.socket
    systemd.sockets.cockpit-wsinstance-http = {
      unitConfig = {
        Description = "Socket for Cockpit Web Service http instance";
        BindsTo = "cockpit.service";
        Documentation = "man:cockpit-ws(8)";
      };
      socketConfig = {
        ListenStream = "/run/cockpit/wsinstance/http.sock";
        SocketUser = "root";
        SocketMode = "0600";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory.socket
    systemd.sockets.cockpit-wsinstance-https-factory = {
      unitConfig = {
        Description = "Socket for Cockpit Web Service https instance factory";
        BindsTo = "cockpit.service";
        Documentation = "man:cockpit-ws(8)";
      };
      socketConfig = {
        ListenStream = "/run/cockpit/wsinstance/https-factory.sock";
        Accept = true;
        SocketUser = "root";
        SocketMode = "0600";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-https-factory@.service
    systemd.services."cockpit-wsinstance-https-factory@" = {
      description = "Cockpit Web Service https instance factory";
      documentation = [ "man:cockpit-ws(8)" ];
      path = [ cfg.package ];
      serviceConfig = {
        ExecStart = "${cfg.package}/libexec/cockpit-wsinstance-factory";
        User = "root";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-wsinstance-http.service
    systemd.services."cockpit-wsinstance-http" = {
      description = "Cockpit Web Service http instance";
      bindsTo = [ "cockpit.service" ];
      path = [ cfg.package ];
      documentation = [ "man:cockpit-ws(8)" ];
      serviceConfig = {
        ExecStart = "${cfg.package}/libexec/cockpit-ws --no-tls --port=0";
        User = "root";
        Group = "";
      };
    };

    # Translation from $out/lib/systemd/system/cockpit.socket
    systemd.sockets."cockpit" = {
      unitConfig = {
        Description = "Cockpit Web Service Socket";
        Documentation = "man:cockpit-ws(8)";
        Wants = "cockpit-motd.service";
      };
      socketConfig = {
        ListenStream = cfg.port;
        ExecStartPost = [
          "-${cfg.package}/share/cockpit/motd/update-motd \"\" localhost"
          "-${pkgs.coreutils}/bin/ln -snf active.motd /run/cockpit/motd"
        ];
        ExecStopPost = "-${pkgs.coreutils}/bin/ln -snf inactive.motd /run/cockpit/motd";
      };
      wantedBy = [ "sockets.target" ];
    };

    # Translation from $out/lib/systemd/system/cockpit.service
    systemd.services."cockpit" = {
      description = "Cockpit Web Service";
      documentation = [ "man:cockpit-ws(8)" ];
      restartIfChanged = true;
      path = with pkgs; [ coreutils cfg.package ];
      requires = [ "cockpit.socket" "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ];
      after = [ "cockpit-wsinstance-http.socket" "cockpit-wsinstance-https-factory.socket" ];
      environment = {
        G_MESSAGES_DEBUG = "cockpit-ws,cockpit-bridge";
      };
      serviceConfig = {
        RuntimeDirectory="cockpit/tls";
        ExecStartPre = [
          # cockpit-tls runs in a more constrained environment, these + means that these commands
          # will run with full privilege instead of inside that constrained environment
          # See https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= for details
          "+${cfg.package}/libexec/cockpit-certificate-ensure --for-cockpit-tls"
        ];
        ExecStart = "${cfg.package}/libexec/cockpit-tls";
        User = "root";
        Group = "";
        NoNewPrivileges = true;
        ProtectSystem = "strict";
        ProtectHome = true;
        PrivateTmp = true;
        PrivateDevices = true;
        ProtectKernelTunables = true;
        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
        MemoryDenyWriteExecute = true;
      };
    };

    # Translation from $out/lib/systemd/system/cockpit-motd.service
    # This part basically implements a motd state machine:
    # - If cockpit.socket is enabled then /run/cockpit/motd points to /run/cockpit/active.motd
    # - If cockpit.socket is disabled then /run/cockpit/motd points to /run/cockpit/inactive.motd
    # - As cockpit.socket is disabled by default, /run/cockpit/motd points to /run/cockpit/inactive.motd
    # /run/cockpit/active.motd is generated dynamically by cockpit-motd.service
    systemd.services."cockpit-motd" = {
      path = with pkgs; [ nettools ];
      serviceConfig = {
        Type = "oneshot";
        ExecStart = "${cfg.package}/share/cockpit/motd/update-motd";
      };
      description = "Cockpit motd updater service";
      documentation = [ "man:cockpit-ws(8)" ];
      wants = [ "network.target" ];
      after = [ "network.target" "cockpit.socket" ];
    };

    systemd.tmpfiles.rules = [ # From $out/lib/tmpfiles.d/cockpit-tmpfiles.conf
      "C /run/cockpit/inactive.motd 0640 root root - ${cfg.package}/share/cockpit/motd/inactive.motd"
      "f /run/cockpit/active.motd   0640 root root -"
      "L+ /run/cockpit/motd - - - - inactive.motd"
      "d /etc/cockpit/ws-certs.d 0600 root root 0"
    ];
  };

  meta.maintainers = pkgs.cockpit.meta.maintainers;
}