import itertools


def group_bboxes_per_line(char_bboxes: dict, page_index: int) -> list:
    """
    Group characters bounding boxes per line.

    A line will have a single bounding box.

    :param char_bboxes: Bounding boxes of the characters.
    :param page_index: Index of the page in the document.
    :return: List with 1 bounding box per line.
    """
    lines_bboxes = []

    # iterate over each line_number and all of the character bboxes with that line number
    for line_number, line_char_bboxes in itertools.groupby(char_bboxes, lambda x: x['line_number']):
        # set the defaut values which we overwrite with the actual character bbox values
        x0 = 100000000
        top = 10000000
        y0 = 10000000
        x1 = 0
        y1 = 0
        bottom = 0
        start_offset = 100000000
        end_offset = 0

        # remove space chars from the line selection so they don't interfere with the merging of bboxes
        # (a bbox should never start with a space char)
        trimmed_line_char_bboxes = [char for char in line_char_bboxes if not char['text'].isspace()]

        if len(trimmed_line_char_bboxes) == 0:
            continue

        # merge characters bounding boxes of the same line
        for char_bbox in trimmed_line_char_bboxes:
            x0 = min(char_bbox['x0'], x0)
            top = min(char_bbox['top'], top)
            y0 = min(char_bbox['y0'], y0)

            x1 = max(char_bbox['x1'], x1)
            bottom = max(char_bbox['bottom'], bottom)
            y1 = max(char_bbox['y1'], y1)

            start_offset = min(int(char_bbox['string_offset']), start_offset)
            end_offset = max(int(char_bbox['string_offset']), end_offset)

        line_bbox = {
            'bottom': bottom,
            'page_index': page_index,
            'top': top,
            'x0': x0,
            'x1': x1,
            'y0': y0,
            'y1': y1,
            'start_offset': start_offset,
            'end_offset': end_offset + 1,
            'line_number': line_number,
        }

        lines_bboxes.append(line_bbox)

    return lines_bboxes