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

with lib;

let
  inherit (flakeInputs.home-manager.lib) hm;

  databasePath = "${config.xdg.dataHome}/feeds";

  imm =
    let
      hlib = pkgs.haskell.lib;
      haskellPackages = pkgs.haskellPackages.override {
        overrides = finalHaskell: prevHaskell: {
          uri-bytestring = finalHaskell.callCabal2nix "uri-bytestring" (pkgs.fetchFromGitHub {
            owner = "gkleen";
            repo = "uri-bytestring";
            rev = "5f7f32c8274bc4d1b81d99582f5148fe3e8b637e";
            sha256 = "XLanwyCDIlMuOkpE5LbTNOBfL+1kZX+URfj9Bhs1Nsc=";
            fetchSubmodules = true;
          }) {};
          atom-conduit = finalHaskell.callCabal2nix "atom-conduit" (pkgs.fetchFromGitHub {
            owner = "gkleen";
            repo = "atom-conduit";
            rev = "022f0182a02373f87c06a0a09817c8c41efe2425";
            sha256 = "8yEyh3ymqkoM/YP+eBqPq1I5ofzj0Qn7ojL7IWx1DPo=";
            fetchSubmodules = true;
          }) {};
          rss-conduit = finalHaskell.callCabal2nix "rss-condit" (pkgs.fetchFromGitHub {
            owner = "gkleen";
            repo = "rss-conduit";
            rev = "dbb0960a8d3dc519f1607aa0223b3a25a49282ef";
            sha256 = "Md1XApZWkdv4JvNoaVnjz0S85LbEC6w9U3PUcwXfu94=";
            fetchSubmodules = true;
          }) {};
          beam-core = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-core" "${beamSrc}/beam-core" {});
          beam-migrate = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-migrate" "${beamSrc}/beam-migrate" {});
          beam-sqlite = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-sqlite" "${beamSrc}/beam-sqlite" {});

          imm = finalHaskell.callCabal2nix "imm" (pkgs.fetchFromGitHub {
            owner = "k0ral";
            repo = "imm";
            rev = "5033879667264cb44cee65671a66f6aa43f249e7";
            sha256 = "PG22caLQmAGhLZP49HsazuNd8IFKKaTuhXIQBD8v4Fs=";
            fetchSubmodules = true;
          }) {};
        };
      };
      beamSrc = pkgs.fetchFromGitHub {
        owner = "haskell-beam";
        repo = "beam";
        rev = "efd464b079755a781c2bb7a2fc030d6c141bbb8a";
        sha256 = "8nTuBP/vD0L/qMo4h3XNrGZvpIwXuMVdj40j5gvHU6w=";
        fetchSubmodules = true;
      };
    in haskellPackages.imm;
   immWrapped = pkgs.runCommand "${imm.name}-wrapped-${config.home.username}"
    { nativeBuildInputs = with pkgs; [ makeWrapper ];
    } ''
      mkdir -p $out/bin
      makeWrapper ${imm}/bin/imm $out/bin/imm \
        --add-flags --callbacks=${notmuchCallbacks}
    '';

  notmuchCallbacks = pkgs.writeText "imm-callbacks-${config.home.username}.dhall" ''
    [ { _executable = "${immNotmuchInsert}/bin/imm-notmuch-insert"
      , _arguments = [] : List Text
      }
    ]
  '';

  immNotmuchInsert = pkgs.stdenv.mkDerivation rec {
    name = "imm-notmuch-insert-${config.home.username}";
    src = ./imm-notmuch-insert.py;

    phases = [ "buildPhase" "checkPhase" "installPhase" "fixupPhase" ];

    python = pkgs.python39.withPackages (ps: with ps; [ configparser dateutil html2text ]);

    nativeBuildInputs = with pkgs; [ makeWrapper ];

    buildPhase = ''
      substituteAll $src imm-notmuch-insert
    '';

    doCheck = true;
    checkPhase = ''
      ${python}/bin/python -m py_compile imm-notmuch-insert
    '';

    installPhase = ''
      install -m 0755 -D -t $out/bin \
        imm-notmuch-insert
    '';

    fixupPhase = ''
      wrapProgram $out/bin/imm-notmuch-insert \
        --prefix PATH : ${pkgs.notmuch}/bin \
        --set NOTMUCH_CONFIG ${configPath}
    '';
  };

  mkIniKeyValue = key: value:
    let
      tweakVal = v:
        if isString v then
          v
        else if isList v then
          concatMapStringsSep ";" tweakVal v
        else if isBool v then
          (if v then "true" else "false")
        else
          toString v;
    in "${key}=${tweakVal value}";

  notmuchIni = {
    database = { path = databasePath; };

    maildir = { synchronize_flags = false; };

    new = {
      ignore = [];
      tags = ["new"];
    };

    user = {
      name = config.home.username;
      primary_email = "${config.home.username}@imm.invalid";
    };

    search = { exclude_tags = ["deleted"]; };
  };
  configPath = pkgs.writeText "notmuchrc" (generators.toINI { mkKeyValue = mkIniKeyValue; } notmuchIni);

  afewConfigDir = pkgs.symlinkJoin {
    name = "afew-config";
    paths = [
      (pkgs.writeTextDir "config" ''
         [InboxFilter]
      '')
    ];
  };

  notmuchHooksDir =
    let
      afewHook = pkgs.writeShellScript "afew" ''
        exec -- ${pkgs.afew}/bin/afew -c ${afewConfigDir} -C ${configPath} --tag --new -vv
      '';
    in pkgs.linkFarm "notmuch-hooks" [
      { name = "post-new";
        path = afewHook;
      }
      { name = "post-insert";
        path = afewHook;
      }
    ];

  notmuchWrapped = pkgs.runCommand "${pkgs.notmuch.name}-wrapped-${config.home.username}"
    { nativeBuildInputs = with pkgs; [ makeWrapper ];
    } ''
      mkdir -p $out/bin
      makeWrapper ${pkgs.notmuch}/bin/notmuch $out/bin/notmuch-feeds \
        --set NOTMUCH_CONFIG ${configPath}
    '';
  alotWrapped = pkgs.runCommand "${pkgs.alot.name}-wrapped-${config.home.username}"
    { nativeBuildInputs = with pkgs; [ makeWrapper gnused ];
    } ''
      mkdir -p $out/bin
      makeWrapper ${pkgs.alot}/bin/alot $out/bin/alot-feeds \
        --prefix MAILCAPS : ${alotMailcaps} \
        --add-flags --config=${alotConfig} \
        --add-flags --notmuch-config=${configPath}

      mkdir $out/share
      ln -s ${pkgs.alot}/share/alot $out/share
      mkdir -p $out/share/applications
      sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/applications/alot.desktop > $out/share/applications/alot-feeds.desktop
      mkdir -p $out/share/zsh/site-functions
      sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/zsh/site-functions/_alot > $out/share/zsh/site-functions/_alot-feeds
    '';

  alotConfig = pkgs.runCommand "alot" {
    realname = notmuchIni.user.name;
    address = notmuchIni.user.primary_email;
  } "substituteAll ${./alot.config} $out";
  alotMailcaps = pkgs.writeText "mailcaps" ''
    text/html; ${pkgs.lynx}/bin/lynx -dump -dont_wrap_pre -assume_charset=utf-8 -display_charset=utf-8 "%s"; nametemplate=%s.html; copiousoutput
  '';
in {
  config = {
    home.packages = [ immWrapped notmuchWrapped pkgs.notmuch.man alotWrapped ];

    home.activation.createImm = hm.dag.entryAfter ["writeBoundary"] ''
      $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.xdg.configHome}/imm
    '';
    
    home.activation.createFeedsDatabase = hm.dag.entryAfter ["linkGeneration" "writeBoundary"] ''
      $DRY_RUN_CMD mkdir -p -m 0750 $VERBOSE_ARG ${databasePath}
      $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${databasePath}/new ${databasePath}/cur ${databasePath}/tmp
      if ! [[ -d ${databasePath}/.notmuch ]]; then
          NOTMUCH_VERBOSE_ARG="--quiet"
          if [[ -v VERBOSE ]]; then
            NOTMUCH_VERBOSE_ARG="--verbose"
          fi
          NOTMUCH_CONFIG=${configPath} $DRY_RUN_CMD ${pkgs.notmuch}/bin/notmuch new $NOTMUCH_VERBOSE_ARG
      fi
      $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG ${notmuchHooksDir} ${databasePath}/.notmuch/hooks
    '';

    systemd.user.services."logrotate-imm" = {
      Unit = {
        Description = "Rotate imm logfile";
      };
      Service = {
        Type = "oneshot";
        ExecStart = ''
          ${pkgs.logrotate}/bin/logrotate --state ${config.xdg.configHome}/imm/imm.logrotate ${pkgs.writeText "logrotate.conf" ''
            ${config.xdg.configHome}/imm/imm.log {
                rotate 5
                size 1024k
            }
          ''}
        '';
      };
    };
    systemd.user.timers."logrotate-imm" = {
      Timer = {
        OnActiveSec = "6h";
        OnUnitActiveSec = "6h";
      };
      Install = {
        WantedBy = ["default.target"];
      };
    };
  };
}