"""NLP text preprocessing utilities."""

from __future__ import annotations

import re
from typing import Any

import pandas as pd


def remove_html(text: str) -> str:
    """Remove HTML tags from text. Uses BeautifulSoup if available, else regex fallback."""
    try:
        from bs4 import BeautifulSoup
        return BeautifulSoup(text, "html.parser").get_text(separator=" ")
    except ImportError:
        return re.sub(r"<[^>]+>", " ", str(text))


def expand_contractions(text: str) -> str:
    """Expand English contractions (e.g. don't -> do not). Requires optional dep: contractions."""
    try:
        import contractions
        return contractions.fix(str(text))
    except ImportError:
        return str(text)


def remove_noise(
    text: str,
    remove_urls: bool = True,
    remove_numbers: bool = True,
    remove_punctuation: bool = False,
    keep_apostrophe: bool = True,
) -> str:
    """Remove URLs, numbers, and optionally punctuation from text."""
    s = str(text)
    if remove_urls:
        s = re.sub(r"https?://\S+|www\.\S+", " ", s)
    if remove_numbers:
        s = re.sub(r"\d+", " ", s)
    if remove_punctuation:
        if keep_apostrophe:
            s = re.sub(r"[^\w\s']", " ", s)
        else:
            s = re.sub(r"[^\w\s]", " ", s)
    return re.sub(r"\s+", " ", s).strip()


def normalize_text(text: str, lowercase: bool = True, strip: bool = True) -> str:
    """Lowercase and strip whitespace."""
    s = str(text)
    if lowercase:
        s = s.lower()
    if strip:
        s = s.strip()
    return re.sub(r"\s+", " ", s)


def clean_pipeline(
    text: str,
    remove_html_tags: bool = True,
    expand_contractions_flag: bool = True,
    remove_urls: bool = True,
    remove_numbers: bool = True,
    lowercase: bool = True,
    **remove_noise_kwargs: Any,
) -> str:
    """Compose: remove HTML → expand contractions → remove noise → normalize."""
    s = str(text)
    if remove_html_tags:
        s = remove_html(s)
    if expand_contractions_flag:
        s = expand_contractions(s)
    s = remove_noise(s, remove_urls=remove_urls, remove_numbers=remove_numbers, **remove_noise_kwargs)
    s = normalize_text(s, lowercase=lowercase, strip=True)
    return s


def handle_nulls(
    df: pd.DataFrame,
    column: str,
    strategy: str = "drop",
    fill_value: str = "",
) -> pd.DataFrame:
    """Handle nulls in a text column: 'drop' rows or 'fill' with fill_value."""
    out = df.copy()
    if strategy == "drop":
        out = out.dropna(subset=[column])
    elif strategy == "fill":
        out[column] = out[column].fillna(fill_value)
    return out


def create_sentiment_label(
    rating: int | float,
    negative_threshold: float = 2.5,
    positive_threshold: float = 3.5,
) -> str:
    """Map numeric rating to sentiment: 1-2 -> negative, 3 -> neutral, 4-5 -> positive. Configurable thresholds."""
    r = float(rating)
    if r <= negative_threshold:
        return "negative"
    if r >= positive_threshold:
        return "positive"
    return "neutral"
