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

with lib;

let
  domaine = "nyanlout.re";

  riot_port = 52345;
  pgmanage_port = 52347;
  max_port = 52348;
  musique_port = 52349;
  factorio_port = 52351;
  airsonic_port = 4040;
in

{
  imports = [
    ../../services/haproxy-acme.nix
    ../../services/mail-server.nix
    ../../services/site-musique.nix
    ../../services/site-max.nix
    ../../services/auto-pr.nix
    ../../services/python-ci.nix
    ../../services/sdtdserver.nix
    ../../containers/vsftpd.nix
    /mnt/secrets/factorio_secrets.nix
  ];

  nixpkgs.overlays = [
    (import ../../overlays/dogetipbot-telegram.nix)
  ];

  services = {

    fail2ban.enable = true;

    smartd = {
      enable = true;
      defaults.monitored = "-a -o on -s (S/../.././02|L/../../1/04)";
      notifications.mail = {
        enable = true;
        recipient = "paul@nyanlout.re";
      };
    };

    fstrim.enable = true;

    haproxy-acme = {
      enable = true;
      domaine = domaine;
      services = {
        "grafana.${domaine}" = { ip = "127.0.0.1"; port = 3000; auth = true; };
        "emby.${domaine}" = { ip = "127.0.0.1"; port = 8096; auth = false; };
        "radarr.${domaine}" = { ip = "127.0.0.1"; port = 7878; auth = true; extraAcls = "acl API path_beg /api\n"; aclBool = "!AUTH_OK !API"; };
        "sonarr.${domaine}" = { ip = "127.0.0.1"; port = 8989; auth = true; extraAcls = "acl API path_beg /api\n"; aclBool = "!AUTH_OK !API"; };
        "transmission.${domaine}" = { ip = "127.0.0.1"; port = 9091; auth = true; };
        "syncthing.${domaine}" = { ip = "127.0.0.1"; port = 8384; auth = true; };
        "jackett.${domaine}" = { ip = "127.0.0.1"; port = 9117; auth = true; };
        "searx.${domaine}" = { ip = "127.0.0.1"; port = 8888; auth = false; };
        "riot.${domaine}" = { ip = "127.0.0.1"; port = riot_port; auth = false; };
        "matrix.${domaine}" = { ip = "127.0.0.1"; port = 8008; auth = false; };
        "pgmanage.${domaine}" = { ip = "127.0.0.1"; port = pgmanage_port; auth = true; };
        "gitea.${domaine}" = { ip = "127.0.0.1"; port = 3001; auth = false; };
        "ci.${domaine}" = { ip = "127.0.0.1"; port = 52350; auth = false; };
        "factorio.${domaine}" = { ip = "127.0.0.1"; port = factorio_port; auth = false; };
        "airsonic.${domaine}" = { ip = "127.0.0.1"; port = airsonic_port; auth = false; };
      };
    };

    mailserver = {
      enable = true;
      domaine = domaine;
    };

    influxdb = {
      enable = true;
      dataDir = "/var/db/influxdb";
    };

    telegraf = {
      enable = true;
      extraConfig = {
        inputs = {
          zfs = { poolMetrics = true; };
          net = { interfaces = [ "eno1" "eno2" "eno3" "eno4" ]; };
          netstat = {};
          cpu = { totalcpu = true; };
          kernel = {};
          mem = {};
          processes = {};
          system = {};
          disk = {};
          ipmi_sensor = { path = "${pkgs.ipmitool}/bin/ipmitool"; };
          smart = {
            path = "${pkgs.writeShellScriptBin "smartctl" "/run/wrappers/bin/sudo ${pkgs.smartmontools}/bin/smartctl $@"}/bin/smartctl";
          };
          exec= [
            { commands = [
              "${pkgs.python}/bin/python ${
                  pkgs.fetchgit {
                    url = "https://gitlab.com/nyanloutre/tplink-smartplug.git";
                    rev = "a0996112fc451b76448589698de440ad5fd6ea79";
                    sha256 = "1f1625g7rfsddgk428g76p8fr7vz5gfhq3f452q17bjni3rf2pj3";
                  }
                }/tplink_smartplug.py -t 10.30.50.7 -c energy"
              ];
              data_format = "json";
              name_suffix = "_tplink-smartplug";
            }
            {
              commands = [
                "${pkgs.python3}/bin/python ${pkgs.writeText "zpool.py" ''
                  import json
                  from subprocess import check_output

                  columns = ["NAME", "SIZE", "ALLOC", "FREE", "EXPANDSZ", "FRAG", "CAP", "DEDUP", "HEALTH", "ALTROOT"]
                  health = {'ONLINE':0, 'DEGRADED':11, 'OFFLINE':21, 'UNAVAIL':22, 'FAULTED':23, 'REMOVED':24}

                  stdout = check_output(["${pkgs.zfs}/bin/zpool", "list", "-Hp"],encoding='UTF-8').split('\n')
                  parsed_stdout = list(map(lambda x: dict(zip(columns,x.split('\t'))), stdout))[:-1]

                  for pool in parsed_stdout:
                    for item in pool:
                      if item in ["SIZE", "ALLOC", "FREE", "FRAG", "CAP"]:
                        pool[item] = int(pool[item])
                      if item in ["DEDUP"]:
                        pool[item] = float(pool[item])
                      if item == "HEALTH":
                        pool[item] = health[pool[item]]

                  print(json.dumps(parsed_stdout))
                ''}"
              ];
              tag_keys = [ "NAME" ];
              data_format = "json";
              name_suffix = "_python_zpool";
            }
          ];
        };
        outputs = {
          influxdb = { database = "telegraf"; urls = [ "http://localhost:8086" ]; };
        };
      };
    };

    udev.extraRules = ''
      KERNEL=="ipmi*", MODE="660", OWNER="telegraf"
    '';

    grafana = {
      enable = true;
      addr = "127.0.0.1";
      dataDir = "/var/lib/grafana";
      extraOptions = {
        SERVER_ROOT_URL = "https://grafana.${domaine}";
        SMTP_ENABLED = "true";
        SMTP_FROM_ADDRESS = "grafana@${domaine}";
        SMTP_SKIP_VERIFY = "true";
        AUTH_DISABLE_LOGIN_FORM = "true";
        AUTH_DISABLE_SIGNOUT_MENU = "true";
        AUTH_ANONYMOUS_ENABLED = "true";
        AUTH_ANONYMOUS_ORG_ROLE = "Admin";
        AUTH_BASIC_ENABLED = "false";
      };
    };

    emby = {
      enable = true;
      dataDir = "/var/lib/emby/ProgramData-Server";
    };

    slimserver = {
      enable = true;
      dataDir = "/var/lib/slimserver";
    };

    syncthing = {
      enable = true;
      dataDir = "/var/lib/syncthing";
      openDefaultPorts = true;
    };

    nfs.server = {
      enable = true;
      exports = ''
        /mnt/medias  10.30.0.0/16(ro,no_root_squash)
        /exports/steam  10.30.0.0/16(rw,async,no_root_squash)
      '';
      statdPort = 4000;
      lockdPort = 4001;
      mountdPort = 4002;
    };

    transmission = {
      enable = true;
      home = "/var/lib/transmission";
      settings = {
        rpc-bind-address = "127.0.0.1";
        rpc-host-whitelist = "*";
        rpc-whitelist-enabled = false;
      };
    };

    radarr.enable = true;
    sonarr.enable = true;
    jackett.enable = true;

    searx.enable = true;

    nginx = {
      enable = true;
      virtualHosts = {
        "riot" = {
          listen = [ { addr = "127.0.0.1"; port = riot_port; } ];
          locations = { "/" = { root = pkgs.riot-web; }; };
        };
        "factorio" = {
          listen = [ { addr = "127.0.0.1"; port = factorio_port; } ];
          locations = { "/" = { root = "/var/www/factorio"; }; };
        };
      };
    };

    postgresql.enable = true;

    matrix-synapse = {
      enable = true;
      enable_registration = true;
      server_name = "nyanlout.re";
      listeners = [
        { # federation
          bind_address = "";
          port = 8448;
          resources = [
            { compress = true; names = [ "client" "webclient" ]; }
            { compress = false; names = [ "federation" ]; }
          ];
          tls = true;
          type = "http";
          x_forwarded = false;
        }
        { # client
          bind_address = "127.0.0.1";
          port = 8008;
          resources = [
            { compress = true; names = [ "client" "webclient" ]; }
          ];
          tls = false;
          type = "http";
          x_forwarded = true;
        }
      ];
      max_upload_size = "100M";
      database_type = "psycopg2";
      database_args = {
        database = "matrix-synapse";
      };
      tls_private_key_path = "/var/lib/acme/${domaine}/key.pem";
      tls_certificate_path = "/var/lib/acme/${domaine}/fullchain.pem";
      url_preview_enabled = true;
      logConfig = ''
        version: 1

        formatters:
            journal_fmt:
                format: '%(name)s: [%(request)s] %(message)s'

        filters:
            context:
                (): synapse.util.logcontext.LoggingContextFilter
                request: ""

        handlers:
            journal:
                class: systemd.journal.JournalHandler
                formatter: journal_fmt
                filters: [context]
                SYSLOG_IDENTIFIER: synapse

        root:
            level: WARNING
            handlers: [journal]

        disable_existing_loggers: False
      '';
    };

    pgmanage = {
      enable = true;
      port = pgmanage_port;
      connections = {
        localhost = "hostaddr=127.0.0.1 port=5432 dbname=postgres";
      };
    };

    borgbackup.jobs = {
      loutre = {
        paths = [
          "/var/certs"
          "/var/dkim"
          "/var/lib/emby"
          "/var/lib/gitea"
          "/var/lib/grafana"
          "/var/lib/jackett"
          "/var/lib/matrix-synapse"
          "/var/lib/postgresql/.zfs/snapshot/borgsnap"
          "/var/lib/radarr"
          "/var/lib/sonarr"
          "/var/lib/syncthing"
          "/var/lib/transmission"
          "/mnt/medias/musique"
          "/mnt/medias/torrent/lidarr"
          "/mnt/medias/torrent/musique"
          "/var/sieve"
          "/var/vmail"
        ];
        repo = "/mnt/backup/borg";
        encryption = {
          mode = "repokey-blake2";
          passCommand = "cat /mnt/secrets/borgbackup_loutre_encryption_pass";
        };
        startAt = "weekly";
        prune.keep = {
          within = "1d";
          weekly = 4;
          monthly = 12;
        };
        preHook = "${pkgs.zfs}/bin/zfs snapshot loutrepool/var/postgresql@borgsnap";
        postHook = ''
          ${pkgs.zfs}/bin/zfs destroy loutrepool/var/postgresql@borgsnap
          if [[ $exitStatus == 0 ]]; then
            ${pkgs.rclone}/bin/rclone --config /mnt/secrets/rclone_loutre.conf sync -v $BORG_REPO BackupStorage:loutre
          fi
        '';
      };
    };

    borgbackup.repos = {
      diskstation = {
        authorizedKeys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDllbxON66dBju7sMnhX8/E0VRo3+PDYvDsHP0/FK+h8JHol4+pouLmI7KIDKYOJmSuom283OqnyZOMqk+RShTwWIFm9hOd2R9aj45Zrd9jPW2APOCec/Epgogj0bwBnc0l2v6qxkxaBMgL5DnAQ+E00uvL1UQpK8c8j4GGiPlkWJD6Kf+pxmnfH1TIm+J2XCwl0oeCkSK/Frd8eM+wCraMSzoaGiEcfMz2jK8hxDWjDxX7epU0ELF22BVCuyN8cYRoFTnV88E38PlaqsOqD5ePkxk425gDh7j/C06f8QKgnasVH2diixo92kYSd7i/RmfeXDDwAD5xqUvODczEuIdt root@DiskStation" ];
        path = "/mnt/backup_loutre/diskstation_borg";
        user = "synology";
      };
    };

    gitea = {
      enable = true;
      cookieSecure = true;
      httpPort = 3001;
      rootUrl = "https://gitea.nyanlout.re/";
      database = {
        type = "postgres";
        port = 5432;
        passwordFile = "/mnt/secrets/gitea_database_passwordFile";
      };
      log.level = "Warn";
      extraConfig = ''
        [ui]
        DEFAULT_THEME = arc-green

        [service]
        DISABLE_REGISTRATION = true
      '';
    };

    site-musique = {
      enable = true;
      port = musique_port;
      domaine = "musique-meyenheim.fr";
    };

    site-max = {
      enable = true;
      port = max_port;
      domaine = "maxspiegel.fr";
    };

    auto-pr.enable = true;

    python-ci.enable = true;

    sdtdserver.enable = false;

    factorio = {
      enable = true;
      autosave-interval = 10;
      game-name = "Shame";
      public = true;
      username = "nyanloutre";
    };

    airsonic = {
      enable = true;
      maxMemory = 500;
    };
  };

  systemd.services.dogetipbot-telegram = {
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    script = "${pkgs.dogetipbot-telegram}/bin/dogetipbot-telegram --block-io-api-key $BLOCK_IO_API_KEY --block-io-pin $BLOCK_IO_PIN --telegram-api-key $TELEGRAM_API_KEY --network DOGE";
    enable = true;
    serviceConfig = {
      EnvironmentFile = "/mnt/secrets/dogetipbot-telegram_env";
      DynamicUser = true;
    };
  };

  systemd.services.matrix-synapse = {
    serviceConfig = {
      MemoryHigh = "3G";
      MemoryMax = "5G";
    };
  };

  users.groups.acme.members = [ "matrix-synapse" ];

  security.sudo.extraRules = [
    { commands = [ { command = "${pkgs.smartmontools}/bin/smartctl"; options = [ "NOPASSWD" ]; } ]; users = [ "telegraf" ]; }
  ];

  networking = {
    wireguard.interfaces = {
      wg0 = {
        ips = [ "192.168.20.1/24" ];
        privateKeyFile = "/mnt/secrets/wireguard/wg0.privatekey";
        listenPort = 51820;
        allowedIPsAsRoutes = false;
        peers = [
          {
            allowedIPs = [ "0.0.0.0/0" ];
            publicKey = "b/SXiqo+GPdNOc54lyEVeUBc6B5AbVMKh+g5EZPGzlE=";
          }
        ];
      };
    };

    firewall.allowedTCPPorts = [
      51413 # Transmission
      8448 # Matrix federation
      20 21 # FTP
    ];

    firewall.allowedTCPPortRanges = [
      { from = 64000; to = 65535; } # FTP
    ];

    firewall.allowedUDPPorts = [
      51413 # Transmission
      51820 # Wireguard
    ];
  };
}