import jax.numpy as jnp

from pytensor.link.jax.dispatch.basic import jax_funcify
from pytensor.tensor.subtensor import (
    AdvancedIncSubtensor,
    AdvancedSubtensor,
    IncSubtensor,
    Subtensor,
    indices_from_subtensor,
)


BOOLEAN_MASK_ERROR = """JAX does not support resizing arrays with boolean
masks. In some cases, however, it is possible to re-express your model
in a form that JAX can compile:

>>> import pytensor.tensor as pt
>>> x_pt = pt.vector('x')
>>> y_pt = x_pt[x_pt > 0].sum()

can be re-expressed as:

>>> import pytensor.tensor as pt
>>> x_pt = pt.vector('x')
>>> y_pt = pt.where(x_pt > 0, x_pt, 0).sum()
"""

DYNAMIC_SLICE_LENGTH_ERROR = """JAX does not support slicing arrays with a dynamic
slice length.
"""


@jax_funcify.register(Subtensor)
@jax_funcify.register(AdvancedSubtensor)
def jax_funcify_Subtensor(op, node, **kwargs):
    def subtensor(x, *ilists):
        indices = indices_from_subtensor(ilists, op.idx_list)
        if len(indices) == 1:
            indices = indices[0]

        return x.__getitem__(indices)

    return subtensor


@jax_funcify.register(IncSubtensor)
@jax_funcify.register(AdvancedIncSubtensor)
def jax_funcify_IncSubtensor(op, node, **kwargs):
    if getattr(op, "set_instead_of_inc", False):

        def jax_fn(x, indices, y):
            return x.at[indices].set(y)

    else:

        def jax_fn(x, indices, y):
            return x.at[indices].add(y)

    def incsubtensor(x, y, *ilist, jax_fn=jax_fn, idx_list=op.idx_list):
        indices = indices_from_subtensor(ilist, idx_list)
        if len(indices) == 1:
            indices = indices[0]

        if isinstance(op, AdvancedIncSubtensor):
            # jax_typify downgrades 0d arrays to Python scalars, which have no .shape,
            # so re-arrayify y for a check that is written against array values.
            op._check_runtime_broadcast_of_vector_index(
                node, x, jnp.asarray(y), indices
            )

        return jax_fn(x, indices, y)

    return incsubtensor
