"""Replay Client: This node is used to represent a leader robot and send a sequence of goals to the dataflow,.

reading a dataset of actions and joints from a specific episode.
"""

import argparse
import os

import pandas as pd
import pyarrow as pa
from dora import Node


def joints_values_to_arrow(joints, values):
    """TODO: Add docstring."""
    return pa.StructArray.from_arrays(
        arrays=[joints, values],
        names=["joints", "values"],
        fields=None,
        mask=None,
        memory_pool=None,
    )


class Client:
    """TODO: Add docstring."""

    def __init__(self, config: dict[str, any]):
        """TODO: Add docstring."""
        self.config = config

        self.node = Node(config["name"])

        dataset = pd.read_parquet(config["episode_path"] + "/dataset.parquet")

        # Filter the dataset to only keep rows from the episode
        dataset = dataset[dataset["episode_index"] == config["episode_id"]]

        self.action = dataset["action"]
        self.joints = dataset["joints"]
        self.frame = 0

    def run(self):
        """TODO: Add docstring."""
        for event in self.node:
            event_type = event["type"]

            if event_type == "INPUT":
                event_id = event["id"]

                if event_id == "pull_position":
                    if self.pull_position(self.node, event["metadata"]):
                        break
                elif event_id == "end":
                    break

            elif event_type == "ERROR":
                raise ValueError("An error occurred in the dataflow: " + event["error"])

        self.node.send_output("end", pa.array([]))

    def pull_position(self, node, metadata) -> bool:
        """TODO: Add docstring."""
        if self.frame >= len(self.action):
            return True

        action = self.action.iloc[self.frame]
        joints = self.joints.iloc[self.frame]

        position = joints_values_to_arrow(joints, pa.array(action, type=pa.float32()))

        self.frame += 1

        node.send_output("position", position, metadata)
        return False


def main():
    """Handle dynamic nodes, ask for the name of the node in the dataflow."""
    parser = argparse.ArgumentParser(
        description="Replay Client: This node is used to replay a sequence of goals for a followee robot.",
    )

    parser.add_argument(
        "--name",
        type=str,
        required=False,
        help="The name of the node in the dataflow.",
        default="replay_client",
    )
    parser.add_argument(
        "--path",
        type=str,
        required=False,
        help="The path to the episode dataset.",
        default=None,
    )
    parser.add_argument(
        "--episode",
        type=int,
        required=False,
        help="The episode id to replay.",
        default=None,
    )

    args = parser.parse_args()

    if (not os.getenv("PATH") and args.path is None) or (
        not os.getenv("EPISODE") and args.episode is None
    ):
        raise ValueError("The environment variables PATH and EPISODE_ID must be set.")

    if not isinstance(int(os.getenv("EPISODE")), int):
        raise ValueError("The environment variable EPISODE_ID must be an integer.")

    # Create configuration
    config = {
        "name": args.name,
        "episode_path": os.getenv("PATH", args.path),
        "episode_id": int(os.getenv("EPISODE", args.episode)),
    }

    print("Replay Client Configuration: ", config, flush=True)

    client = Client(config)
    client.run()


if __name__ == "__main__":
    main()
