"""
textfsmgen.engine.common
========================

Common grammar pattern utilities for the TextFSM Generator framework.
"""

import re
from typing import Optional

from textfsmgen.core.patterns import TextPattern
from textfsmgen.libs import PATTERN
from textfsmgen.libs import number
from textfsmgen.libs import text

from textfsmgen.engine.translate import PatternTranslator

from textfsmgen.exceptions import RuntimeException
from textfsmgen.exceptions import raise_exception

def get_line_position_by(
    lines: list[str],
    item: Optional[str | int | None]
) -> Optional[int | None]:
    """
    Determine the position of a line in `lines` based on a string
    pattern or numeric index.
    """
    if item is None:
        return None

    is_number, index = number.try_to_get_number(item, return_type=int)
    if is_number:
        return None if index >= len(lines) else index

    regex_prefix = r'(?i)^\s*--regex\s+'

    if re.match(regex_prefix, item):
        pattern = re.sub(regex_prefix, "", item)
    else:
        pattern = TextPattern(item)

    for index, line in enumerate(lines):
        if re.search(pattern, line, re.I):
            return index

    return None


def get_fixed_line_snippet(
    lines: list[str],
    line: str = "",
    index: Optional[int | None] = None
) -> str:
    """
    Generate a normalized snippet representation of a line.
    """
    # Resolve line by index if provided
    if index is not None:
        is_number, converted_index = number.try_to_get_number(index, return_type=int)
        if is_number:
            try:
                line = lines[converted_index]
            except IndexError as ex:
                total = len(lines)
                msg = (
                    f"Index out of range: attempted to access index {converted_index}, "
                    f"but only {total} lines are available."
                )
                raise_exception(ex, msg=msg)
            except Exception as ex:
                RuntimeException.do_raise_runtime_error(ex)
        else:
            RuntimeException.do_raise_runtime_error(
                obj="UnknownParamIndexTypeError",
                msg=(
                    f"Invalid index type: expected an integer to access list, "
                    f"but received {type(index).__name__} ({index!r})."
                ),
            )

    # Decode bytes to string if necessary
    if isinstance(line, bytes):
        line = line.decode("utf-8")

    # Validate type
    if not isinstance(line, str):
        RuntimeException.do_raise_runtime_error(
            obj="UnknownParamLineTypeError",
            msg=(
                f"Invalid line type: expected a string, "
                f"but received {type(line).__name__} with value {line!r}."
            ),
        )

    # Handle empty or whitespace-only lines
    if not line.strip():
        ws_type = "whitespace" if line.strip(" ") else "space"
        return f"start() end({ws_type})"

    # Tokenize and normalize numeric tokens
    tokens = text.Text(line.strip()).do_finditer_split(PATTERN.NON_WSS)
    for i, token in enumerate(tokens):
        if token.strip():
            factory = PatternTranslator.do_factory_create(token)
            if factory.name in {"digit", "digits", "number", "mixed_number", "puncts"}:
                tokens[i] = factory.get_template_snippet()

    snippet_body = text.join_string(*tokens)
    leading = text.Line.get_leading(line)
    trailing = text.Line.get_trailing(line)

    return f"{leading}{snippet_body}{trailing}"
