{
  config,
  lib,
  namespace,
  ...
}:
with lib;
let
  cfg = config.${namespace}.services.traefik;

  # Process extraServices into service configurations
  extraServiceConfigs =
    let
      makeService =
        service:
        nameValuePair service.name {
          loadBalancer.servers = [
            {
              url = service.url;
            }
          ];
        };
    in
    listToAttrs (map makeService cfg.extraServices);

  # Process extraRouters into router configurations
  extraRouterConfigs =
    let
      makeRouter =
        router:
        nameValuePair router.subdomain {
          entryPoints = router.entryPoints;
          rule = "Host(`${router.subdomain}.${domain}`)";
          service = router.service;
          middlewares = router.middlewares ++ [
            "crowdsec"
            "whitelist-geoblock"
          ];
          tls.certResolver = "letsencrypt";
        };
    in
    listToAttrs (map makeRouter cfg.extraRouters);

  # Process reverseProxies into service and router configurations
  reverseProxyServiceConfigs =
    let
      makeService = reverseProxy: nameValuePair reverseProxy.service.name reverseProxy.service.config;
    in
    listToAttrs (map makeService cfg.reverseProxies);

  reverseProxyRouterConfigs =
    let
      makeRouter = reverseProxy: nameValuePair reverseProxy.router.subdomain reverseProxy.router.config;
    in
    listToAttrs (map makeRouter cfg.reverseProxies);

  domain = "mjallen.dev";
  serverIp = "10.0.1.3";

  # Forward services
  authUrl = "http://${serverIp}:9000/outpost.goauthentik.io";

  authentikUrl = "http://${serverIp}:9000";
  cacheUrl = "http://${serverIp}:9012";
  cloudUrl = "https:/10.0.1.3:9988";
  # cloudUrl = "http://${config.containers.nextcloud.localAddress}:80";
  hassUrl = "http://10.0.1.4:8123";
  immichUrl = "http://${serverIp}:${toString config.services.immich.port}";
  jellyfinUrl = "http://${serverIp}:8096";
  jellyseerrUrl = "http://10.0.1.3:${toString config.services.jellyseerr.port}";
  lubeloggerUrl = "http://${serverIp}:6754";
  # onlyofficeUrl = "http://${config.containers.nextcloud.localAddress}:${toString config.containers.nextcloud.config.services.onlyoffice.port}";
  onlyofficeUrl = "http://10.0.1.3:9943";
  openWebUIUrl = "http://${serverIp}:8888";
  paperlessUrl = "http://${config.containers.paperless.localAddress}:${toString config.containers.paperless.config.services.paperless.port}";

  # Plugins
  traefikPlugins = {
    bouncer = {
      moduleName = "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin";
      version = "v1.4.5";
    };
    geoblock = {
      moduleName = "github.com/PascalMinder/geoblock";
      version = "v0.2.5";
    };
  };

  # Ports
  httpPort = 80;
  httpsPort = 443;
  traefikPort = 8080;
  metricsPort = 8082;

  forwardPorts = [
    httpPort
    httpsPort
    traefikPort
    metricsPort
  ];

  # misc
  letsEncryptEmail = "jalle008@proton.me";
  dataDir = "/media/nas/main/nix-app-data/traefik";
  authentikAddress = "http://${serverIp}:9000/outpost.goauthentik.io/auth/traefik";
in
{
  imports = [ ./options.nix ];

  config = mkIf cfg.enable {
    sops = {
      secrets = {
        "jallen-nas/traefik/crowdsec/lapi-key" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };

        "jallen-nas/traefik/crowdsec/capi-machine-id" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };

        "jallen-nas/traefik/crowdsec/capi-password" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
        "jallen-nas/traefik/cloudflare-dns-api-token" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
        "jallen-nas/traefik/cloudflare-zone-api-token" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
        "jallen-nas/traefik/cloudflare-api-key" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
        "jallen-nas/traefik/cloudflare-email" = {
          sopsFile = (lib.snowfall.fs.get-file "secrets/nas-secrets.yaml");
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
      };
      templates = {
        "traefik.env" = {
          content = ''
            CLOUDFLARE_DNS_API_TOKEN = ${config.sops.placeholder."jallen-nas/traefik/cloudflare-dns-api-token"}
            CLOUDFLARE_ZONE_API_TOKEN =  ${
              config.sops.placeholder."jallen-nas/traefik/cloudflare-zone-api-token"
            }
            CLOUDFLARE_API_KEY =  ${config.sops.placeholder."jallen-nas/traefik/cloudflare-api-key"}
            CLOUDFLARE_EMAIL =  ${config.sops.placeholder."jallen-nas/traefik/cloudflare-email"}
          '';
          owner = config.users.users.traefik.name;
          group = config.users.users.traefik.group;
          restartUnits = [ "traefik.service" ];
        };
      };
    };

    networking.firewall = {
      allowedTCPPorts = forwardPorts;
      allowedUDPPorts = forwardPorts;
    };

    services.traefik = {
      enable = true;
      dataDir = dataDir;
      group = "jallen-nas"; # group;
      environmentFiles = [ "${config.services.traefik.dataDir}/traefik.env" ]; # todo: sops

      staticConfigOptions = {
        entryPoints = {
          web = {
            address = ":${toString httpPort}";
            asDefault = true;
            http.redirections.entrypoint = {
              to = "websecure";
              scheme = "https";
            };
          };

          websecure = {
            address = ":${toString httpsPort}";
            asDefault = true;
            http.tls.certResolver = "letsencrypt";
          };

          metrics = {
            address = ":${toString metricsPort}"; # Port for metrics
          };
        };

        log = {
          level = "INFO";
        };

        metrics = {
          prometheus = {
            entryPoint = "metrics";
            addEntryPointsLabels = true;
            addServicesLabels = true;
            buckets = [
              0.1
              0.3
              1.2
              5.0
            ]; # Response time buckets
          };
        };

        certificatesResolvers.letsencrypt.acme = {
          email = letsEncryptEmail;
          storage = "${config.services.traefik.dataDir}/acme.json";
          dnsChallenge = {
            provider = "cloudflare";
            resolvers = [
              "1.1.1.1:53"
              "8.8.8.8:53"
            ];
          };
        };

        api.dashboard = true;
        # Access the Traefik dashboard on <Traefik IP>:8080 of your server
        api.insecure = true;

        experimental = {
          plugins = traefikPlugins;
        };
      };

      dynamicConfigOptions = {
        # udp = {
        #   services = {
        #     wireguard.loadBalancer.servers = [
        #       {
        #         url = "localhost:51820";
        #       }
        #     ];
        #   };
        #   routers = {
        #     wireguard = {
        #       entryPoints = [ "websecure" ];
        #       service = "wireguard";
        #     };
        #   };
        # };

        http = {

          serversTransports = {
            internal-https = {
              insecureSkipVerify = true;
            };
            attich1 = {
              serverName = "localhost";
              disableHTTP2 = true;
            };
          };

          middlewares = {
            authentik = {
              forwardAuth = {
                tls.insecureSkipVerify = true;
                address = authentikAddress;
                trustForwardHeader = true;
                authResponseHeaders = [
                  "X-authentik-username"
                  "X-authentik-groups"
                  "X-authentik-email"
                  "X-authentik-name"
                  "X-authentik-uid"
                  "X-authentik-jwt"
                  "X-authentik-meta-jwks"
                  "X-authentik-meta-outpost"
                  "X-authentik-meta-provider"
                  "X-authentik-meta-app"
                  "X-authentik-meta-version"
                ];
              };
            };
            onlyoffice-websocket = {
              headers.customrequestheaders = {
                X-Forwarded-Proto = "https";
              };
            };
            crowdsec = {
              plugin = {
                bouncer = {
                  enabled = true;
                  crowdsecLapiKeyFile = config.sops.secrets."jallen-nas/traefik/crowdsec/lapi-key".path;
                  crowdsecLapiScheme = "http";
                  crowdsecLapiHost = "localhost:8181";
                  crowdsecLapiPath = "/";
                  crowdsecLapiTLSInsecureVerify = false;
                  crowdsecCapiMachineIdFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-machine-id".path;
                  crowdsecCapiPasswordFile = config.sops.secrets."jallen-nas/traefik/crowdsec/capi-password".path;
                  crowdsecCapiScenarios = [ ];
                };
              };
            };
            whitelist-geoblock = {
              plugin = {
                geoblock = {
                  silentStartUp = false;
                  allowLocalRequests = true;
                  logLocalRequests = false;
                  logAllowedRequests = false;
                  logApiRequests = false;
                  api = "https://get.geojs.io/v1/ip/country/{ip}";
                  apiTimeoutMs = 500;
                  cacheSize = 25;
                  forceMonthlyUpdate = true;
                  allowUnknownCountries = false;
                  unknownCountryApiResponse = "nil";
                  blackListMode = false;
                  countries = [
                    "CA"
                    "US"
                  ];
                };
              };
            };
            internal-ipallowlist = {
              ipAllowList = {
                sourceRange = [
                  "127.0.0.1/32"
                  "10.0.1.0/24"
                ];
              };
            };
          };

          services = {
            auth.loadBalancer.servers = [
              {
                url = authUrl;
              }
            ];

            gitea.loadBalancer.servers = [
              {
                url = "http://10.0.1.3:3000";
              }
            ];

            actual.loadBalancer.servers = [
              {
                url = "http://10.0.1.3:3333";
              }
            ];

            matrix.loadBalancer.servers = [
              {
                url = "http://10.0.1.3:8448";
              }
            ];

            authentik.loadBalancer.servers = [
              {
                url = authentikUrl;
              }
            ];
            cache.loadBalancer = {
              servers = [
                {
                  url = cacheUrl;
                }
              ];
              serversTransport = "attich1";
            };
            chat.loadBalancer.servers = [
              {
                url = openWebUIUrl;
              }
            ];
            cloud.loadBalancer = {
              servers = [
                {
                  url = cloudUrl;
                }
              ];
              serversTransport = "internal-https";
            };
            hass.loadBalancer.servers = [
              {
                url = hassUrl;
              }
            ];
            immich.loadBalancer.servers = [
              {
                url = immichUrl;
              }
            ];
            jellyfin.loadBalancer.servers = [
              {
                url = jellyfinUrl;
              }
            ];
            jellyseerr.loadBalancer.servers = [
              {
                url = jellyseerrUrl;
              }
            ];
            lubelogger.loadBalancer.servers = [
              {
                url = lubeloggerUrl;
              }
            ];
            onlyoffice.loadBalancer = {
              servers = [
                {
                  url = onlyofficeUrl;
                }
              ];
              passHostHeader = true;
            };
            paperless.loadBalancer.servers = [
              {
                url = paperlessUrl;
              }
            ];
          }
          // extraServiceConfigs
          // reverseProxyServiceConfigs;

          routers = {
            auth = {
              entryPoints = [ "websecure" ];
              rule = "HostRegexp(`{subdomain:[a-z]+}.mjallen.dev`) && PathPrefix(`/outpost.goauthentik.io/`)";
              service = "auth";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              priority = 15;
              tls.certResolver = "letsencrypt";
            };

            gitea = {
              entryPoints = [ "websecure" ];
              rule = "Host(`gitea.${domain}`)";
              service = "gitea";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };

            actual = {
              entryPoints = [ "websecure" ];
              rule = "Host(`actual.${domain}`)";
              service = "actual";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };

            matrix = {
              entryPoints = [ "websecure" ];
              rule = "Host(`matrix.${domain}`)";
              service = "matrix";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };

            authentik = {
              entryPoints = [ "websecure" ];
              rule = "Host(`authentik.${domain}`)";
              service = "authentik";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            cache = {
              entryPoints = [ "websecure" ];
              rule = "Host(`cache.${domain}`)";
              service = "cache";
              middlewares = [ ];
              priority = 10;
              tls.certResolver = "letsencrypt";
            };
            cloud = {
              entryPoints = [ "websecure" ];
              rule = "Host(`cloud.${domain}`)";
              service = "cloud";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            hass = {
              entryPoints = [ "websecure" ];
              rule = "Host(`hass.${domain}`)";
              service = "hass";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
                "authentik"
              ];
              priority = 10;
              tls.certResolver = "letsencrypt";
            };
            immich = {
              entryPoints = [ "websecure" ];
              rule = "Host(`immich.${domain}`)";
              service = "immich";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            jellyfin = {
              entryPoints = [ "websecure" ];
              rule = "Host(`jellyfin.${domain}`)";
              service = "jellyfin";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            jellyseerr = {
              entryPoints = [ "websecure" ];
              rule = "Host(`jellyseerr.${domain}`)";
              service = "jellyseerr";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            lubelogger = {
              entryPoints = [ "websecure" ];
              rule = "Host(`lubelogger.${domain}`)";
              service = "lubelogger";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
              ];
              tls.certResolver = "letsencrypt";
            };
            onlyoffice = {
              entryPoints = [ "websecure" ];
              rule = "Host(`office.${domain}`)";
              service = "onlyoffice";
              middlewares = [
                "crowdsec"
                "whitelist-geoblock"
                "onlyoffice-websocket"
              ];
              tls.certResolver = "letsencrypt";
            };
          }
          // extraRouterConfigs
          // reverseProxyRouterConfigs;
        };
      };
    };
  };
}
