"""Shared utility functions for the LAPIS converter."""

from __future__ import annotations

import re

MAX_DESCRIPTION_LENGTH = 80


def truncate_description(text: str | None, max_len: int = MAX_DESCRIPTION_LENGTH) -> str | None:
    """Truncate a description to a single line of at most *max_len* characters."""
    if not text:
        return None
    line = text.replace("\n", " ").strip()
    line = re.sub(r"\s+", " ", line)
    if len(line) <= max_len:
        return line
    return line[: max_len - 3].rstrip() + "..."


def to_snake_case(name: str) -> str:
    """Convert a string to snake_case.

    Handles camelCase, PascalCase, kebab-case, and slash/brace path segments.
    """
    name = re.sub(r"[{}]", "", name)
    name = name.strip("/")
    name = name.replace("/", "_").replace("-", "_").replace(".", "_")
    name = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name)
    name = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name)
    name = re.sub(r"_+", "_", name).strip("_")
    return name.lower()


def generate_operation_id(method: str, path: str) -> str:
    """Synthesise an operationId from HTTP method + path.

    Example: ``GET /invoices/{id}`` -> ``get_invoices_id``
    """
    return to_snake_case(f"{method}_{path}")


def pick_shorter(a: str | None, b: str | None) -> str | None:
    """Return the shorter of two strings, preferring non-None values."""
    if a is None:
        return b
    if b is None:
        return a
    return a if len(a) <= len(b) else b
