{ pkgs, self }:
let
  lib = pkgs.lib;
  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.")
  '';
}