import textwrap
from typing import Optional

from .enums import Tag, Tags
from .export import _render
from .marker import get_marker_config, register_marker
from .styles import StxStyles, Style
from .toc import register_toc_entry
from .utils import contain_link, strip_html


def st_write(
    *args, style: Style = StxStyles.none, tag: Tag = Tags.span,
    link:str="", no_link_decor:bool=False, hover:bool=True,
    toc_lvl: Optional[str] = None, label: str = "",
    marker: Optional[bool] = None ):
    """
    Function to write a styled string with optional link reference and table of content entry.

    :param args: The text or HTML content to wrap in the specified tag. See `Syntax` for example of usage.
    :param style: A Style object representing the CSS to apply to the text. Defaults to `StxStyles.none`.
    :param tag: The HTML tag to use for wrapping the content (e.g., <span>, <div>). Defaults to `Tags.span`.
    :param link: An optional hyperlink to wrap around the content. If provided, the text will be clickable.
    :param no_link_decor: If True, removes text decoration (like underline) for links. Defaults to False.
    :param hover: If True, includes hover functionality to display additional information (e.g., link previews).
    :param toc_lvl: A numeric string that may start with '+' or '-' (e.g. '1', '-1' or '+1')
        denoting the toc level of this content. This allows the text to be part of a hierarchical TOC.
    :param label: An optional label to use for the TOC entry. If not provided, a truncated version of `txt` is used.

    Notes:
    - If `toc_lvl` is provided, the function wraps the content in tags to associate it with the specified TOC level.
    - Labels for TOC are automatically generated by truncating the text to a predefined length if no label is given.
    - The function uses `contain_link` to handle optional hyperlinking and link decorations.

    ## Syntax Examples:
    - st_write(s.header, "My Title")
    - st_write(s.header, (s.red, "R"), "ainbow ", (s.blue, "T"), "ext")
    - st_write("click this ", (s.blue, "link", "https://google.com"))
    """

    # Parse style and txt arguments
    container_style, final_txt = _parse_args(*args, style=style, no_link_decor=no_link_decor, hover=hover)
    final_txt = textwrap.dedent(final_txt)

    # Handle ToC registration and element id
    final_txt, key_anchor = _handle_toc(final_txt, toc_lvl, label, marker)
    elementId = f" id='{key_anchor}'" if key_anchor else ""

    # Wrap the text in the specified tag with the given style. This ensures consistent styling.
    txt_tag = f'<{tag}{elementId} style="{container_style}">{final_txt}</{tag}>'

    # Handle optional hyperlinking and hover effects.
    # Note: If inline links are used in args, avoid using a wrapper link here
    # as nested <a> tags are invalid HTML.
    # Thus, this could be a future source of bugs.
    txt_tag = contain_link(txt_tag, link, no_link_decor, hover)

    _render(txt_tag)

def _parse_args(*args, style: Style = StxStyles.none, no_link_decor:bool=False, hover:bool=False):
    """
    Parses arguments to distinguish between Wrapper Styles, Text, and Tuples.
    """
    container_style = StxStyles.none
    content_args = args

    # Check if the first arg is a Wrapper Style
    if len(args) > 0 and isinstance(args[0], Style):
        container_style = args[0]
        content_args = args[1:]
    elif style is not None:
        # Fallback if style was passed as a keyword arg
        container_style = style

    # Build Inner HTML
    html_parts = []

    for item in content_args:
        if isinstance(item, tuple):
            sub_style = StxStyles.none
            sub_txt = ""
            sub_link = ""

            # --- CASE 1: (Style, Text) ---
            if len(item) == 2:
                sub_style, sub_txt = item

            # --- CASE 2: (Style, Text, Link) ---
            elif len(item) == 3:
                sub_style, sub_txt, sub_link = item

            span = f'<span style="{sub_style}">{sub_txt}</span>'
            span = contain_link(span, sub_link, no_link_decor=no_link_decor, hover=hover)

            html_parts.append(span)

        else:
            # String or other object
            html_parts.append(str(item))

    final_txt = "".join(html_parts)

    return container_style, final_txt

def _handle_toc(final_txt: str, toc_lvl: Optional[str] = None, label: str = "", marker: Optional[bool] = None):
        # --- 3. Handle ToC Registration ---
    key_anchor = ""
    if toc_lvl:
        # If no label is provided, generate a truncated label from the text. This ensures TOC entries are descriptive
        # but not overly long, avoiding clutter in the TOC.
        if not label:
            label_length = 73
            clean_label = strip_html(final_txt)  # Remove HTML tags to generate a clean label
            label = (clean_label[:label_length] + '..') if len(clean_label) > label_length else clean_label

        # Generate the opening and closing TOC tags for the specified level and label.
        key_anchor, section_number, resolved_lvl = register_toc_entry(label, toc_lvl)
        final_txt = section_number + final_txt # Prepend the numbering of the ToC title

        # Bridge TOC → Marker (opt-in)
        marker_cfg = get_marker_config()
        if marker_cfg is not None and marker is not False:
            auto = marker_cfg.auto_marker_on_toc
            should_mark = (
                marker is True
                or (auto is True)
                or (isinstance(auto, int) and resolved_lvl <= auto)
            )
            if should_mark:
                register_marker(label, key_anchor)

    return final_txt, key_anchor
