# dialects/mysql/expression.py
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php

from __future__ import annotations

from typing import Any

from ... import exc
from ... import util
from ...sql import coercions
from ...sql import elements
from ...sql import operators
from ...sql import roles
from ...sql.base import _generative
from ...sql.base import Generative
from ...util.typing import Self


class match(Generative, elements.BinaryExpression[Any]):
    """Produce a ``MATCH (X, Y) AGAINST ('TEXT')`` clause.

    E.g.::

        from sqlalchemy import desc
        from sqlalchemy.dialects.mysql import match

        match_expr = match(
            users_table.c.firstname,
            users_table.c.lastname,
            against="Firstname Lastname",
        )

        stmt = (
            select(users_table)
            .where(match_expr.in_boolean_mode())
            .order_by(desc(match_expr))
        )

    Would produce SQL resembling:

    .. sourcecode:: sql

        SELECT id, firstname, lastname
        FROM user
        WHERE MATCH(firstname, lastname) AGAINST (:param_1 IN BOOLEAN MODE)
        ORDER BY MATCH(firstname, lastname) AGAINST (:param_2) DESC

    The :func:`_mysql.match` function is a standalone version of the
    :meth:`_sql.ColumnElement.match` method available on all
    SQL expressions, as when :meth:`_expression.ColumnElement.match` is
    used, but allows to pass multiple columns

    :param cols: column expressions to match against

    :param against: expression to be compared towards

    :param in_boolean_mode: boolean, set "boolean mode" to true

    :param in_natural_language_mode: boolean , set "natural language" to true

    :param with_query_expansion: boolean, set "query expansion" to true

    .. versionadded:: 1.4.19

    .. seealso::

        :meth:`_expression.ColumnElement.match`

    """

    __visit_name__ = "mysql_match"

    inherit_cache = True
    modifiers: util.immutabledict[str, Any]

    def __init__(self, *cols: elements.ColumnElement[Any], **kw: Any):
        if not cols:
            raise exc.ArgumentError("columns are required")

        against = kw.pop("against", None)

        if against is None:
            raise exc.ArgumentError("against is required")
        against = coercions.expect(
            roles.ExpressionElementRole,
            against,
        )

        left = elements.BooleanClauseList._construct_raw(
            operators.comma_op,
            clauses=cols,
        )
        left.group = False

        flags = util.immutabledict(
            {
                "mysql_boolean_mode": kw.pop("in_boolean_mode", False),
                "mysql_natural_language": kw.pop(
                    "in_natural_language_mode", False
                ),
                "mysql_query_expansion": kw.pop("with_query_expansion", False),
            }
        )

        if kw:
            raise exc.ArgumentError("unknown arguments: %s" % (", ".join(kw)))

        super().__init__(left, against, operators.match_op, modifiers=flags)

    @_generative
    def in_boolean_mode(self) -> Self:
        """Apply the "IN BOOLEAN MODE" modifier to the MATCH expression.

        :return: a new :class:`_mysql.match` instance with modifications
         applied.
        """

        self.modifiers = self.modifiers.union({"mysql_boolean_mode": True})
        return self

    @_generative
    def in_natural_language_mode(self) -> Self:
        """Apply the "IN NATURAL LANGUAGE MODE" modifier to the MATCH
        expression.

        :return: a new :class:`_mysql.match` instance with modifications
         applied.
        """

        self.modifiers = self.modifiers.union({"mysql_natural_language": True})
        return self

    @_generative
    def with_query_expansion(self) -> Self:
        """Apply the "WITH QUERY EXPANSION" modifier to the MATCH expression.

        :return: a new :class:`_mysql.match` instance with modifications
         applied.
        """

        self.modifiers = self.modifiers.union({"mysql_query_expansion": True})
        return self
