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

{
  imports = [
    ./mautrix-telegram.nix
    ./mautrix-services.nix
    # ./mautrix-discord.nix
    # ./mautrix-whatsapp.nix
    # ./mautrix-slack.nix
    # ./mautrix-signal.nix
    ./mjolnir.nix
    ./heisenbridge.nix
    ./compress-state-service.nix
  ];
  age.secrets.synapse-database-password.file = "${self}/secrets/synapse-database-password.age";
  age.secrets.synapse-database-password.owner = "matrix-synapse";
  age.secrets.synapse-config.file = "${self}/secrets/synapse-config.age";
  age.secrets.synapse-config.owner = "matrix-synapse";

  systemd.services.matrix-synapse.serviceConfig.TimeoutStartSec = "10min";
  
  services.matrix-synapse = {
    enable = true;
    settings = {
      max_upload_size = "100M";
      server_name = "sealight.xyz";
      federation_sender_instances = [];
      listeners = [
        {
          port = 8448;
          tls = false;
          resources = [{
            compress = true;
            names = [ "client" "federation" ];
          }];
        }
        {
          port = 9090;
          type = "metrics";
          bind_addresses = [ "0.0.0.0" ];
          resources = [{
            compress = false;
            names = [ ];
          }];
          tls = false;
        }
      ];

      app_service_config_files = [
        #   The registration file is automatically generated after starting the appservice for the first time.
        #   cp /var/lib/matrix-appservice-discord/discord-registration.yaml /var/lib/matrix-synapse/
        #   chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/discord-registration.yaml
        # "/var/lib/matrix-synapse/telegram-registration.yaml"
        "/var/lib/matrix-synapse/signal-registration.yaml"
        #"/var/lib/matrix-as-whatsapp/whatsapp-registration.yaml"
        "/var/lib/matrix-as-discord/discord-registration.yaml"
        # "/var/lib/matrix-synapse/slack-registration.yaml"
        # "/var/lib/matrix-synapse/discord-registration.yaml"
        # "/var/lib/matrix-synapse/whatsapp-registration.yaml"
      ];
      turn_uris = [
        "turn:turn.sealight.xyz:3478?transport=udp"
        "turn:turn.sealight.xyz:3478?transport=tcp"
      ];
      # turn_shared_secret = config.services.coturn.static-auth-secret;
      # Example config (saved as secret??)
      # ''
      #   max_upload_size: "50M"
      #   use_presence: false
      #   registration_shared_secret: "hD9HQGTTDxp0mQsQ5JDsfudWMDiubmZENOgPchIvfBvUlPxlvQSvjoO4wn2L1seU"; 
      #   enable_registration_without_verification: true
      # '';
      enable_metrics = true;
      enable_registration = false;
      database = {
        name = "psycopg2";
        args.passfile = "/run/agenix/synapse-database-password";
      };
    };
    extraConfigFiles = [ "/run/agenix/synapse-config" ];
    ## coturn based TURN server integration (TURN server setup mentioned later),
    ## shared secret generated while configuring coturn
    ## and reused here (power of Nix being a real programming language)
  };

  # services.coturn = {
  #   enable = true;
  #   use-auth-secret = true;
  #   static-auth-secret = "jXW1ohIq6wM3NB00xeME3uBihY85xjpkhGoyzBIdwhOpj7gjyxXZu1fwp1lUiYwJ"; # TODO agenix
  #   realm = "turn.sealight.xyz";
  #   min-port = 49111;
  #   max-port = 51111;
  #   no-cli = true;
  #   no-tcp-relay = true;
  #   no-tls = true;
  #   cert = "${config.security.acme.certs."turn.sealight.xyz".directory}/full.pem";
  #   pkey = "${config.security.acme.certs."turn.sealight.xyz".directory}/key.pem";
  #   extraConfig = ''
  #     verbose
  #     user-quota=12
  #     total-quota=1200
  #     denied-peer-ip=10.0.0.0-10.255.255.255
  #     denied-peer-ip=192.168.0.0-192.168.255.255
  #     denied-peer-ip=172.16.0.0-172.31.255.255
  #     denied-peer-ip=0.0.0.0-0.255.255.255
  #     denied-peer-ip=100.64.0.0-100.127.255.255
  #     denied-peer-ip=127.0.0.0-127.255.255.255
  #     denied-peer-ip=169.254.0.0-169.254.255.255
  #     denied-peer-ip=192.0.0.0-192.0.0.255
  #     denied-peer-ip=192.0.2.0-192.0.2.255
  #     denied-peer-ip=192.88.99.0-192.88.99.255
  #     denied-peer-ip=198.18.0.0-198.19.255.255
  #     denied-peer-ip=198.51.100.0-198.51.100.255
  #     denied-peer-ip=203.0.113.0-203.0.113.255
  #     denied-peer-ip=240.0.0.0-255.255.255.255
  #   '';
  # };

  # security.acme.certs.${config.services.coturn.realm} = {
  #   /* insert here the right configuration to obtain a certificate */
  #   webroot = "/var/lib/acme/acme-challenge/";
  #   email = "anish+acme@lakhwara.com";
  #   postRun = "systemctl restart coturn.service";
  #   group = "turnserver";
  # };

  # TODO fix up jitsi bridge stuff
  ## services.jitsi-meet = {
  ##   enable = true;
  ##   hostName = "jitsi.sealight.xyz";
  ## };
  ## services.jitsi-videobridge.enable = true;

  services.postgresql = {
    enable = true;
    package = pkgs.postgresql_14;
    ## postgresql user and db name remains in the
    ## service.matrix-synapse.database_args setting which
    ## by default is matrix-synapse
    # TODO agenix
    initialScript = pkgs.writeText "synapse-init.sql" ''
      CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD "s0m3s3cur3p455w0rdth4tisch4ng3d"; 
      CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
          TEMPLATE template0
          LC_COLLATE = "C"
          LC_CTYPE = "C";
    '';
    authentication = ''
      local matrix-synapse all ident map=matrix-synapse-users
    '';
    identMap = # Map the matrix-synapse user to postgresql
      ''
        matrix-synapse-users matrix-synapse matrix-synapse
      '';
  };

  networking.firewall =
    let
      range = with config.services.coturn; [{
        from = min-port;
        to = max-port;
      }];
    in
    {
      enable = true;
      allowedUDPPortRanges = range; # coturn
      allowedTCPPortRanges = range;
      allowedTCPPorts = [
        22 # SSH
        8448 # Matrix federation
        8008
        80
        443
        3478 # Coturn service
        5349 # Coturn service
        9090 # Synapse Metrics
      ];
      allowedUDPPorts = [
        3478
        5349 # Coturn service
      ];
    };

  nixpkgs.overlays = [
    (self: super: {
      element-web = super.element-web.override {
        conf = {
          default_server_config = {
            "m.homeserver" = {
              "base_url" = "https://chat.sealight.xyz";
              "server_name" = "sealight.xyz";
            };
            "m.identity_server" = {
              "base_url" = "https://vector.im";
            };
          };

          ## jitsi will be setup later,
          ## but we need to add to Riot configuration
          jitsi.preferredDomain = "jitsi.sealight.xyz";
        };
      };
    })
  ];

  services.nginx = {
    enable = true;
    virtualHosts = {
      "sealight.xyz" = {
        forceSSL = true;
        enableACME = true;
        locations."/" = {
          root = "/var/www/sealight.xyz";
        };
        locations."/_matrix" = {
          proxyPass = "http://127.0.0.1:8448";
          extraConfig = ''
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
          '';
        };
        # locations."/slackbridge" = {
        #   proxyPass = "http://localhost:9899";
        # };
      };
      ## virtual host for Synapse
      "chat.sealight.xyz" = {
        forceSSL = true;
        enableACME = true;
        locations."/" = {
          proxyPass = "http://localhost:8448";
          extraConfig = ''
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
          '';
        };
      };
      ## virtual host for Riot/Web
      "element.sealight.xyz" = {
        forceSSL = true;
        enableACME = true;
        ## root points to the element-web package content, also configured via Nix
        locations."/" = {
          root = pkgs.element-web;
        };
      };
      # ${config.services.jitsi-meet.hostName} = {
      #  enableACME = true; # TODO
      #  forceSSL = true; # TODO
      # };
    };
    ## other nginx specific best practices
    recommendedGzipSettings = true;
    recommendedOptimisation = true;
    recommendedTlsSettings = true;
  };

}
