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

let
  cfg = config.services.librechat-oci;

  defaultImages = {
    librechat = pkgs.dockerTools.pullImage {
      imageName = "ghcr.io/danny-avila/librechat";
      imageDigest = "sha256:a46254938507971e0d4f7ed3f9d116bd9b118f4810b5b75eb716baf575645068";
      hash = "sha256-zevUN6vrs3hymwCGFmk/YXlUzYjN37H+EO5aLxYchyc=";
      finalImageName = "ghcr.io/danny-avila/librechat";
      finalImageTag = "v0.8.5";
    };

    meilisearch = pkgs.dockerTools.pullImage {
      imageName = "getmeili/meilisearch";
      imageDigest = "sha256:b839a48d008d4e67e57f78dcff5b21d5e8b8fa066bacd11f97824d6307abb0dd";
      hash = "sha256-+hypvjFDSdnnWAO4tARTnjNd/6HlF6pMg1L6UUffdYM=";
      finalImageName = "getmeili/meilisearch";
      finalImageTag = "v1.44.0";
    };

    mongodb = pkgs.dockerTools.pullImage {
      imageName = "mongo";
      imageDigest = "sha256:098862b1339f031900ca66cf8fef799e616d6324fa41b9a263f2ec899552c1ef";
      hash = "sha256-XuWnvcqAAkAGshdQtnngKOOJP2Bd33FXTbGHTRX3nUc=";
      finalImageName = "mongo";
      finalImageTag = "8.0.20";
    };

    vectordb = pkgs.dockerTools.pullImage {
      imageName = "pgvector/pgvector";
      imageDigest = "sha256:8809cfffff0082cf260c9ac752f1dd1afc77f6f0a55c4e6411321e78efc3d9a5";
      hash = "sha256-rc6gQLMzv8UOZVLmWKGUESNIo+iPf5DR7T79AmbzWc4=";
      finalImageName = "pgvector/pgvector";
      finalImageTag = "0.8.0-pg15-trixie";
    };

    ragApi = pkgs.dockerTools.pullImage {
      imageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite";
      imageDigest = "sha256:6dfb6832661ff9c26fa329c823ce266059e33567670a763e9ecb9b566b8daa68";
      hash = "sha256-k8pkEgbqT4NU2+2ZjdRFlfFvMUk/1p+pkysbELh95pM=";
      finalImageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite";
      finalImageTag = "latest";
    };
  };

  defaultEnv = {
    # Server
    HOST = "0.0.0.0";
    PORT = toString cfg.port;
    NO_INDEX = "true";
    TRUST_PROXY = "1";

    # Logging
    CONSOLE_JSON = "false";
    DEBUG_LOGGING = "true";
    DEBUG_CONSOLE = "false";
    AGENT_DEBUG_LOGGING = "false";
    DEBUG_OPENAI = "false";

    # Node
    NODE_MAX_OLD_SPACE_SIZE = "6144";

    # Search
    SEARCH = "true";
    MEILI_NO_ANALYTICS = "true";

    # Moderation
    OPENAI_MODERATION = "false";
    BAN_VIOLATIONS = "true";
    BAN_DURATION = "1000 * 60 * 60 * 2";
    BAN_INTERVAL = "20";
    LOGIN_VIOLATION_SCORE = "1";
    REGISTRATION_VIOLATION_SCORE = "1";
    CONCURRENT_VIOLATION_SCORE = "1";
    MESSAGE_VIOLATION_SCORE = "1";
    NON_BROWSER_VIOLATION_SCORE = "20";
    TTS_VIOLATION_SCORE = "0";
    STT_VIOLATION_SCORE = "0";
    FORK_VIOLATION_SCORE = "0";
    IMPORT_VIOLATION_SCORE = "0";
    FILE_UPLOAD_VIOLATION_SCORE = "0";
    LOGIN_MAX = "7";
    LOGIN_WINDOW = "5";
    REGISTER_MAX = "5";
    REGISTER_WINDOW = "60";
    LIMIT_CONCURRENT_MESSAGES = "true";
    CONCURRENT_MESSAGE_MAX = "2";
    LIMIT_MESSAGE_IP = "true";
    MESSAGE_IP_MAX = "40";
    MESSAGE_IP_WINDOW = "1";
    LIMIT_MESSAGE_USER = "false";
    MESSAGE_USER_MAX = "40";
    MESSAGE_USER_WINDOW = "1";
    ILLEGAL_MODEL_REQ_SCORE = "5";

    # Registration and login
    ALLOW_EMAIL_LOGIN = "true";
    ALLOW_REGISTRATION = "false";
    ALLOW_SOCIAL_LOGIN = "false";
    ALLOW_SOCIAL_REGISTRATION = "false";
    ALLOW_PASSWORD_RESET = "false";
    ALLOW_UNVERIFIED_EMAIL_LOGIN = "true";
    SESSION_EXPIRY = "1000 * 60 * 15";
    REFRESH_TOKEN_EXPIRY = "(1000 * 60 * 60 * 24) * 7";

    # OpenID
    OPENID_SCOPE = "openid profile email";
    OPENID_CALLBACK_URL = "/oauth/openid/callback";
    OPENID_AUTO_REDIRECT = "false";
    OPENID_USE_PKCE = "false";
    OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE = "user.read";

    # OAuth callback URLs
    DISCORD_CALLBACK_URL = "/oauth/discord/callback";
    FACEBOOK_CALLBACK_URL = "/oauth/facebook/callback";
    GITHUB_CALLBACK_URL = "/oauth/github/callback";
    GOOGLE_CALLBACK_URL = "/oauth/google/callback";
    APPLE_CALLBACK_URL = "/oauth/apple/callback";
    SAML_CALLBACK_URL = "/oauth/saml/callback";

    # Entra ID
    USE_ENTRA_ID_FOR_PEOPLE_SEARCH = "false";
    ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS = "false";
    OPENID_GRAPH_SCOPES = "User.Read,People.Read,GroupMember.Read.All";

    # Shared links
    ALLOW_SHARED_LINKS = "true";
    ALLOW_SHARED_LINKS_PUBLIC = "false";

    # UI
    APP_TITLE = "LibreChat";
    HELP_AND_FAQ_URL = "https://librechat.ai";

    # Flux
    FLUX_API_BASE_URL = "https://api.us1.bfl.ai";

    # Email
    EMAIL_PORT = "25";
    EMAIL_FROM = "noreply@librechat.ai";

    # Azure Blob Storage
    AZURE_STORAGE_PUBLIC_ACCESS = "false";
    AZURE_CONTAINER_NAME = "files";
  };

  mkImageOption =
    name: description:
    mkOption {
      type = types.package;
      default = defaultImages.${name};
      description = description;
    };

  inherit (lib)
    literalExpression
    mkEnableOption
    mkIf
    mkOption
    mkOverride
    optional
    types
    ;
in
{
  options.services.librechat-oci = {
    enable = mkEnableOption "LibreChat container with Podman.";

    images = {
      librechat = mkImageOption "librechat" "The LibreChat Docker image (`pkgs.dockerTools.pullImage`).";
      meilisearch = mkImageOption "meilisearch" "The Meilisearch Docker image (`pkgs.dockerTools.pullImage`).";
      mongodb = mkImageOption "mongodb" "The MongoDB Docker image (`pkgs.dockerTools.pullImage`).";
      vectordb = mkImageOption "vectordb" "The pgvector Docker image (`pkgs.dockerTools.pullImage`).";
      ragApi = mkImageOption "ragApi" "The LibreChat RAG API Docker image (`pkgs.dockerTools.pullImage`).";
    };

    externalUrl = mkOption {
      type = types.nullOr types.str;
      default = null;
      example = literalExpression ''"https://chat.example.com"'';
      description = "Public URL to configure for LibreChat (sets DOMAIN_CLIENT and DOMAIN_SERVER).";
    };

    port = mkOption {
      type = types.port;
      default = 3080;
      description = "Which port the LibreChat server listens on.";
    };

    meiliPort = mkOption {
      type = types.port;
      default = 7700;
      description = "Which port Meilisearch listens on.";
    };

    ragPort = mkOption {
      type = types.port;
      default = 8000;
      description = "Which port the RAG API listens on.";
    };

    environment = mkOption {
      default = { };
      type = types.attrsOf types.str;
      description = ''
        Extra environment variables for LibreChat.
        These are merged on top of the defaults and can override them.
        For secrets use <option>environmentFile</option> instead.
        See <https://docs.librechat.ai/docs/configuration/dotenv>.
      '';
    };

    environmentFile = mkOption {
      type = types.nullOr types.path;
      default = null;
      example = literalExpression "config.sops.templates.librechat-env.path";
      description = ''
        Environment file passed to the LibreChat, Meilisearch, and RAG API
        containers. Use this for secrets such as JWT_SECRET, CREDS_KEY,
        MEILI_MASTER_KEY, and API keys.
      '';
    };
  };

  config = mkIf cfg.enable {
    virtualisation.podman = {
      enable = true;
      autoPrune.enable = true;
      dockerCompat = true;
    };

    networking.firewall.interfaces =
      let
        matchAll = if !config.networking.nftables.enable then "podman+" else "podman*";
      in
      {
        "${matchAll}".allowedUDPPorts = [ 53 ];
      };

    virtualisation.oci-containers.backend = "podman";

    virtualisation.oci-containers.containers.librechat-mongodb = {
      image = with cfg.images.mongodb; "${imageName}:${imageTag}";
      imageFile = cfg.images.mongodb;
      cmd = [
        "mongod"
        "--noauth"
      ];
      volumes = [
        "librechat_mongodb_data:/data/db:rw"
      ];
      log-driver = "journald";
      extraOptions = [
        "--network-alias=mongodb"
        "--network=librechat_default"
      ];
    };

    virtualisation.oci-containers.containers.librechat-meilisearch = {
      image = with cfg.images.meilisearch; "${imageName}:${imageTag}";
      imageFile = cfg.images.meilisearch;
      environment = {
        MEILI_NO_ANALYTICS = "true";
        MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}";
      };
      environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
      volumes = [
        "librechat_meili_data:/meili_data:rw"
      ];
      log-driver = "journald";
      extraOptions = [
        "--network-alias=meilisearch"
        "--network=librechat_default"
      ];
    };

    virtualisation.oci-containers.containers.librechat-vectordb = {
      image = with cfg.images.vectordb; "${imageName}:${imageTag}";
      imageFile = cfg.images.vectordb;
      environment = {
        POSTGRES_DB = "mydatabase";
        POSTGRES_USER = "myuser";
        POSTGRES_PASSWORD = "mypassword";
      };
      volumes = [
        "librechat_pgdata:/var/lib/postgresql/data:rw"
      ];
      log-driver = "journald";
      extraOptions = [
        "--network-alias=vectordb"
        "--network=librechat_default"
      ];
    };

    virtualisation.oci-containers.containers.librechat-rag-api = {
      image = with cfg.images.ragApi; "${imageName}:${imageTag}";
      imageFile = cfg.images.ragApi;
      environment = {
        DB_HOST = "vectordb";
        RAG_PORT = toString cfg.ragPort;
      };
      environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
      dependsOn = [ "librechat-vectordb" ];
      log-driver = "journald";
      extraOptions = [
        "--network-alias=rag_api"
        "--network=librechat_default"
      ];
    };

    virtualisation.oci-containers.containers.librechat = {
      image = with cfg.images.librechat; "${imageName}:${imageTag}";
      imageFile = cfg.images.librechat;
      environment =
        defaultEnv
        // {
          MONGO_URI = "mongodb://mongodb:27017/LibreChat";
          MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}";
          RAG_PORT = toString cfg.ragPort;
          RAG_API_URL = "http://rag_api:${toString cfg.ragPort}";
          DOMAIN_CLIENT =
            if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}";
          DOMAIN_SERVER =
            if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}";
        }
        // cfg.environment;
      environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile;
      volumes = [
        "librechat_images:/app/client/public/images:rw"
        "librechat_uploads:/app/uploads:rw"
        "librechat_logs:/app/logs:rw"
      ];
      ports = [
        "127.0.0.1:${toString cfg.port}:${toString cfg.port}/tcp"
      ];
      dependsOn = [
        "librechat-mongodb"
        "librechat-rag-api"
      ];
      log-driver = "journald";
      extraOptions = [
        "--network-alias=api"
        "--network=librechat_default"
      ];
    };

    systemd.services.podman-librechat-mongodb = {
      serviceConfig.Restart = mkOverride 90 "always";
      after = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_mongodb_data.service"
      ];
      requires = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_mongodb_data.service"
      ];
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-librechat-meilisearch = {
      serviceConfig.Restart = mkOverride 90 "always";
      after = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_meili_data.service"
      ];
      requires = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_meili_data.service"
      ];
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-librechat-vectordb = {
      serviceConfig.Restart = mkOverride 90 "always";
      after = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_pgdata.service"
      ];
      requires = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_pgdata.service"
      ];
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-librechat-rag-api = {
      serviceConfig.Restart = mkOverride 90 "always";
      after = [
        "podman-network-librechat_default.service"
        "podman-librechat-vectordb.service"
      ];
      requires = [
        "podman-network-librechat_default.service"
        "podman-librechat-vectordb.service"
      ];
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-librechat = {
      serviceConfig.Restart = mkOverride 90 "always";
      after = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_images.service"
        "podman-volume-librechat_uploads.service"
        "podman-volume-librechat_logs.service"
        "podman-librechat-mongodb.service"
        "podman-librechat-meilisearch.service"
        "podman-librechat-rag-api.service"
      ];
      requires = [
        "podman-network-librechat_default.service"
        "podman-volume-librechat_images.service"
        "podman-volume-librechat_uploads.service"
        "podman-volume-librechat_logs.service"
        "podman-librechat-mongodb.service"
        "podman-librechat-meilisearch.service"
        "podman-librechat-rag-api.service"
      ];
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-network-librechat_default = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
        ExecStop = "podman network rm -f librechat_default";
      };
      script = ''
        podman network inspect librechat_default || podman network create librechat_default
      '';
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_mongodb_data = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_mongodb_data || podman volume create librechat_mongodb_data";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_meili_data = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_meili_data || podman volume create librechat_meili_data";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_pgdata = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_pgdata || podman volume create librechat_pgdata";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_images = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_images || podman volume create librechat_images";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_uploads = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_uploads || podman volume create librechat_uploads";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.services.podman-volume-librechat_logs = {
      path = [ pkgs.podman ];
      serviceConfig = {
        Type = "oneshot";
        RemainAfterExit = true;
      };
      script = "podman volume inspect librechat_logs || podman volume create librechat_logs";
      partOf = [ "podman-compose-librechat-root.target" ];
      wantedBy = [ "podman-compose-librechat-root.target" ];
    };

    systemd.targets.podman-compose-librechat-root = {
      unitConfig.Description = "Root target generated by compose2nix.";
      wantedBy = [ "multi-user.target" ];
    };
  };
}
