{ inputs }:
let
  inherit (inputs.nixpkgs.lib)
    listToAttrs
    nameValuePair
    ;
in
rec {
  # Create a service configuration for Traefik
  mkService =
    {
      name,
      url,
      loadBalancer ? { },
    }:
    {
      inherit name url;
      config = {
        loadBalancer = {
          servers = [ { inherit url; } ];
        }
        // loadBalancer;
      };
    };

  # Create a router configuration for Traefik
  mkRouter =
    {
      subdomain,
      domain ? "mjallen.dev",
      service,
      entryPoints ? [ "websecure" ],
      middlewares ? [
        "crowdsec"
        "whitelist-geoblock"
      ],
      priority ? null,
      rule ? null,
      tls ? {
        certResolver = "letsencrypt";
      },
    }:
    {
      inherit
        subdomain
        service
        entryPoints
        middlewares
        ;
      config = {
        inherit
          entryPoints
          service
          middlewares
          tls
          ;
        rule = if rule != null then rule else "Host(`${subdomain}.${domain}`)";
      }
      // (if priority != null then { inherit priority; } else { });
    };

  # Create both service and router for a simple reverse proxy setup
  mkReverseProxy =
    {
      name,
      subdomain,
      url,
      domain ? "mjallen.dev",
      entryPoints ? [ "websecure" ],
      middlewares ? [
        "crowdsec"
        "whitelist-geoblock"
      ],
      priority ? null,
      rule ? null,
      tls ? {
        certResolver = "letsencrypt";
      },
      loadBalancer ? { },
    }:
    {
      service = mkService {
        inherit name url loadBalancer;
      };
      router = mkRouter {
        inherit
          subdomain
          domain
          entryPoints
          middlewares
          priority
          rule
          tls
          ;
        service = name;
      };
    };

  # Convert a list of services to the format expected by Traefik module
  servicesToConfig =
    services: listToAttrs (map (service: nameValuePair service.name service.config) services);

  # Convert a list of routers to the format expected by Traefik module
  routersToConfig =
    routers: listToAttrs (map (router: nameValuePair router.subdomain router.config) routers);

  # Helper to create multiple reverse proxies at once
  mkReverseProxies =
    proxies:
    let
      results = map mkReverseProxy proxies;
      services = map (result: result.service) results;
      routers = map (result: result.router) results;
    in
    {
      services = servicesToConfig services;
      routers = routersToConfig routers;
      extraServices = services;
      extraRouters = map (router: {
        inherit (router) subdomain entryPoints middlewares;
        service = router.service;
      }) routers;
    };

  # Common middleware configurations
  middlewares = {
    # Authentication middleware
    auth = [ "authentik" ];

    # Basic security (default)
    basic = [
      "crowdsec"
      "whitelist-geoblock"
    ];

    # Internal only access
    internal = [
      "crowdsec"
      "whitelist-geoblock"
      "internal-ipallowlist"
    ];

    # WebSocket support
    websocket = [
      "crowdsec"
      "whitelist-geoblock"
      "onlyoffice-websocket"
    ];

    # Authenticated with basic security
    authBasic = [
      "crowdsec"
      "whitelist-geoblock"
      "authentik"
    ];
  };

  # Common service URL builders
  urls = {
    # Local container service
    container =
      containerName: port: "http://\${config.containers.${containerName}.localAddress}:${toString port}";

    # Local host service
    localhost = port: "http://127.0.0.1:${toString port}";

    # Network service
    network = ip: port: "http://${ip}:${toString port}";

    # Server IP service (using your server IP pattern)
    server = port: "http://\${serverIp}:${toString port}";
  };

  # Pre-configured reverse proxy templates
  templates = {
    # Standard web application
    webapp =
      { port, ... }@args:
      mkReverseProxy (
        {
          url = urls.localhost port;
          middlewares = middlewares.basic;
        }
        // args
      );

    # Authenticated web application
    authWebapp =
      { port, ... }@args:
      mkReverseProxy (
        {
          url = urls.localhost port;
          middlewares = middlewares.authBasic;
        }
        // args
      );

    # Container-based service
    containerService =
      { containerName, port, ... }@args:
      mkReverseProxy (
        {
          url = urls.container containerName port;
          middlewares = middlewares.basic;
        }
        // args
      );

    # Internal-only service
    internalService =
      { port, ... }@args:
      mkReverseProxy (
        {
          url = urls.localhost port;
          middlewares = middlewares.internal;
        }
        // args
      );
  };
}
