{ lib, pkgs, ... }:

let
  # Note: For some reason Privoxy can't issue valid
  # certificates if the CA is generated using gnutls :(
  certs = pkgs.runCommand "example-certs" { buildInputs = [ pkgs.openssl ]; } ''
    mkdir $out

    # generate CA keypair
    openssl req -new -nodes -x509 \
      -extensions v3_ca -keyout $out/ca.key \
      -out $out/ca.crt -days 365 \
      -subj "/O=Privoxy CA/CN=Privoxy CA"

    # generate server key/signing request
    openssl genrsa -out $out/server.key 3072
    openssl req -new -key $out/server.key \
      -out server.csr -sha256 \
      -subj "/O=An unhappy server./CN=example.com"

    # sign the request/generate the certificate
    openssl x509 -req -in server.csr -CA $out/ca.crt \
    -CAkey $out/ca.key -CAcreateserial -out $out/server.crt \
    -days 500 -sha256
  '';
in

{
  name = "privoxy";
  meta = with lib.maintainers; {
    maintainers = [ rnhmjoj ];
  };

  nodes.machine =
    { ... }:
    {
      services.nginx.enable = true;
      services.nginx.virtualHosts."example.com" = {
        addSSL = true;
        sslCertificate = "${certs}/server.crt";
        sslCertificateKey = "${certs}/server.key";
        locations."/".root = pkgs.writeTextFile {
          name = "bad-day";
          destination = "/how-are-you/index.html";
          text = "I've had a bad day!\n";
        };
        locations."/ads".extraConfig = ''
          return 200 "Hot Nixpkgs PRs in your area. Click here!\n";
        '';
      };

      services.privoxy = {
        enable = true;
        inspectHttps = true;
        settings = {
          ca-cert-file = "${certs}/ca.crt";
          ca-key-file = "${certs}/ca.key";
          debug = 65536;
        };
        userActions = ''
          {+filter{positive}}
          example.com

          {+block{Fake ads}}
          example.com/ads
        '';
        userFilters = ''
          FILTER: positive This is a filter example.
          s/bad/great/ig
        '';
      };

      security.pki.certificateFiles = [ "${certs}/ca.crt" ];

      networking.hosts."::1" = [ "example.com" ];
      networking.proxy.httpProxy = "http://localhost:8118";
      networking.proxy.httpsProxy = "http://localhost:8118";
    };

  nodes.machine_socks4 =
    { ... }:
    {
      services.privoxy = {
        enable = true;
        settings.forward-socks4 = "/ 127.0.0.1:9050 .";
      };
    };
  nodes.machine_socks4a =
    { ... }:
    {
      services.privoxy = {
        enable = true;
        settings.forward-socks4a = "/ 127.0.0.1:9050 .";
      };
    };
  nodes.machine_socks5 =
    { ... }:
    {
      services.privoxy = {
        enable = true;
        settings.forward-socks5 = "/ 127.0.0.1:9050 .";
      };
    };
  nodes.machine_socks5t =
    { ... }:
    {
      services.privoxy = {
        enable = true;
        settings.forward-socks5t = "/ 127.0.0.1:9050 .";
      };
    };

  testScript = ''
    with subtest("Privoxy is running"):
        machine.wait_for_unit("privoxy")
        machine.wait_for_open_port(8118)
        machine.succeed("curl -f http://config.privoxy.org")

    with subtest("Privoxy can filter http requests"):
        machine.wait_for_open_port(80)
        assert "great day" in machine.succeed(
            "curl -sfL http://example.com/how-are-you? | tee /dev/stderr"
        )

    with subtest("Privoxy can filter https requests"):
        machine.wait_for_open_port(443)
        assert "great day" in machine.succeed(
            "curl -sfL https://example.com/how-are-you? | tee /dev/stderr"
        )

    with subtest("Blocks are working"):
        machine.wait_for_open_port(443)
        machine.fail("curl -f https://example.com/ads 1>&2")
        machine.succeed("curl -f https://example.com/PRIVOXY-FORCE/ads 1>&2")

    with subtest("Temporary certificates are cleaned"):
        # Count current certificates
        machine.succeed("test $(ls /run/privoxy/certs | wc -l) -gt 0")
        # Forward in time 12 days, trigger the timer..
        machine.succeed("date -s \"$(date --date '12 days')\"")
        machine.systemctl("start systemd-tmpfiles-clean")
        # ...and count again
        machine.succeed("test $(ls /run/privoxy/certs | wc -l) -eq 0")

    with subtest("Privoxy supports socks upstream proxies"):
        for m in [machine_socks4, machine_socks4a, machine_socks5, machine_socks5t]:
            m.wait_for_unit("privoxy")
            m.wait_for_open_port(8118)
            # We expect a 503 error because the dummy upstream proxy is not reachable.
            # In issue #265654, instead privoxy segfaulted causing curl to exit with "Empty reply from server".
            m.succeed("http_proxy=http://localhost:8118 curl -v http://does-not-exist/ 2>&1 | grep 'HTTP/1.1 503'")
  '';
}
