"""Reasoners service utility helpers.

These helpers are intentionally small and backend-agnostic (used by both SQL and HTTP
gateways).
"""

from __future__ import annotations

import re
from typing import Any, Sequence
from relationalai.services._shared.http_util import build_url

from .models import Reasoner, ReasonerType


def build_where(conditions: Sequence[tuple[str, Any]]) -> tuple[str, list[Any]]:
    """Build a parameterized WHERE clause from (sql_fragment, value) pairs.

    Conditions where the value is `None` are omitted.

    Args:
        conditions: A sequence of `(sql_fragment, value)` pairs.

    Returns:
        A tuple of `(where_sql, params)` where `where_sql` is either "" (no conditions)
        or a string starting with `"WHERE ..."`.
    """
    clauses: list[str] = []
    params: list[Any] = []
    for sql, value in conditions:
        if value is None:
            continue
        clauses.append(sql)
        params.append(value)
    if not clauses:
        return "", []
    return f"WHERE {' AND '.join(clauses)}", params


_INVALID_USER_CHARS = re.compile(r"[^a-zA-Z0-9_]+")
_DUP_UNDERSCORES = re.compile(r"_+")


def sanitize_user_name(user: str) -> str:
    """Sanitize a username into a valid default reasoner name.

    - If the username looks like an email, uses the part before '@'.
    - Replaces characters outside `[A-Za-z0-9_]` with `_`.
    - Collapses consecutive `_` and strips leading/trailing `_`.
    - Ensures the result starts with an alphanumeric character; otherwise prefixes `user_`.
    """
    if not isinstance(user, str):
        return ""
    raw = user.strip()
    if not raw:
        return ""

    # Extract local-part if email-like.
    local = raw.split("@", 1)[0].strip()
    if not local:
        local = raw

    out = _INVALID_USER_CHARS.sub("_", local)
    out = _DUP_UNDERSCORES.sub("_", out).strip("_")
    if not out:
        return ""
    if not out[0].isalnum():
        out = f"user_{out}"
    return out


def _pick_reasoner_name_match(candidates: Sequence[Reasoner], reasoner_name: str) -> Reasoner | None:
    """Return the best name match, preferring exact case before case-insensitive."""
    for candidate in candidates:
        if candidate.name == reasoner_name:
            return candidate
    folded = reasoner_name.casefold()
    for candidate in candidates:
        if candidate.name.casefold() == folded:
            return candidate
    return None


def _pick_reasoner_match(
    candidates: Sequence[Reasoner], reasoner_type: str | None, reasoner_name: str | None
) -> Reasoner | None:
    """Return the best reasoner match using case-insensitive type and name matching."""
    filtered = list(candidates)
    if reasoner_type is not None:
        normalized_type = ReasonerType.normalize(reasoner_type)
        filtered = [candidate for candidate in filtered if (candidate.type or "").upper() == normalized_type]
    if reasoner_name is None:
        return filtered[0] if filtered else None
    return _pick_reasoner_name_match(filtered, reasoner_name)


__all__ = [
    "build_url",
    "build_where",
    "sanitize_user_name",
]