{ pkgs
, lib
, config
, ... }: with lib; let
  cfg = config.services.photoview;
in {
  options.services.photoview = {
    enable = mkEnableOption "photoview server";

    package = mkOption {
      type = with types; package;
      default = pkgs.photoview;
      description = "photoview package";
    };

    user = mkOption {
      type = with types; str;
      default = "photoview";
      description = "user under which photoview runs";
    };
    group = mkOption {
      type = with types; str;
      default = "photoview";
      description = "group under which photoview runs";
    };

    listen = {
      host = mkOption {
        type = with types; str;
        default = "127.0.0.1";
        description = "host to listen on";
      };
      port = mkOption {
        type = with types; port;
        default = 8000;
        description = "port to listen on";
      };
    };

    database = {
      driver = mkOption {
        type = with types; enum [ "sqlite" "mysql" "postgres" ];
        default = "sqlite";
        description = "database driver";
      };
      string = mkOption {
        type = with types; str;
        description = "database connection string";
      };
    };

    stateDir = mkOption {
      type = with types; str;
      default = "/var/lib/photoview";
      description = "path to photoview state directory";
    };

    cacheDir = mkOption {
      type = with types; str;
      default = "${cfg.stateDir}/media_cache";
      description = "path to photoview media cache";
    };

    secrets = mkOption {
      type = with types; nullOr str;
      default = null;
      description = "path to secrets environment file";
      example = ''
        MAPBOX_TOKEN=
      '';
    };
  };

  config = mkIf cfg.enable {
    systemd.services.photoview = {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      description = "Photoview - Photo gallery for self-hosted personal servers";
      environment = with cfg; {
        PHOTOVIEW_LISTEN_IP = listen.host;
        PHOTOVIEW_LISTEN_PORT = toString listen.port;
        PHOTOVIEW_MEDIA_CACHE = cacheDir;
        PHOTOVIEW_DATABASE_DRIVER = database.driver;
        "PHOTOVIEW_${toUpper database.driver}_URL" = database.string;
      };
      serviceConfig = {
        ExecStartPre = pkgs.writeShellScript "photoview-pre" ''
          rm -f "${cfg.stateDir}/data"
          ln -s "${cfg.package}/lib/photoview/data" "${cfg.stateDir}/data"
        '';
        ExecStart = "${cfg.package}/bin/photoview";
        WorkingDirectory = cfg.stateDir;
        User = cfg.user;
        Group = cfg.group;
        EnvironmentFile = mkIf (cfg.secrets != null) cfg.secrets;

        DevicePolicy = "closed";
        LockPersonality = true;
        MemoryDenyWriteExecute = true;
        NoNewPrivileges = true;
        PrivateTmp = true;
        PrivateUsers = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        ProcSubset = "all";
        ProtectSystem = "strict";
        RemoveIPC = true;
        ReadWritePaths = [ cfg.stateDir ];
        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
          "AF_NETLINK"
          "AF_UNIX"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [
          "@system-service"
          "~@privileged"
        ];
        UMask = "0077";
      };
    };

    users.users = mkIf (cfg.user == "photoview") {
      photoview = {
        description = "photoview service account";
        group = cfg.group;
        uid = config.ids.uids.photoview;
      };
    };

    users.groups = mkIf (cfg.group == "photoview") {
      photoview.gid = config.ids.gids.photoview;
    };

    ids.uids.photoview = 287;
    ids.gids.photoview = 287;
  };
}
