# ╔══════════════════════════════════════════════════════════════════╗
# ║                                                                  ║
# ║   ░█▀▀░█▀█░█▀▄░█▀▀░█░█   ░█▀▄░█▀▀░█░█░█▀▀                     ║
# ║   ░█░░░█░█░█░█░█▀▀░▄▀▄   ░█░█░█▀▀░▀▄▀░▀▀█                     ║
# ║   ░▀▀▀░▀▀▀░▀▀░░▀▀▀░▀░▀   ░▀▀░░▀▀▀░░▀░░▀▀▀                     ║
# ║                                                                  ║
# ║            © 2026 CodeX Devs — All Rights Reserved              ║
# ║                                                                  ║
# ║   discord  ──  https://discord.gg/codexdev                      ║
# ║   youtube  ──  https://youtube.com/@CodeXDevs                   ║
# ║   github   ──  https://github.com/RayExo                        ║
# ║                                                                  ║
# ╚══════════════════════════════════════════════════════════════════╝

from __future__ import annotations

from typing import TYPE_CHECKING, Optional, Literal
import random

import discord
from discord.ext import commands

from ..utils import *

if TYPE_CHECKING:
    from typing_extensions import TypeAlias

    Board: TypeAlias = list[list[Optional[int]]]


class SlideButton(discord.ui.Button["SlideView"]):
    def __init__(self, label: str, *, style: discord.ButtonStyle, row: int) -> None:
        super().__init__(
            label=label,
            style=style,
            row=row,
        )

        if label == "\u200b":
            self.disabled = True

    async def callback(self, interaction: discord.Interaction) -> None:
        game = self.view.game

        if interaction.user != game.player:
            return await interaction.response.send_message(
                "This is not your game!", ephemeral=True
            )
        else:
            num = int(self.label)

            if num not in game.beside_blank():
                return await interaction.response.defer()
            else:
                ix, iy = game.get_item(num)
                nx, ny = game.get_item()

                game.numbers[nx][ny], game.numbers[ix][iy] = (
                    game.numbers[ix][iy],
                    game.numbers[nx][ny],
                )

                self.view.update_board(clear=True)

                game.moves += 1
                game.embed.set_field_at(
                    0, name="\u200b", value=f"Moves: `{game.moves}`"
                )

                if game.numbers == game.completed:
                    self.view.disable_all()
                    self.view.stop()
                    game.embed.description = "**Congrats! You won!**"

                return await interaction.response.edit_message(
                    embed=game.embed, view=self.view
                )


class SlideView(BaseView):
    def __init__(self, game: NumberSlider, *, timeout: float) -> None:
        super().__init__(timeout=timeout)

        self.game = game

        self.update_board()

    def update_board(self, *, clear: bool = False) -> None:

        if clear:
            self.clear_items()

        for i, row in enumerate(self.game.numbers):
            for j, number in enumerate(row):
                if number == self.game.completed[i][j]:
                    style = self.game.correct_style
                else:
                    style = self.game.wrong_style

                button = SlideButton(
                    label=number or "\u200b",
                    style=style,
                    row=i,
                )
                self.add_item(button)


class NumberSlider:
    """
    Number Slider Game
    """

    def __init__(self, count: Literal[1, 2, 3, 4, 5] = 4) -> None:

        if count not in range(1, 6):
            raise ValueError("Count must be an integer between 1 and 5")

        self.all_numbers = list(range(1, count**2))

        self.player: Optional[discord.User] = None

        self.moves: int = 0
        self.count = count
        self.numbers: Board = []
        self.completed: Board = []

        self.wrong_style: discord.ButtonStyle = discord.ButtonStyle.gray
        self.correct_style: discord.ButtonStyle = discord.ButtonStyle.green

    def get_item(self, obj: Optional[int] = None) -> tuple[int, int]:
        return next(
            (x, y)
            for x, row in enumerate(self.numbers)
            for y, item in enumerate(row)
            if item == obj
        )

    def beside_blank(self) -> list[int]:
        nx, ny = self.get_item()

        beside_item = [
            (nx - 1, ny),
            (nx, ny - 1),
            (nx + 1, ny),
            (nx, ny + 1),
        ]

        data = [
            self.numbers[i][j]
            for i, j in beside_item
            if i in range(self.count) and j in range(self.count)
        ]
        return data

    async def start(
        self,
        ctx: commands.Context[commands.Bot],
        *,
        wrong_style: discord.ButtonStyle = discord.ButtonStyle.gray,
        correct_style: discord.ButtonStyle = discord.ButtonStyle.green,
        embed_color: DiscordColor = DEFAULT_COLOR,
        timeout: Optional[float] = None,
    ) -> discord.Message:
        """
        starts the number slider game

        Parameters
        ----------
        ctx : commands.Context
            the context of the invokation command
        wrong_style : discord.ButtonStyle, optional
            the button style to use for tiles that are in the wrong spot, by default discord.ButtonStyle.gray
        correct_style : discord.ButtonStyle, optional
            the button style to use for tiles that are in the right spot, by default discord.ButtonStyle.green
        embed_color : DiscordColor, optional
            the game embedd color, by default DEFAULT_COLOR
        timeout : Optional[float], optional
            the timeout for the view, by default None

        Returns
        -------
        discord.Message
            returns the game message
        """
        self.player = ctx.author
        self.wrong_style = wrong_style
        self.correct_style = correct_style

        numbers = self.all_numbers[:]
        random.shuffle(numbers)
        random.shuffle(numbers)

        numbers.append(None)
        self.numbers = chunk(numbers, count=self.count)

        self.completed = chunk(self.all_numbers + [None], count=self.count)

        self.view = SlideView(self, timeout=timeout)
        self.embed = discord.Embed(
            description="Slide the tiles back in ascending order!", color=discord.Color.random()
        )
        self.embed.add_field(name="\u200b", value="Moves: `0`")

        self.message = await ctx.send(embed=self.embed, view=self.view)

        await double_wait(
            wait_for_delete(ctx, self.message),
            self.view.wait(),
        )
        return self.message
