import mlx.core as mx
from mlx import nn

from mflux.config.config import Config


class ResnetBlock2D(nn.Module):
    def __init__(
        self,
        norm1: int,
        conv1_in: int,
        conv1_out: int,
        norm2: int,
        conv2_in: int,
        conv2_out: int,
        conv_shortcut_in: int | None = None,
        conv_shortcut_out: int | None = None,
        is_conv_shortcut: bool = False,
    ):
        super().__init__()
        self.norm1 = nn.GroupNorm(
            num_groups=32,
            dims=norm1,
            eps=1e-6,
            affine=True,
            pytorch_compatible=True,
        )
        self.norm2 = nn.GroupNorm(
            num_groups=32,
            dims=norm2,
            eps=1e-6,
            affine=True,
            pytorch_compatible=True,
        )
        self.conv1 = nn.Conv2d(
            in_channels=conv1_in,
            out_channels=conv1_out,
            kernel_size=(3, 3),
            stride=(1, 1),
            padding=(1, 1),
        )
        self.conv2 = nn.Conv2d(
            in_channels=conv2_in,
            out_channels=conv2_out,
            kernel_size=(3, 3),
            stride=(1, 1),
            padding=(1, 1),
        )
        self.is_conv_shortcut = is_conv_shortcut
        self.conv_shortcut = (
            nn.Conv2d(
                in_channels=conv_shortcut_in,
                out_channels=conv_shortcut_out,
                kernel_size=(1, 1),
                stride=(1, 1),
            )
            if is_conv_shortcut
            else None
        )

    def __call__(self, input_array: mx.array) -> mx.array:
        input_array = mx.transpose(input_array, (0, 2, 3, 1))
        hidden_states = self.norm1(input_array.astype(mx.float32)).astype(Config.precision)
        hidden_states = nn.silu(hidden_states)
        hidden_states = self.conv1(hidden_states)
        hidden_states = self.norm2(hidden_states.astype(mx.float32)).astype(Config.precision)
        hidden_states = nn.silu(hidden_states)
        hidden_states = self.conv2(hidden_states)
        if self.conv_shortcut is not None:
            input_array = self.conv_shortcut(input_array)
        output_tensor = input_array + hidden_states
        return mx.transpose(output_tensor, (0, 3, 1, 2))
