#!/usr/bin/env python3
#
# Testing tool for the Faulty Connection problem
#
# Usage:
#
#   python3 testing_tool.py -f inputfile <program invocation>
#
#
# Use the -f parameter to specify the input file, e.g. 1.in.
# The input file should contain the following:
# - The first line contains "send".
# - The second line contains an integer to send.

# You can compile and run your solution as follows:

# C++:
#   g++ solution.cpp
#   python3 testing_tool.py -f 1.in ./a.out

# Python:
#   python3 testing_tool.py -f 1.in python3 ./solution.py

# Java:
#   javac solution.java
#   python3 testing_tool.py -f 1.in java solution

# Kotlin:
#   kotlinc solution.kt
#   python3 testing_tool.py -f 1.in kotlin solutionKt


# The tool is provided as-is, and you should feel free to make
# whatever alterations or augmentations you like to it.
#
# The tool attempts to detect and report common errors, but it is not an exhaustive test.
# It is not guaranteed that a program that passes this testing tool will be accepted.


import argparse
import subprocess
import traceback
import random

parser = argparse.ArgumentParser(description="Testing tool for problem Faulty Connection.")
parser.add_argument(
    "-f",
    dest="inputfile",
    metavar="inputfile",
    default=None,
    type=argparse.FileType("r"),
    required=True,
    help="The input file to use.",
)
parser.add_argument("program", nargs="+", help="Invocation of your solution")

args = parser.parse_args()


def single_pass(action: str, input_numbers: list[int]) -> list[int]:
    with (
        subprocess.Popen(
            " ".join(args.program),
            shell=True,
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE,
            universal_newlines=True,
        ) as p,
    ):
        assert p.stdin is not None and p.stdout is not None

        raw = "\n".join([action, " ".join(map(str, input_numbers))])
        stdout, stderr = p.communicate(input=raw)
        output_numbers = list(map(int, stdout.strip().split()))

        print(f"{action} exit code: {p.returncode}")
        print(f"{action} output:")
        print()
        print(stdout, flush=True)

        if action == "send":
            assert len(output_numbers) == 30, (
                f"Your submission printed {len(output_numbers)} numbers, when it needed to print 30 numbers"
            )
        else:
            assert len(output_numbers) == 1, (
                f"Your submission printed {len(output_numbers)} numbers, when it needed to print 1 number"
            )
        assert len(output_numbers) == len(set(output_numbers)), "Your submission printed duplicate numbers"

        return output_numbers


try:
    with args.inputfile as f:
        # Parse input
        lines = [line.strip() for line in f.readlines()]
        action = lines[0]
        sent_number = int(lines[1].strip())

        assert action == "send", f"Initial action must be 'send', but got {action}"

    returned_numbers = single_pass("send", [sent_number])

    receive_numbers = random.sample(returned_numbers, 2)
    print("Running receive with numbers:", receive_numbers)
    print()

    received_number = single_pass("receive", receive_numbers)

    assert received_number[0] == sent_number, f"Expected received number {sent_number}, but got {received_number[0]}"

    print("Success.")

except AssertionError as e:
    print()
    print(f"Error: {e}")
    print()
    exit(1)

except Exception:
    print()
    print("Unexpected error:")
    traceback.print_exc()
    print()
    exit(1)
