{ pkgs, self }:
let
  lib = pkgs.lib;

  ## FIXME: this binding was not used, but maybe we want a side-effect or something?
  # rebuildableTest = import ./rebuildableTest.nix pkgs;

  seleniumScript =
    pkgs.writers.writePython3Bin "selenium-script"
      { libraries = with pkgs.python3Packages; [ selenium ]; }
      ''
        from selenium import webdriver
        from selenium.webdriver.common.by import By
        from selenium.webdriver.firefox.options import Options
        from selenium.webdriver.support.ui import WebDriverWait

        print(1)

        options = Options()
        options.add_argument("--headless")
        # devtools don't show up in headless screenshots
        # options.add_argument("-devtools")
        service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}")  # noqa: E501

        driver = webdriver.Firefox(options=options, service=service)
        driver.get("http://mastodon.localhost:55001/public/local")

        # wait until the statuses load
        WebDriverWait(driver, 90).until(
            lambda x: x.find_element(By.CLASS_NAME, "status"))

        driver.save_screenshot("/mastodon-screenshot.png")

        driver.close()
      '';
in
pkgs.nixosTest {
  name = "test-mastodon-garage";

  nodes = {
    server =
      { config, ... }:
      {
        virtualisation.memorySize = lib.mkVMOverride 4096;
        imports = with self.nixosModules; [
          bleedingFediverse
          fediversity
          garage-vm
          mastodon-vm
        ];
        # TODO: pair down
        environment.systemPackages = with pkgs; [
          python3
          firefox-unwrapped
          geckodriver
          toot
          xh
          seleniumScript
          helix
          imagemagick
        ];
        environment.variables = {
          POST_MEDIA = ./green.png;
          AWS_ACCESS_KEY_ID = config.services.garage.ensureKeys.mastodon.id;
          AWS_SECRET_ACCESS_KEY = config.services.garage.ensureKeys.mastodon.secret;
        };
      };
  };

  testScript =
    { nodes, ... }:
    ''
      import re
      import time

      server.start()

      with subtest("Mastodon starts"):
        server.wait_for_unit("mastodon-web.service")

      # make sure mastodon is fully up and running before we interact with it
      # TODO: is there a way to test for this?
      time.sleep(180)

      with subtest("Account creation"):
        account_creation_output = server.succeed("mastodon-tootctl accounts create test --email test@test.com --confirmed --approve")
        password_match = re.match('.*New password: ([^\n]*).*', account_creation_output, re.S)
        if password_match is None:
          raise Exception(f"account creation did not generate a password.\n{account_creation_output}")
        password = password_match.group(1)

      with subtest("TTY Login"):
        server.wait_until_tty_matches("1", "login: ")
        server.send_chars("root\n");

      with subtest("Log in with toot"):
        # toot doesn't provide a way to just specify our login details as arguments, so we have to pretend we're typing them in at the prompt
        server.send_chars("toot login_cli --instance http://mastodon.localhost:55001 --email test@test.com\n")
        server.wait_until_tty_matches("1", "Password: ")
        server.send_chars(password + "\n")
        server.wait_until_tty_matches("1", "Successfully logged in.")

      with subtest("post text"):
        server.succeed("echo 'hello mastodon' | toot post")

      with subtest("post image"):
        server.succeed("toot post --media $POST_MEDIA")

      with subtest("access garage"):
        server.succeed("mc alias set garage ${nodes.server.fediversity.internal.garage.api.url} --api s3v4 --path off $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY")
        server.succeed("mc ls garage/mastodon")

      with subtest("access image in garage"):
        image = server.succeed("mc find garage --regex original")
        image = image.rstrip()
        if image == "":
          raise Exception("image posted to mastodon did not get stored in garage")
        server.succeed(f"mc cat {image} >/garage-image.webp")
        garage_image_hash = server.succeed("identify -quiet -format '%#' /garage-image.webp")
        image_hash = server.succeed("identify -quiet -format '%#' $POST_MEDIA")
        if garage_image_hash != image_hash:
          raise Exception("image stored in garage did not match image uploaded")

      with subtest("Content security policy allows garage images"):
        headers = server.succeed("xh -h http://mastodon.localhost:55001/public/local")
        csp_match = None
        # I can't figure out re.MULTILINE
        for header in headers.split("\n"):
          csp_match =  re.match('^Content-Security-Policy: (.*)$', header)
          if csp_match is not None:
            break
        if csp_match is None:
          raise Exception("mastodon did not send a content security policy header")
        csp = csp_match.group(1)
        # the img-src content security policy should include the garage server
        ## TODO: use `nodes.server.fediversity.internal.garage.api.url` same as above, but beware of escaping the regex. Be careful with port 80 though.
        garage_csp = re.match(".*; img-src[^;]*web\.garage\.localhost.*", csp)
        if garage_csp is None:
          raise Exception("Mastodon's content security policy does not include garage server. image will not be displayed properly on mastodon.")

      # this could in theory give a false positive if mastodon changes it's colorscheme to include pure green.
      with subtest("image displays"):
        server.succeed("selenium-script")
        server.copy_from_vm("/mastodon-screenshot.png", "")
        displayed_colors = server.succeed("convert /mastodon-screenshot.png -define histogram:unique-colors=true -format %c histogram:info:")
        # check that the green image displayed somewhere
        green_check = re.match(".*#00FF00.*", displayed_colors, re.S)
        if green_check is None:
          raise Exception("cannot detect the uploaded image on mastodon page.")
    '';
}