import fitz


def get_page_content_bbox(page: fitz.Page, padding=0) -> fitz.Rect:
    blocks = page.get_text("blocks")
    if not blocks:
        return page.cropbox

    x0, y0, x1, y1 = blocks[0][0:4]
    for b in blocks[1:]:
        x0 = min(x0, b[0])
        y0 = min(y0, b[1])
        x1 = max(x1, b[2])
        y1 = max(y1, b[3])

    return fitz.Rect(x0 - padding, y0, x1 + padding, y1)


def scale_content_horizontally(doc: fitz.Document, scaling_factor: float, progress_callback: callable = None) -> tuple[fitz.Document, list]:
    """
      - increase the page width by `scaling_factor`.
      - Place original content unscaled and centered horizontally on the wider page.
      - Return the new document and a list of the content bboxes adjusted to the new page coordinates.
    """
    if scaling_factor <= 0:
        raise ValueError("scaling_factor must be positive.")
    new_doc = fitz.open()
    original_content_bboxes = []
    num_pages = len(doc)

    for i, source_page in enumerate(doc):
        if progress_callback:
            progress_callback(i, num_pages)
        src_w = source_page.rect.width
        src_h = source_page.rect.height

        # New (wider) page width
        new_w = src_w * scaling_factor
        new_h = src_h

        # Center the original content horizontally on the new page
        x_offset = (new_w - src_w) / 2.0
        dest_rect = fitz.Rect(x_offset, 0, x_offset + src_w, src_h)

        new_page = new_doc.new_page(width=new_w, height=new_h)

        # Draw the original page into the destination rect WITHOUT scaling (show_pdf_page scales content to the dest_rect size).
        # Since dest_rect has same size as original page, content is placed unscaled and just shifted by x_offset.
        new_page.show_pdf_page(dest_rect, doc, source_page.number)

        # Get content bbox from original page and shift it by x_offset to match new page coordinates
        content_bbox = get_page_content_bbox(source_page)
        shifted_bbox = fitz.Rect(
            content_bbox.x0 + x_offset,
            content_bbox.y0,
            content_bbox.x1 + x_offset,
            content_bbox.y1,
        )
        original_content_bboxes.append(shifted_bbox)

    return new_doc, original_content_bboxes


def is_margin_space_occupied(page: fitz.Page, new_text_bbox: fitz.Rect, margin_area: fitz.Rect) -> bool:
    existing_blocks = page.get_text("blocks")

    for block in existing_blocks:
        existing_bbox = fitz.Rect(block[:4])
        if margin_area.contains(existing_bbox) and new_text_bbox.intersects(existing_bbox):
            return True

    return False


def add_definition_to_margin(
    doc: fitz.Document,
    scaling_factor: float,
    main_word: str,
    definition: str,
    original_location: dict,
    original_content_bboxes: list,
    using_llm: bool = False,
):
    page_num = original_location["page"]
    page = doc[page_num]

    original_bbox = fitz.Rect(original_location["bbox"])
    content_bbox = original_content_bboxes[page_num]
    content_center_x = content_bbox.x0 + content_bbox.width / 2

    scaled_content_x0 = content_bbox.x0
    scaled_content_x1 = content_bbox.x1

    y_position = original_bbox.y0 + 3  # Adjusted y_position slightly downwards
    padding = 5

    if original_location["column"] == 1:
        target_rect = fitz.Rect(padding, y_position, scaled_content_x0 - padding, page.rect.height - padding)
        margin_area_to_check = fitz.Rect(0, 0, scaled_content_x0, page.rect.height)
    elif original_location["column"] == 2:
        target_rect = fitz.Rect(scaled_content_x1 + padding, y_position, page.rect.width - padding, page.rect.height - padding)
        margin_area_to_check = fitz.Rect(scaled_content_x1, 0, page.rect.width, page.rect.height)
    else:
        return

    clean_def = " ".join(definition.replace('\r', ' ').replace('\n', ' ').split())
    full_text = f"{main_word}: {clean_def}"

    tw = fitz.TextWriter(page.rect)
    tw.fill_textbox(target_rect, full_text, fontsize=5)

    temp_doc = fitz.open()
    temp_page = temp_doc.new_page(width=page.rect.width, height=page.rect.height)
    tw.write_text(temp_page)
    blocks = temp_page.get_text("blocks")

    if not blocks:
        temp_doc.close()
        return
    new_text_bbox = fitz.Rect(blocks[0][:4])
    temp_doc.close()

    # --- Collision Check ----
    if is_margin_space_occupied(page, new_text_bbox, margin_area_to_check):
        # TODO: Implement a better collision strategy, e.g. shifting the new definition down until a free space is found
        return
    if using_llm:
        page.insert_textbox(target_rect, full_text, fontsize=5, fontname="helv", color=(0.5,0, 0))
    else:
        page.insert_textbox(target_rect, full_text, fontsize=5, fontname="helv", color=(0, 0.5, 0))  

    # except Exception as e:
    #     print(f"Error adding definition for '{main_word}': {e}")
