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

with builtins; with lib;
{
  options.wsl = with types;
    let
      coercedToStr = coercedTo (oneOf [ bool path int ]) (toString) str;
    in
    {
      enable = mkEnableOption "support for running NixOS as a WSL distribution";
      automountPath = mkOption {
        type = str;
        default = "/mnt";
        description = "The path where windows drives are mounted (e.g. /mnt/c)";
      };
      automountOptions = mkOption {
        type = str;
        default = "metadata,uid=1000,gid=100";
        description = "Options to use when mounting windows drives";
      };
      defaultUser = mkOption {
        type = str;
        default = "nixos";
        description = "The name of the default user";
      };
      startMenuLaunchers = mkEnableOption "shortcuts for GUI applications in the windows start menu";
      wslConf = mkOption {
        type = attrsOf (attrsOf (oneOf [ str int bool ]));
        description = "Entries that are added to /etc/wsl.conf";
      };
    };

  config =
    let
      cfg = config.wsl;
      syschdemd = import ../syschdemd.nix { inherit lib pkgs config; inherit (cfg) automountPath defaultUser; defaultUserHome = config.users.users.${cfg.defaultUser}.home; };
    in
    mkIf cfg.enable {

      wsl.wslConf = {
        automount = {
          enabled = true;
          mountFsTab = true;
          root = "${cfg.automountPath}/";
          options = cfg.automountOptions;
        };
        network = {
          generateResolvConf = mkDefault true;
          generateHosts = mkDefault true;
        };
      };

      # WSL is closer to a container than anything else
      boot.isContainer = true;

      environment.noXlibs = lib.mkForce false; # override xlibs not being installed (due to isContainer) to enable the use of GUI apps
      hardware.opengl.enable = true; # Enable GPU acceleration

      environment = {

        etc = {
          "wsl.conf".text = generators.toINI { } cfg.wslConf;

          # DNS settings are managed by WSL
          hosts.enable = !config.wsl.wslConf.network.generateHosts;
          "resolv.conf".enable = !config.wsl.wslConf.network.generateResolvConf;
        };

        systemPackages = [
          (pkgs.runCommand "wslpath" { } ''
            mkdir -p $out/bin
            ln -s /init $out/bin/wslpath
          '')
        ];
      };

      networking.dhcpcd.enable = false;

      users.users.${cfg.defaultUser} = {
        isNormalUser = true;
        uid = 1000;
        extraGroups = [ "wheel" ]; # Allow the default user to use sudo
      };

      users.users.root = {
        shell = "${syschdemd}/bin/syschdemd";
        # Otherwise WSL fails to login as root with "initgroups failed 5"
        extraGroups = [ "root" ];
      };

      security.sudo = {
        extraConfig = ''
          Defaults env_keep+=INSIDE_NAMESPACE
        '';
        wheelNeedsPassword = mkDefault false; # The default user will not have a password by default
      };

      system.activationScripts = {
        copy-launchers = mkIf cfg.startMenuLaunchers (
          stringAfter [ ] ''
            for x in applications icons; do
              echo "Copying /usr/share/$x"
              mkdir -p /usr/share/$x
              ${pkgs.rsync}/bin/rsync -ar --delete $systemConfig/sw/share/$x/. /usr/share/$x
            done
          ''
        );
        populateBin = stringAfter [ ] ''
          echo "setting up /bin..."
          ln -sf /init /bin/wslpath
          ln -sf ${pkgs.bashInteractive}/bin/bash /bin/sh
          ln -sf ${pkgs.util-linux}/bin/mount /bin/mount
        '';
      };

      systemd = {
        # Disable systemd units that don't make sense on WSL
        services = {
          "serial-getty@ttyS0".enable = false;
          "serial-getty@hvc0".enable = false;
          "getty@tty1".enable = false;
          "autovt@".enable = false;
          firewall.enable = false;
          systemd-resolved.enable = false;
          systemd-udevd.enable = false;
        };

        tmpfiles.rules = [
          # Don't remove the X11 socket
          "d /tmp/.X11-unix 1777 root root"
        ];

        # Don't allow emergency mode, because we don't have a console.
        enableEmergencyMode = false;
      };

      warnings = (optional (config.systemd.services.systemd-resolved.enable && config.wsl.wslConf.network.generateResolvConf) "systemd-resolved is enabled, but resolv.conf is managed by WSL");
    };
}
