{ lib, pkgs, config, ... }:
with lib;
let
  cfg = config.services.hledger-web;
in {
  options.services.hledger-web = {

    enable = mkEnableOption (lib.mdDoc "hledger-web service");

    serveApi = mkEnableOption (lib.mdDoc "Serve only the JSON web API, without the web UI.");

    host = mkOption {
      type = types.str;
      default = "127.0.0.1";
      description = lib.mdDoc ''
        Address to listen on.
      '';
    };

    port = mkOption {
      type = types.port;
      default = 5000;
      example = 80;
      description = lib.mdDoc ''
        Port to listen on.
      '';
    };

    capabilities = {
      view = mkOption {
        type = types.bool;
        default = true;
        description = lib.mdDoc ''
          Enable the view capability.
        '';
      };
      add = mkOption {
        type = types.bool;
        default = false;
        description = lib.mdDoc ''
          Enable the add capability.
        '';
      };
      manage = mkOption {
        type = types.bool;
        default = false;
        description = lib.mdDoc ''
          Enable the manage capability.
        '';
      };
    };

    stateDir = mkOption {
      type = types.path;
      default = "/var/lib/hledger-web";
      description = lib.mdDoc ''
        Path the service has access to. If left as the default value this
        directory will automatically be created before the hledger-web server
        starts, otherwise the sysadmin is responsible for ensuring the
        directory exists with appropriate ownership and permissions.
      '';
    };

    journalFiles = mkOption {
      type = types.listOf types.str;
      default = [ ".hledger.journal" ];
      description = lib.mdDoc ''
        Paths to journal files relative to {option}`services.hledger-web.stateDir`.
      '';
    };

    baseUrl = mkOption {
      type = with types; nullOr str;
      default = null;
      example = "https://example.org";
      description = lib.mdDoc ''
        Base URL, when sharing over a network.
      '';
    };

    extraOptions = mkOption {
      type = types.listOf types.str;
      default = [];
      example = [ "--forecast" ];
      description = lib.mdDoc ''
        Extra command line arguments to pass to hledger-web.
      '';
    };

  };

  config = mkIf cfg.enable {

    users.users.hledger = {
      name = "hledger";
      group = "hledger";
      isSystemUser = true;
      home = cfg.stateDir;
      useDefaultShell = true;
    };

    users.groups.hledger = {};

    systemd.services.hledger-web = let
      capabilityString = with cfg.capabilities; concatStringsSep "," (
        (optional view "view")
        ++ (optional add "add")
        ++ (optional manage "manage")
      );
      serverArgs = with cfg; escapeShellArgs ([
        "--serve"
        "--host=${host}"
        "--port=${toString port}"
        "--capabilities=${capabilityString}"
        (optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}")
        (optionalString (cfg.serveApi) "--serve-api")
      ] ++ (map (f: "--file=${stateDir}/${f}") cfg.journalFiles)
        ++ extraOptions);
    in {
      description = "hledger-web - web-app for the hledger accounting tool.";
      documentation = [ "https://hledger.org/hledger-web.html" ];
      wantedBy = [ "multi-user.target" ];
      after = [ "networking.target" ];
      serviceConfig = mkMerge [
        {
          ExecStart = "${pkgs.hledger-web}/bin/hledger-web ${serverArgs}";
          Restart = "always";
          WorkingDirectory = cfg.stateDir;
          User = "hledger";
          Group = "hledger";
          PrivateTmp = true;
        }
        (mkIf (cfg.stateDir == "/var/lib/hledger-web") {
          StateDirectory = "hledger-web";
        })
      ];
    };

  };

  meta.maintainers = with lib.maintainers; [ marijanp erictapen ];
}
