{ config, ... }:
let
  settings = import ../settings.nix;
  domain = "mjallen.dev";
  serverIp = settings.hostAddress;

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

  actualUrl = "http://${config.containers.actual.localAddress}:${toString config.containers.actual.config.services.actual.settings.port}";
  authentikUrl = "http://${serverIp}:9000";
  cacheUrl = "http://${serverIp}:9012";
  cloudUrl = "http://${config.containers.nextcloud.localAddress}:80";
  giteaUrl = "http://${config.containers.gitea.localAddress}:${toString config.containers.gitea.config.services.gitea.settings.server.HTTP_PORT}";
  hassUrl = "http://homeassistant.local:8123";
  immichUrl = "http://${serverIp}:${toString config.services.immich.port}";
  jellyfinUrl = "http://${serverIp}:8096";
  jellyseerrUrl = "http://${config.containers.jellyseerr.localAddress}:${toString config.containers.jellyseerr.config.services.jellyseerr.port}";
  lubeloggerUrl = "http://${serverIp}:6754";
  onlyofficeUrl = "http://${config.containers.nextcloud.localAddress}:${toString config.containers.nextcloud.config.services.onlyoffice.port}";
  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.2";
    };
    geoblock = {
      moduleName = "github.com/PascalMinder/geoblock";
      version = "v0.2.5";
    };
  };

  crowdsecAppsecHost = "${serverIp}:7422";
  crowdsecLapiKeyFile = config.sops.secrets."jallen-nas/traefik/crowdsec-lapi-key".path;

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

  forwardPorts = [
      httpPort
      httpsPort
      traefikPort
      metricsPort
  ];

  # misc
  letsEncryptEmail = "jalle008@proton.me";
  dataDir = "/media/nas/ssd/nix-app-data/traefik";
  authentikAddress = "http://${serverIp}:9000/outpost.goauthentik.io/auth/traefik";
in
{
  sops = {
    secrets = {
      "jallen-nas/traefik/crowdsec-lapi-key" = {
        owner = config.users.users.traefik.name;
        group = config.users.users.traefik.group;
        restartUnits = [ "traefik.service" ];
      };
      "jallen-nas/traefik/cloudflare-dns-api-token" = { };
      "jallen-nas/traefik/cloudflare-zone-api-token" = { };
      "jallen-nas/traefik/cloudflare-api-key" = { };
      "jallen-nas/traefik/cloudflare-email" = { };
    };
    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 = {
      http = {
        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 = {
                crowdsecAppsecEnabled = true;
                crowdsecAppsecHost = crowdsecAppsecHost;
                crowdsecAppsecFailureBlock = true;
                crowdsecAppsecUnreachableBlock = true;
                crowdsecLapiKeyFile = crowdsecLapiKeyFile;
              };
            };
          };
          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;
            }
          ];

          actual.loadBalancer.servers = [
            {
              url = actualUrl;
            }
          ];
          authentik.loadBalancer.servers = [
            {
              url = authentikUrl;
            }
          ];
          cache.loadBalancer.servers = [
            {
              url = cacheUrl;
            }
          ];
          chat.loadBalancer.servers = [
            {
              url = openWebUIUrl;
            }
          ];
          cloud.loadBalancer.servers = [
            {
              url = cloudUrl;
            }
          ];
          gitea.loadBalancer.servers = [
            {
              url = giteaUrl;
            }
          ];
          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;
            }
          ];
          paperless.loadBalancer.servers = [
            {
              url = paperlessUrl;
            }
          ];
        };

        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";
          };

          actual = {
            entryPoints = [ "websecure" ];
            rule = "Host(`actual.${domain}`)";
            service = "actual";
            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 = [ "crowdsec" "whitelist-geoblock" ];
            priority = 10;
            tls.certResolver = "letsencrypt";
          };
          cloud = {
            entryPoints = [ "websecure" ];
            rule = "Host(`cloud.${domain}`)";
            service = "cloud";
            middlewares = [ "crowdsec" "whitelist-geoblock" ];
            tls.certResolver = "letsencrypt";
          };
          gitea = {
            entryPoints = [ "websecure" ];
            rule = "Host(`gitea.${domain}`)";
            service = "gitea";
            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";
          };
        };
      };
    };
  };
}
