{ config, pkgs, ... }:

# rollback to a previously working channel version, useful
# when "doas nixos-rebuild switch --upgrade" breaks:
#
# $ doas nix-channel --rollback

let
  enable-virtualization = true;
  extra-groups = if enable-virtualization then [ "docker" ] else [];
  extra-packages = with pkgs; [
  ] ++ (if enable-virtualization then [ gitlab-runner ] else []);
  work = {
    packages = with pkgs; {
      proprietary = [
        slack
        zoom-us
        openfortivpn
        skypeforlinux
        chromium
      ];
      libre = [
        yubioath-desktop
        clojure # =clj=
      ];
    };
    config = {
      yubioauth-support = true;
    };
  };
in {
  imports = [ /etc/nixos/hardware-configuration.nix ];

  # Use the systemd-boot EFI boot loader.
  boot = {
    loader = {
      efi.canTouchEfiVariables = true;
      grub = {
        enable = true;
        version = 2;
        efiSupport = true;
        device = "nodev";
      };
    };
    initrd.luks.devices = {
      crypted = {
        device = "/dev/nvme0n1p2";
        preLVM = true;
      };
    };
  };

  hardware = {
    bluetooth.enable = true;
    pulseaudio = {
      package = pkgs.pulseaudioFull;
      enable = true;
      extraModules = [ pkgs.pulseaudio-modules-bt ];
      extraConfig = ''
        # Switch between headset and headphone mode (e.g. for calls and music) automatically
        load-module module-bluetooth-policy auto_switch=2
        # Echo cancellation and noise cleanup of mic
        load-module module-echo-cancel aec_method=webrtc

        # https://til.flourishing.stream/2020/05/24/cancelamento-de-ruido-pulseaudio/
        ### Enable Echo/Noise-Cancellation
        load-module module-echo-cancel use_master_format=1 aec_method=webrtc aec_args="analog_gain_control=0\ digital_gain_control=1" source_name=echoCancel_source sink_name=echoCancel_sink
        set-default-source echoCancel_source
        set-default-sink echoCancel_sink
      '';
    };
  };

  networking = {
    hostName = "usurpador";
    networkmanager.enable = true;
  };

  console = {
    keyMap = "br-abnt2";
  };

  # Select internationalisation properties.
  i18n = {
    defaultLocale = "fr_FR.UTF-8";
  };

  # Set your time zone.
  time.timeZone = "America/Sao_Paulo";

  documentation = {
    enable = true;
    man.enable = true;
    dev.enable = true;
    man.generateCaches = true;
    nixos.includeAllModules = true;
  };

  environment = {
    enableDebugInfo = true;

    systemPackages = let
      pkgs-19 = import (fetchTarball
        "https://github.com/NixOS/nixpkgs/archive/b0d748e244df6c977b2a1db3873ffdc271e59615.tar.gz")
        { };
      pkgs-21 = import (fetchTarball
        "https://github.com/NixOS/nixpkgs/archive/e6377ff35544226392b49fa2cf05590f9f0c4b43.tar.gz")
        { };
      bmake-with-alias = pkgs.bmake.overrideAttrs (oldAttrs: {
        postInstall = ''
          ln -s $out/bin/bmake $out/bin/make
        '';
      });
      tinycc-with-posix-alias = pkgs.tinycc.overrideAttrs (oldAttrs: {
        postInstall = ''
          ln -s $out/bin/tcc $out/bin/c99
        '';
      });
      timidity-with-more-formats = pkgs.timidity.overrideAttrs (oldAttrs: {
        configureFlags = oldAttrs.configureFlags ++ [ "--enable-audio=vorbis,flac" ];
        buildInputs = oldAttrs.buildInputs ++ (with pkgs; [ libogg libvorbis flac ]);
      });
      mktorrent-latest = pkgs.mktorrent.overrideAttrs (oldAttrs: rec {
        version = "de7d011b35458de1472665f50b96c9cf6c303f39";
        src = pkgs.fetchFromGitHub {
          owner = "Rudde";
          repo = "mktorrent";
          rev = version;
          sha256 = "1gsky1ijxrfsw07lvfjw4annjkdfpmnp8nnddi1wakbzqn6v5dwq";
        };
      });

    in with pkgs;
    [
      vagrant

      ## Documentation
      posix_man_pages
      scheme-manpages

      ## DVCS
      mr
      fossil
      mercurial
      cvs
      pkgs-19.bitkeeper
      pkgs-19.packer
      gitAndTools.gitFull
      gitAndTools.gitRemoteGcrypt
      gitAndTools.git-open

      ## Personal data tools
      borgbackup
      pass
      gnupg
      pinentry_gtk2

      ## CLI tools
      groff
      mktorrent-latest
      pkgs-21.diffoscope
      diffoscope
      bash-completion
      nix-bash-completions
      youtube-dl
      wget
      tmux
      sox # =play=, SoX - Sound eXchange
      gettext
      perlPackages.Po4a
      rlwrap
      jq
      rq
      openvpn
      xclip
      psmisc # =killall=
      tree
      feh
      qrencode
      bind # =dig=
      rsync
      moreutils # =vipe=, =sponge=, errno
      graphviz # =dot= and others
      mkpasswd
      direnv
      recutils
      sqlite
      trash-cli
      entr
      mailutils
      ffmpeg-full
      ledger
      gnuplot
      ranger
      electrum
      zathura
      st
      pavucontrol
      newsboat
      acpi
      xsel
      nixfmt
      nvi
      fzf
      calcurse
      pandoc
      cloc
      inetutils # telnet
      lsof
      lsscsi
      gotop
      imagemagick
      enscript
      tcl
      python3
      unzip

      ## System utilities
      pmutils
      btrfs-progs

      ## Mail
      isync
      notmuch
      afew
      # alot FIXME
      pkgs-21.alot
      w3m
      urlscan
      md4c
      docutils
      timidity-with-more-formats

      ## Conversations
      # gomuks
      weechat
      # poezio

      ## Programming tools
      perl
      shellcheck
      gcc
      musl.dev
      clang
      clang-tools
      valgrind
      gdb
      sbcl
      nodejs_latest
      quickjs
      deno
      lilypond
      ghostscript
      pdftk
      guile_3_0

      ## POSIX
      bmake-with-alias
      byacc
      bc
      lzip
      gnugrep
      file
      ed
      tinycc-with-posix-alias
      m4
      bintools-unwrapped
      units

      ## Containers and VMs
      qemu
      expect

      ## xmonad
      xorg.xmodmap
      xorg.xmessage
      dmenu
      escrotum
      xorg.xbacklight
      playerctl
      lightdm
      fvwm # =xpmroot=
      haskellPackages.xmobar
      arandr
      alsaUtils # for xmonad volume commands
      yad
      amfora
      cmus # remove it, use just mpv?
      mutagen

      ## EuAndreh overlay
      # xyz-euandreh.remembering-latest
      # xyz-euandreh.git-permalink-latest
      # xyz-euandreh.autoqemu-latest

      ## GUI programs
      # frescobaldi broken
      inkscape
      # dino
      fractal
      poedit
      audacity
      zbar
      (mpv-with-scripts.override {
        scripts = with mpvScripts; [ mpris sponsorblock ];
      })
      firefox
      libreoffice

      ## Candidates to be replaced by alternatives
      transmission-gtk # https://github.com/btpd/btpd
      tdesktop # telegram -> gomuks
      whatsapp-for-linux
    ] ++ work.packages.libre ++ work.packages.proprietary ++ extra-packages;

    etc = {
      "with-email.sh".text = ''
        #!/bin/sh
        set -u
        . ~/.bashrc

        while getopts 's:' flag; do
        	case "$flag" in
        		s)
        			SUBJECT="$OPTARG"
        			;;
        		*)
        			exit 2
        			;;
        	esac
        done
        shift $((OPTIND - 1))

        now() {
        	date '+%Y-%m-%dT%H:%M:%S%Z'
        }

        OUT="$(echo 'mkstemp(template)' | m4 -D template="${"\${TMPDIR:-/tmp}"}/m4-tmpname.")"
        printf 'Running command: %s\nStarting at: %s\n\n' "$*" "$(now)" >> "$OUT"
        (sh -c "$@" 2>&1) >> "$OUT"
        STATUS="$?"
        printf '\nFinished at: %s\n' "$(now)" >> "$OUT"

        mail \
        	-a 'Content-Type: text/plain; charset=UTF-8'         \
        	-a 'From:cron@euandre.org'                           \
        	-s " (exit status: $STATUS) - ${"\${SUBJECT:-NO SUBJECT}"}" \
        	eu@euandre.org < "$OUT"
      '';
    };
  };

  nixpkgs = let allow-unfree = builtins.length work.packages.proprietary > 0;
  in {
    config = {
      android_sdk.accept_license = allow-unfree;
      allowUnfree = allow-unfree;
    };
    overlays = let
      use-local = false;
      euandreh-nixpkgs = import (if use-local then
        ../package-repository/default.nix
      else
        fetchTarball {
          url =
            "https://euandreh.xyz/package-repository.git/snapshot/package-repository-main.tar.gz";
        }) { inherit pkgs; };
    in [ euandreh-nixpkgs ];
  };

  # Look at
  # https://www.vivaolinux.com.br/topico/Off-Code-Cafe/Me-apresente-sua-distro

  programs = {
    bash.enableCompletion = true;
    autojump.enable = true;
    msmtp = {
      enable = true;
      extraConfig = ''
        defaults
         auth on
         tls on
         port 587
         syslog on

        account euandreh
         host box.euandre.org
         from eu@euandre.org
         user eu@euandre.org
	 # msmtp in cron's MAILTO environment doesn't include PASSWORD_STORE_DIR
         passwordeval pass show Mail-in-a-Box

        account euandrehxyz
         host mail.euandreh.xyz
         from eu@euandreh.xyz
         user eu@euandreh.xyz
         passwordeval pass show VPS/kuvira/mail

        account euandreharrobaponto
         host mail.arrobaponto.org
         from euandreh@arrobaponto.org
         user euandreh@arrobaponto.org
         passwordeval pass show VPS/toph/mail

        account nubank
         host smtp.gmail.com
         from andre.moreira@nubank.com.br
         user andre.moreira@nubank.com.br
         passwordeval pass show andre.moreira@nubank.com.br

        account default : euandreh
      '';
    };
  };

  services = {
    # required by vagrant+libvirt
    nfs.server.enable = enable-virtualization;

    # Required for local network printer
    avahi = {
      enable = true;
      nssmdns = true;
    };

    printing = {
      enable = false;
      drivers = let
        epson-201401w = with pkgs;
          stdenv.mkDerivation rec {
            v = "201401w";
            pname = "epson-${v}";
            version = "1.0.0";

            src = fetchurl {
              urls = [
                "https://download.ebz.epson.net/dsc/op/stable/SRPMS/epson-inkjet-printer-${v}-${version}-1lsb3.2.src.rpm"
                "https://download3.ebz.epson.net/dsc/f/03/00/03/45/41/92e9c9254f0ee4230a069545ba27ec2858a2c457/epson-inkjet-printer-201401w-1.0.0-1lsb3.2.src.rpm"
              ];
              sha256 = "0c60m1sd59s4sda38dc5nniwa7dh1b0kv1maajr0x9d38gqlyk3x";
            };

            nativeBuildInputs = [ rpmextract autoreconfHook file ];

            buildInputs = [ libjpeg cups ];

            unpackPhase = ''
              rpmextract $src
              tar -zxf epson-inkjet-printer-${v}-${version}.tar.gz
              tar -zxf epson-inkjet-printer-filter-${version}.tar.gz
              for ppd in epson-inkjet-printer-${v}-${version}/ppds/*; do
                      substituteInPlace $ppd --replace "/opt/epson-inkjet-printer-${v}" "$out"
                      substituteInPlace $ppd --replace "/cups/lib" "/lib/cups"
              done
              cd epson-inkjet-printer-filter-${version}
            '';

            postInstall = ''
              cd ../epson-inkjet-printer-${v}-${version}
              cp -a lib64 resource watermark $out
              mkdir -p $out/share/cups/model/epson-inkjet-printer-${v}
              cp -a ppds $out/share/cups/model/epson-inkjet-printer-${v}/
              cp -a Manual.txt $out/doc/
              cp -a README $out/doc/README.driver
            '';
          };
      in [
        epson-201401w
      ];
    };

    cron = {
      enable = true;
      # Keep this so that when the "mail" command fails I also get a notification email
      mailto = "eu@euandre.org";
      systemCronJobs = [
        "40 23  * * *  root    sh /etc/with-email.sh -s '[CRON] usurpador: guix upgrade' -- 'guix pull && guix package -u && systemctl restart guix-daemon.service'"
        "40 22  * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: guix upgrade' -- 'guix pull && guix package -u'"
        "0  22  * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: guix deploy'  -- 'guix deploy -L ~/dev/libre/servers/src/lib ~/dev/libre/servers/src/machines.scm'"
        "10 22  * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: mr update'    -- 'mr -s safeupdate'"
        "20 */6 * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: email flush'  -- 'msmtp-queue -r'"
        "30 */6 * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: email sync'   -- 'mbsync -a && notmuch new'"
        "50 22  * * *  andreh  sh /etc/with-email.sh -s '[CRON] usurpador: borg backup'  -- '~/dev/libre/dotfiles/src/bin/backup'"
      ];
    };

    xserver = {
      enable = true;
      layout = "br";
      xkbOptions = "caps:swapescape";

      # Touchpad support
      libinput.enable = true;

      config = ''
        Section "Device"
          Identifier  "Intel Graphics"
          Driver      "intel01"
          Option      "Backlight"  "intel_backlight"
        EndSection
      '';

      windowManager.xmonad = {
        enable = true;
        enableContribAndExtras = true;
      };
      displayManager = {
        defaultSession = "none+xmonad";

        lightdm.enable = true;

        # The config inception was inspired by:
        # http://ubuntuforum-br.org/index.php?topic=13784.0
        # Lambda symbol name taken from:
        # http://wiki.linuxquestions.org/wiki/List_of_Keysyms_Recognised_by_Xmodmap
        # To test a keycode:
        # xmodmap -e 'keycode 118 = bar Greek_lamda'
        sessionCommands = ''
          ${pkgs.fvwm}/bin/xpmroot  ~/annex/docs/emotion.png &
        '';
      };
    };

    clipmenu.enable = true;
    blueman.enable = true;
    pcscd.enable = work.config.yubioauth-support;
  };

  security = {
    sudo.enable = false;
    doas = {
      enable = true;
      extraConfig = ''
        permit persist setenv { NIX_PATH } :wheel
      '';
    };
  };

  users = {
    extraUsers = let
      andrehUser = {
        andreh = {
          isNormalUser = true;
          uid = 1000;
          description = "EuAndreh";
          extraGroups = [ "wheel" "networkmanager" "libvirtd" ] ++ extra-groups ;
        };
      };
      # From the Guix manual:
      # https://www.gnu.org/software/guix/manual/en/html_node/Build-Environment-Setup.html#Build-Environment-Setup
      buildUser = (i: {
        "guixbuilder${i}" = { # guixbuilder$i
          group = "guixbuild"; # -g guixbuild
          extraGroups = [ "guixbuild" ]; # -G guixbuild
          home = "/var/empty"; # -d /var/empty
          shell = pkgs.shadow; # -s `which nologin`
          description = "Guix build user ${i}"; # -c "Guix buid user $i"
          isSystemUser = true; # --system
        };
      });
      # merge all users
    in pkgs.lib.fold (str: acc: acc // buildUser str) andrehUser
    # for i in `seq -w 1 10`
    (map (pkgs.lib.fixedWidthNumber 2) (builtins.genList (n: n + 1) 10));

    extraGroups.guixbuild = { name = "guixbuild"; };
  };

  systemd = {
    services = {
      # Derived from Guix guix-daemon.service.in
      # https://git.savannah.gnu.org/cgit/guix.git/tree/etc/guix-daemon.service.in?id=00c86a888488b16ce30634d3a3a9d871ed6734a2
      guix-daemon = {
        enable = true;
        description = "Build daemon for GNU Guix";
        serviceConfig = {
          ExecStart =
            "/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon --build-users-group=guixbuild";
        };
        wantedBy = [ "multi-user.target" ];
      };
    };
    user.services = {
      mpris-proxy = {
        enable = true;
        description = "Mpris proxy, added to support bluetooth media keys";
        serviceConfig = {
          After = [ "network.target" "sound.target" ];
          ExecStart = "${pkgs.bluez}/bin/mpris-proxy";
          WantedBy = [ "default.target" ];
        };
      };
    };
  };

  system = {
    stateVersion = "20.03";
    autoUpgrade = {
      enable = true;
      dates = "0:10";
    };
  };

  xdg.portal = {
    enable = true;
    extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
  };

  virtualisation.libvirtd.enable = enable-virtualization;
  virtualisation.docker.enable = enable-virtualization;
}
