"""
Common utility functions.

This module provides general-purpose functions including:
- Range generation
- Hashing and UUID operations
- Raw source code attachment
"""
from __future__ import annotations

from . import StringValue, IntegerValue
from ..frontend.base import Library, Expression, Field, TupleVariable, Value
from ..frontend.core import Any, String, Integer, Hash
from relationalai.util.docutils import include_in_docs


__include_in_docs__ = True

# the front-end library object
library = Library("common")


_range = library.Relation("range", [Field.input("start", Integer), Field.input("stop", Integer), Field.input("step", Integer), Field("value", Integer)])

@include_in_docs
def range(*args: IntegerValue) -> Expression:
    """
    Generate a range of integers.

    - Supports `range(stop)`, `range(start, stop)`, `range(start, stop, step)`.
    - `start` is inclusive and defaults to `0`.
    - `stop` is exclusive.
    - `step` defaults to `1`.

    Parameters
    ----------
    *args: IntegerValue
        - 1 arg: `stop`
        - 2 args: `start`, `stop`
        - 3 args: `start`, `stop`, `step`

    Returns
    -------
    Expression
        An `Expression` computing integer values in the specified range. Returns `Integer`.

    Examples
    --------
    Generate numbers from 0 to 9:

    >>> common.range(10)

    Generate numbers from 5 to 14:

    >>> common.range(5, 15)

    Generate even numbers from 0 to 18:

    >>> common.range(0, 20, 2)
    """
    if len(args) == 1:
        start, stop, step = 0, args[0], 1
    elif len(args) == 2:
        start, stop, step = args[0], args[1], 1
    elif len(args) == 3:
        start, stop, step = args
    else:
        raise ValueError(f"range expects 1, 2, or 3 arguments, got {len(args)}")

    return _range(start, stop, step)

_hash = library.Relation("hash", [Field.input("args", Any, is_list=True), Field("hash", Hash)])

@include_in_docs
def hash(*args: Value) -> Expression:
    """
    Compute a hash value for the given arguments.

    Parameters
    ----------
    *args: Value
        Values to hash together.

    Returns
    -------
    Expression
        An `Expression` representing the computed hash value. Returns `Hash`.

    Examples
    --------
    Compute hash of a single value:

    >>> select(common.hash(Person.email))

    Compute hash of multiple values together:

    >>> select(common.hash(Order.customer_id, Order.order_date))
    """
    return _hash(TupleVariable(args))

_uuid_to_string = library.Relation("uuid_to_string", [Field.input("uuid", Hash), Field("str", String)])

@include_in_docs
def uuid_to_string(uuid: Value) -> Expression:
    """
    Convert a UUID (Hash) to its string representation.

    Parameters
    ----------
    uuid: Value
        The UUID (Hash) value to convert.

    Returns
    -------
    Expression
        An `Expression` representing the string representation of the UUID. Returns `String`.

    Examples
    --------
    Convert UUID to string:

    >>> select(common.uuid_to_string(Session.id))
    """
    return _uuid_to_string(uuid)

_parse_uuid = library.Relation("parse_uuid", [Field.input("str", String), Field("uuid", Hash)])

@include_in_docs
def parse_uuid(s: StringValue) -> Expression:
    """
    Parse a UUID from its string representation.

    Parameters
    ----------
    s: StringValue
        The string representation of the UUID.

    Returns
    -------
    Expression
        An `Expression` representing the parsed UUID. Returns `Hash`.

    Examples
    --------
    Parse UUID from string:

    >>> select(common.parse_uuid("550e8400-e29b-41d4-a716-446655440000"))
    >>> select(common.parse_uuid(Request.session_id_str))
    """
    return _parse_uuid(s)

_raw_source = library.Relation("raw_source", [Field.input("lang", String), Field.input("source", String)])

@include_in_docs
def raw_source(lang: StringValue, source: str) -> Expression:
    """
    Attach raw source code to the transaction when the backend understands this language.

    Parameters
    ----------
    lang: StringValue
        The language identifier for the source code (e.g. `"sql"`,  `"lqp"`, or `"rel"`).
    source: str
        The source code to attach.

    Returns
    -------
    Expression
        An `Expression` representing the raw source attachment.

    Examples
    --------
    Attach raw Rel source code:

    >>> common.raw_source("rel", "def my_rule { 42 }")
    """
    return _raw_source(lang, source)
