"""Article viewer for Jupyter notebooks with keyword highlighting."""

import re
from typing import List

import pandas as pd
from IPython.display import display, HTML
import ipywidgets as widgets


def highlight_keywords(text: str, keywords: List[str]) -> str:
    """Highlight keywords in text with yellow background."""
    if not keywords:
        return text

    # Escape HTML in text first
    text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

    # Highlight each keyword (case-insensitive)
    for keyword in keywords:
        if not keyword:
            continue
        pattern = re.compile(re.escape(keyword), re.IGNORECASE)
        text = pattern.sub(
            lambda m: f'<mark style="background-color: #ffeb3b; padding: 2px 4px; border-radius: 3px;">{m.group()}</mark>',
            text
        )

    return text


def display_articles(
    df: pd.DataFrame,
    keywords: List[str] = None,
    title: str = 'title',
    body: str = 'body',
    extra_fields: List[str] = None
) -> None:
    """
    Display articles from a dataframe in a Jupyter notebook.

    Parameters
    ----------
    df : pd.DataFrame
        DataFrame with title and body columns
    keywords : List[str], optional
        List of keywords to highlight in the articles
    title : str, optional
        Name of the column containing article titles (default: 'title')
    body : str, optional
        Name of the column containing article bodies (default: 'body')
    extra_fields : List[str], optional
        List of additional column names to display below the title
    """
    if keywords is None:
        keywords = []
    if extra_fields is None:
        extra_fields = []

    if df.empty:
        display(HTML("<p>No articles to display.</p>"))
        return

    # Track current article index
    total_articles = len(df)
    current_index = [0]  # Use list to allow mutation in closures

    # Navigation buttons
    prev_btn = widgets.Button(
        description='← Prev',
        disabled=True,
        layout=widgets.Layout(width='80px')
    )
    next_btn = widgets.Button(
        description='Next →',
        disabled=(total_articles <= 1),
        layout=widgets.Layout(width='80px')
    )

    # Jump to article input
    jump_input = widgets.BoundedIntText(
        value=1,
        min=1,
        max=total_articles,
        description='Go to:',
        style={'description_width': 'initial'},
        layout=widgets.Layout(width='120px')
    )

    # Article counter label
    counter_label = widgets.HTML(
        value=f"<span style='font-size: 14px; color: #666;'>of {total_articles}</span>"
    )

    # Output area for article content
    output = widgets.Output()

    def render_article(index):
        """Render a single article with highlighted keywords."""
        row = df.iloc[index] if isinstance(index, int) else df.loc[index]
        title_text = highlight_keywords(str(row[title]), keywords)
        body_text = highlight_keywords(str(row[body]), keywords)

        # Convert newlines to <br> for body
        body_text = body_text.replace('\n', '<br>')

        # Render extra fields
        extra_html = ""
        if extra_fields:
            extra_items = []
            for field in extra_fields:
                if field in row.index:
                    value = highlight_keywords(str(row[field]), keywords)
                    extra_items.append(f"<span><b>{field}:</b> {value}</span>")
            if extra_items:
                extra_html = f"""
            <div style="color: #666; font-size: 13px; margin-bottom: 16px;">
                {' &nbsp;|&nbsp; '.join(extra_items)}
            </div>"""

        html = f"""
        <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    max-width: 800px; padding: 20px; border: 1px solid #e0e0e0;
                    border-radius: 8px; background-color: #fafafa;">
            <h2 style="color: #1a1a1a; margin-bottom: 16px; padding-bottom: 12px;
                       border-bottom: 2px solid #3498db;">{title_text}</h2>{extra_html}
            <div style="color: #333; line-height: 1.7; font-size: 15px;">
                {body_text}
            </div>
            <div style="margin-top: 16px; padding-top: 12px; border-top: 1px solid #e0e0e0;
                        color: #888; font-size: 12px;">
                Article {index + 1 if isinstance(index, int) else list(df.index).index(index) + 1} of {len(df)}
            </div>
        </div>
        """
        return HTML(html)

    def update_display():
        """Update the article display and button states."""
        idx = current_index[0]
        prev_btn.disabled = (idx == 0)
        next_btn.disabled = (idx >= total_articles - 1)
        jump_input.value = idx + 1
        output.clear_output()
        with output:
            display(render_article(idx))

    def on_prev_click(b):
        if current_index[0] > 0:
            current_index[0] -= 1
            update_display()

    def on_next_click(b):
        if current_index[0] < total_articles - 1:
            current_index[0] += 1
            update_display()

    def on_jump_change(change):
        new_idx = change['new'] - 1  # Convert to 0-indexed
        if 0 <= new_idx < total_articles and new_idx != current_index[0]:
            current_index[0] = new_idx
            update_display()

    prev_btn.on_click(on_prev_click)
    next_btn.on_click(on_next_click)
    jump_input.observe(on_jump_change, names='value')

    # Initial display
    with output:
        display(render_article(0))

    # Show navigation and keyword info
    keyword_info = ""
    if keywords:
        keyword_info = f"<p style='color: #666; font-size: 13px;'>Highlighting: <b>{', '.join(keywords)}</b></p>"

    # Navigation bar
    nav_bar = widgets.HBox([
        prev_btn,
        jump_input,
        counter_label,
        next_btn
    ], layout=widgets.Layout(align_items='center', gap='10px', justify_content='flex-end', width='800px'))

    display(HTML(keyword_info))
    display(nav_bar)
    display(output)
