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

let
  cfg = config.services.pingvin-share;
  inherit (lib)
    mkOption
    mkEnableOption
    mkIf
    mkPackageOption
    types
    ;
in

{
  options = {
    services.pingvin-share = {
      enable = mkEnableOption "Pingvin Share, a self-hosted file sharing platform";

      user = mkOption {
        type = types.str;
        default = "pingvin";
        description = ''
          User account under which Pingvin Share runs.
        '';
      };

      group = mkOption {
        type = types.str;
        default = "pingvin";
        description = ''
          Group under which Pingvin Share runs.
        '';
      };

      openFirewall = mkOption {
        type = types.bool;
        default = false;
        description = ''
          Whether to open the firewall for the port in {option}`services.pingvin-share.frontend.port`.
        '';
      };

      dataDir = mkOption {
        type = types.path;
        default = "/var/lib/pingvin-share";
        example = "/var/lib/pingvin";
        description = ''
          The path to the data directory in which Pingvin Share will store its data.
        '';
      };

      hostname = mkOption {
        type = types.str;
        default = "localhost:${toString cfg.backend.port}";
        defaultText = lib.literalExpression "localhost:\${options.services.pingvin-share.backend.port}";
        example = "pingvin-share.domain.tdl";
        description = ''
          The domain name of your instance. If null, the redirections will be made to localhost.
        '';
      };

      https = mkOption {
        type = types.bool;
        default = false;
        example = true;
        description = ''
          Whether to enable HTTPS for the domain.
        '';
      };

      backend = {
        package = mkPackageOption pkgs [
          "pingvin-share"
          "backend"
        ] { };

        port = mkOption {
          type = types.port;
          default = 8080;
          example = 9000;
          description = ''
            The port that the backend service of Pingvin Share will listen to.
          '';
        };
      };

      frontend = {
        package = mkPackageOption pkgs [
          "pingvin-share"
          "frontend"
        ] { };

        port = mkOption {
          type = types.port;
          default = 3000;
          example = 8000;
          description = ''
            The port that the frontend service of Pingvin Share will listen to.
          '';
        };
      };

      nginx = {
        enable = mkEnableOption "a Nginx reverse proxy for Pingvin Share.";
      };
    };
  };

  config = mkIf cfg.enable {

    users.groups = mkIf (cfg.group == "pingvin") { pingvin = { }; };

    users.users = mkIf (cfg.user == "pingvin") {
      pingvin = {
        group = cfg.group;
        description = "Pingvin Share daemon user";
        isSystemUser = true;
      };
    };

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

    systemd.services.pingvin-share-backend = {
      description = "Backend service of Pingvin Share, a self-hosted file sharing platform.";

      wantedBy = [
        "multi-user.target"
        "pingvin-share-frontend.service"
      ];
      before = [ "pingvin-share-frontend.service" ];
      after = [
        "network.target"
        "network-online.target"
      ];
      wants = [ "network-online.target" ];

      environment = {
        PRISMA_SCHEMA_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/schema-engine";
        PRISMA_QUERY_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/query-engine";
        PRISMA_QUERY_ENGINE_LIBRARY = "${pkgs.prisma-engines}/lib/libquery_engine.node";
        PRISMA_INTROSPECTION_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/introspection-engine";
        PRISMA_FMT_BINARY = "${pkgs.prisma-engines}/bin/prisma-fmt";
        BACKEND_PORT = toString cfg.backend.port;
        DATABASE_URL = "file:${cfg.dataDir}/pingvin-share.db?connection_limit=1";
        DATA_DIRECTORY = cfg.dataDir;
      };

      path = with pkgs; [
        cfg.backend.package
        openssl
        prisma-engines
      ];

      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        Type = "simple";
        Restart = "on-failure";
        ExecStartPre = [
          "${cfg.backend.package}/node_modules/.bin/prisma migrate deploy"
          "${cfg.backend.package}/node_modules/.bin/prisma db seed"
        ];
        ExecStart = "${cfg.backend.package}/node_modules/.bin/ts-node dist/src/main";
        StateDirectory = mkIf (cfg.dataDir == "/var/lib/pingvin-share") "pingvin-share";
        WorkingDirectory = cfg.backend.package;
      };
    };

    systemd.services.pingvin-share-frontend = {
      description = "Frontend service of Pingvin Share, a self-hosted file sharing platform.";

      wantedBy = [ "multi-user.target" ];
      wants = [
        "network-online.target"
        "pingvin-share-backend.service"
      ];
      after = [
        "network-online.target"
        "pingvin-share-backend.service"
      ];

      environment = {
        PORT = toString cfg.frontend.port;
        API_URL = "${if cfg.https then "https" else "http"}://${cfg.hostname}";
      };
      path = [ cfg.frontend.package ];

      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        Type = "simple";
        Restart = "on-failure";
        ExecStart = "${cfg.frontend.package}/node_modules/.bin/next start";
        StateDirectory = mkIf (cfg.dataDir == "/var/lib/pingvin-share") "pingvin-share";
        WorkingDirectory = cfg.frontend.package;
      };
    };

    services.nginx = mkIf cfg.nginx.enable {
      enable = lib.mkDefault true;
      virtualHosts."${cfg.hostname}" = {
        enableACME = cfg.https;
        forceSSL = cfg.https;

        locations."/" = {
          proxyPass = "http://localhost:${toString cfg.frontend.port}";
          recommendedProxySettings = true;
        };
        locations."/api" = {
          proxyPass = "http://localhost:${toString cfg.backend.port}";
          recommendedProxySettings = true;
        };
      };
    };
  };

  meta = {
    maintainers = with lib.maintainers; [ ratcornu ];
    doc = ./pingvin-share.md;
  };
}
