"""
Regular expression function.

This module provides regex pattern matching and manipulation functions including:
- Pattern matching
- Finding all matches
- String substitution
- Match object with position and grouping support
"""
from __future__ import annotations


from . import StringValue, IntegerValue, strings, _function_not_implemented
from ..frontend.base import Library, Expression, Field, Variable
from ..frontend.core import String, Integer
from relationalai.util.docutils import include_in_docs
from enum import Enum

__include_in_docs__ = True

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

#--------------------------------------------------
# Relationships
#--------------------------------------------------

_regex_match_all = library.Relation("regex_match_all", [Field.input("regex", String), Field.input("value", String), Field("pos", Integer), Field("match", String)])
# _regex_replace = library.Relation("regex_replace", [Field.input("regex", String), Field.input("replacement", String), Field.input("value", String), Field("result", String)])

# _regex_escape = library.Relation("regex_escape", [Field.input("regex", String), Field("result", String)])
# _capture_group_by_index = library.Relation("capture_group_by_index", [Field.input("regex", String), Field.input("string", String), Field.input("pos", Integer), Field.input("index", Integer), Field("result", String)])
# _capture_group_by_name = library.Relation("capture_group_by_name", [Field.input("regex", String), Field.input("string", String), Field.input("pos", Integer), Field.input("name", String), Field("result", String)])


#--------------------------------------------------
# Operations
#--------------------------------------------------

#  match is ^{REGEX} , search is {REGEX}, and fullmatch is ^{REGEX}$

@include_in_docs
def match(regex: StringValue, value: StringValue) -> RegexMatch:
    """
    Check if the regex matches the value from the start.

    Parameters
    ----------
    regex: StringValue
        The regular expression pattern.
    value: StringValue
        The string value to match against.

    Returns
    -------
    RegexMatch
        A `RegexMatch` object representing the match result.

    Examples
    --------
    Match a pattern from the start of a string:

    >>> re.match(r"S.*", Person.name)
    >>> select(Person.name).where(re.match(r"^[A-Z]", Person.name))
    """
    # REGEXP_LIKE
    return RegexMatch(regex, value, type=RegexMatchType.MATCH)

@include_in_docs
def search(regex: StringValue, value: StringValue, pos: IntegerValue = 0) -> RegexMatch:
    """
    Search for the regex pattern in the value starting at the given position.

    .. note::
       This function is not yet implemented.

    Parameters
    ----------
    regex: StringValue
        The regular expression pattern.
    value: StringValue
        The string value to search in.
    pos: IntegerValue
        The starting position for the search (0-based). Default: 0.

    Returns
    -------
    RegexMatch
        A `RegexMatch` object representing the search result.
    """
    _function_not_implemented("re.search")
    return RegexMatch(regex, value, pos, type=RegexMatchType.SEARCH)

@include_in_docs
def fullmatch(regex: StringValue, value: StringValue, pos: IntegerValue = 0) -> RegexMatch:
    """
    Check if the regex matches the entire value starting at the given position.

    Parameters
    ----------
    regex: StringValue
        The regular expression pattern.
    value: StringValue
        The string value to match against.
    pos: IntegerValue
        The starting position for the match (0-based). Default: 0.

    Returns
    -------
    RegexMatch
        A `RegexMatch` object representing the match result.

    Examples
    --------
    Match entire string against a pattern:

    >>> re.fullmatch(r"[A-Z][a-z]+", Person.name)
    >>> select(Email.address).where(re.fullmatch(r"\\w+@\\w+\\.\\w+", Email.address))
    """
    return RegexMatch(regex, value, pos, type=RegexMatchType.FULLMATCH)

@include_in_docs
def findall(regex: StringValue, value: StringValue) -> tuple[Variable, Variable]:
    """
    Find all non-overlapping matches of the regex in the value.

    .. note::
       This function is not yet implemented.

    Parameters
    ----------
    regex: StringValue
        The regular expression pattern.
    value: StringValue
        The string value to search in.

    Returns
    -------
    tuple[Variable, Variable]
        A tuple of (position, match) `Variable`s representing all matches. Position is `Integer`, match is `String`.
    """
    _function_not_implemented("re.findall")
    # exp = _regex_match_all(regex, value)
    # ix, match = exp._arg_ref(2), exp._arg_ref(3)
    # rank = i.rank(i.asc(ix, match))
    # return rank, match
    raise

@include_in_docs
def sub(regex: StringValue, repl: StringValue, value: StringValue):
    """
    Replace occurrences of the regex in the value with the replacement string.

    .. note::
       This function is not yet implemented.

    Parameters
    ----------
    regex: StringValue
        The regular expression pattern.
    repl: StringValue
        The replacement string.
    value: StringValue
        The string value to perform substitution on.

    Returns
    -------
    Expression
        An `Expression` computing the substituted string. Returns `String`.
    """
    _function_not_implemented("re.sub")
    # return _regex_replace(regex, repl, value)

class RegexMatchType(Enum):
    """Enumeration of regex match types."""
    MATCH = "match"
    SEARCH = "search"
    FULLMATCH = "fullmatch"

@include_in_docs
class RegexMatch(Expression):
    """
    Represents a regex match result with position and grouping support.

    Attributes
    ----------
    regex: StringValue
        The regex pattern used for matching.
    value: StringValue
        The string value being matched.
    pos: IntegerValue
        The starting position of the match (0-based).
    match: Variable
        A Variable representing the matched string.
    """

    def __init__(self, regex: StringValue, value: StringValue, pos: IntegerValue = 0, type=RegexMatchType.MATCH):
        """
        Initialize a RegexMatch object.

        Parameters
        ----------
        regex: StringValue
            The regular expression pattern.
        value: StringValue
            The string value to match against.
        pos: IntegerValue
            The starting position for the match (0-based). Default: 0.
        type: RegexMatchType
            The type of match (MATCH, SEARCH, or FULLMATCH). Default: MATCH.
        """
        if type == RegexMatchType.FULLMATCH:
            # fullmatch: ^{REGEX}$
            self.regex = strings.concat(regex, "$")
        else:
            self.regex = regex
        self.value = value
        # pos is the 0-based index in value where we start matching
        self.pos = pos
        # return value is the matched string
        self.match = String.ref("match")
        super().__init__(_regex_match_all, (self.regex, value, self.pos, self.match))

    @include_in_docs
    def start(self) -> IntegerValue:
        """
        Get the starting position of the match.

        Returns
        -------
        IntegerValue
            The 0-based starting position of the match.
        """
        return self.pos

    @include_in_docs
    def end(self) -> IntegerValue:
        """
        Get the ending position of the match.

        Returns
        -------
        IntegerValue
            The 0-based ending position of the match.
        """
        return strings.len(self.match) + self.pos - 1

    @include_in_docs
    def span(self) -> tuple[IntegerValue, IntegerValue]:
        """
        Get the start and end positions of the match.

        Returns
        -------
        tuple[IntegerValue, IntegerValue]
            A tuple of (start, end) positions.
        """
        return self.start(), self.end()

    @include_in_docs
    def group(self, index: IntegerValue = 0) -> Variable:
        """
        Get a capture group by index.

        .. note::
           This method is not yet implemented.

        Parameters
        ----------
        index: IntegerValue
            The index of the capture group (0 for the entire match). Default: 0.

        Returns
        -------
        Variable
            A Variable representing the capture group content.
        """
        _function_not_implemented("re.RegexMatch.group")
        raise

    @include_in_docs
    def group_by_name(self, name: StringValue) -> Variable:
        """
        Get a named capture group.

        .. note::
           This method is not yet implemented.

        Parameters
        ----------
        name: StringValue
            The name of the capture group.

        Returns
        -------
        Variable
            A Variable representing the named capture group content.
        """
        _function_not_implemented("re.RegexMatch.group_by_name")
        raise

#--------------------------------------------------
# Helpers
#--------------------------------------------------

# def _regex_match_all(regex: StringValue, string: StringValue, pos: IntegerValue|None = None) -> i.Expression:
#     if pos is None:
#         pos = i.Int64.ref()
#     return _make_expr("regex_match_all", regex, string, pos, i.StringValue.ref())
