from typing import Optional
from typing import List


def make_sample_until(min_timesteps: Optional[int]=None, min_episodes: Optional[int]=None):
    """Returns a termination condition sampling for a number of timesteps and episodes.
    
    Args:
        min_timesteps: Sampling will not stop until there are at least this many
            timesteps.
        min_episodes: Sampling will not stop until there are at least this many
            episodes.

    Returns:
        A termination condition which given a list of trajectories returns true if the condition is met.

    Raises:
        ValueError if neither of n_timesteps and n_episodes are set, or if either are
            non-positive.
    """
    if min_timesteps is None and min_episodes is None:
        raise ValueError(
            "At least one of min_timesteps and min_episodes needs to be non-None"
        )

    conditions = []
    if min_timesteps is not None:
        if min_timesteps <= 0:
            raise ValueError(
                f"min_timesteps={min_timesteps} if provided must be positive"
            )

        def timestep_cond(trajectories):
            if len(trajectories) == 0:
                return False
            timesteps = sum(len(t['obs']) - 1 for t in trajectories)
            return timesteps >= min_timesteps   
        conditions.append(timestep_cond)

    if min_episodes is not None:
        if min_episodes <= 0:
            raise ValueError(
                f"min_episodes={min_episodes} if provided must be positive"
            )
        conditions.append(lambda trajectories: len(trajectories) >= min_episodes)

    def sample_until(trajs: List[dict]) -> bool:
        for cond in conditions:
            if not cond(trajs):
                return False
        return True

    return sample_until