"""Jackett-style field filters for transforming extracted values.

Each filter is a standalone function taking ``(value, args)`` and returning
the transformed string, or ``None`` when the filter produces no match.
Filters are registered in the module-level ``FILTERS`` dict and dispatched
by :func:`apply`.
"""

import logging
import re
from collections.abc import Callable

from mariner.models.html import FieldFilter

log = logging.getLogger(__name__)

FilterFn = Callable[[str, list[str]], str | None]


def _split(value: str, args: list[str]) -> str | None:
    separator = args[0]
    index = int(args[1])
    parts = value.split(separator)
    try:
        return parts[index].strip()
    except IndexError:
        return None


def _regexp(value: str, args: list[str]) -> str | None:
    pattern = args[0]
    match = re.search(pattern, value)
    if match:
        return match.group(1) if match.lastindex else match.group(0)
    return None


def _replace(value: str, args: list[str]) -> str | None:
    return value.replace(args[0], args[1])


def _re_replace(value: str, args: list[str]) -> str | None:
    return re.sub(args[0], args[1], value)


def _trim(value: str, args: list[str]) -> str | None:
    return value.strip()


def _append(value: str, args: list[str]) -> str | None:
    return value + args[0]


def _prepend(value: str, args: list[str]) -> str | None:
    return args[0] + value


def _dateparse(value: str, args: list[str]) -> str | None:
    # dateparse is handled downstream by normalize_date
    return value


def _tolower(value: str, args: list[str]) -> str | None:
    return value.lower()


def _toupper(value: str, args: list[str]) -> str | None:
    return value.upper()


FILTERS: dict[str, FilterFn] = {
    "split": _split,
    "regexp": _regexp,
    "replace": _replace,
    "re_replace": _re_replace,
    "trim": _trim,
    "append": _append,
    "prepend": _prepend,
    "dateparse": _dateparse,
    "tolower": _tolower,
    "toupper": _toupper,
}


def apply(value: str, f: FieldFilter) -> str | None:
    """Apply a single Jackett-style filter to a string value.

    Args:
        value: The current string value.
        f: The filter to apply.

    Returns:
        The transformed value, or None if the filter produces no match.

    """
    fn = FILTERS.get(f.name)
    if fn is None:
        log.debug("Unknown filter %r, passing value through", f.name)
        return value
    return fn(value, f.args)
