import { range } from "@common/utils";
import type Player from "./player";
import { Players } from "./player";
import Entity from "./entity";
import Tile from "./tile";
import type Item from "./item";
import type TileMap from "./tilemap";
import { ItemType } from "./item";

export type UseListener = (player: Player, item: Item) => void;

interface ActiveTile {
    tile: Tile;
    player: Player;
    transfer?: boolean;
    heal?: boolean;
    item?: Item;
}

export default class Inventory extends Entity {
    private playerTiles: Tile[];
    private transferButtons: Tile[];
    private healButtons: Tile[];
    private tiles: Tile[][];
    private transferTarget: Player | undefined;

    constructor(public readonly map: TileMap, position: [number, number], size: [number, number]) {
        super(position, size);

        const numPlayers = Object.keys(Players).length;
        const tileSize = this.width / numPlayers;
        const numTiles = Math.floor(this.height / tileSize) - 2;

        this.playerTiles = range(numPlayers).map((x) =>
            new Tile([x * tileSize, 0], tileSize)
        );

        this.transferButtons = range(numPlayers).map((x) =>
            new Tile([x * tileSize, 1.5 * tileSize], tileSize / 2)
        );

        this.healButtons = range(numPlayers).map((x) =>
            new Tile([(x + 0.5) * tileSize, 1.5 * tileSize], tileSize / 2)
        );

        this.tiles = range(numPlayers).map((x) =>
            range(numTiles).map((y) =>
                new Tile([x * tileSize, (2 + y) * tileSize], tileSize)
            )
        );
    }

    private get players() {
        return this.map.players;
    }

    private get player() {
        return this.map.player;
    }

    private get activeTiles(): ActiveTile[] {
        return [
            ...this.playerTiles
                .slice(0, this.players.length)
                .map((tile, playerIndex) => ({
                    tile,
                    player: this.players[playerIndex],
                })),

            ...this.transferButtons
                .slice(0, this.players.length)
                .map((tile, playerIndex) => ({
                    tile,
                    player: this.players[playerIndex],
                    transfer: true,
                }))
                .filter(({ player }) =>
                    this.players.some(p => this.map.canTransferTo(p, player))
                ),

            ...this.healButtons
                .slice(0, this.players.length)
                .map((tile, playerIndex) => ({
                    tile,
                    player: this.players[playerIndex],
                    heal: true,
                }))
                .filter(({ player }) =>
                    this.players.some(healer => this.map.canHeal(healer, player))
                ),

            ...this.tiles
                .slice(0, this.players.length)
                .flatMap((column, playerIndex) =>
                    column
                        .slice(0, this.players[playerIndex].numItems)
                        .map((tile, tileIndex) => ({
                            tile,
                            player: this.players[playerIndex],
                            item: this.players[playerIndex].getItem(tileIndex),
                        }))
                        .filter(({ item, player }) => this.transferTarget
                            ? this.map.canTransferTo(player, this.transferTarget)
                            : item.isConsumable)
                )
        ];
    }

    public override handleClick(x: number, y: number): void {
        for (const { tile, item, player, transfer, heal } of this.activeTiles) {
            if (tile.isPointInBounds(x - this.left, y - this.top)) {
                if (heal) {
                    this.handleHeal(player);
                } else if (transfer) {
                    this.handleToggleTransfer(player);
                } else if (this.transferTarget && item) {
                    this.handleTransfer(player, item);
                } else if (item) {
                    this.map.handleItemUse(player, item);
                } else if (player !== this.player) {
                    player.toggleActive();
                }
                return;
            }
        }
    }

    public override handleMouseMove(x: number, y: number): void {
        this.activeTiles.forEach(({ tile }) => tile.handleMouseMove(x - this.left, y - this.top));
        this.playerTiles.forEach((tile) => tile.handleMouseMove(x - this.left, y - this.top));
    }

    private handleToggleTransfer(target: Player) {
        if (this.transferTarget === target) {
            this.transferTarget = undefined;
        } else {
            this.transferTarget = target;
        }
    }

    private handleTransfer(player: Player, item: Item) {
        if (this.transferTarget) {
            const itemExisted = player.removeItem(item);
            if (itemExisted) {
                this.transferTarget.addItem(item);
            }
        }
        this.transferTarget = undefined;
    }

    private handleHeal(target: Player) {
        const healers = [
            this.players.find(p => p.type === ItemType.CHAR_NURSE),
            target,
            ...this.players,
        ];
        for (const healer of healers) {
            if (!healer || !this.map.canHeal(healer, target)) continue;

            const itemExisted = healer.removeItem(healer.healingKit);
            if (itemExisted) {
                healer.heal(target);
                return;
            }
        }
    }

    private drawPlayer(ctx: CanvasRenderingContext2D, idx: number) {
        const player = this.players[idx];

        ctx.fillStyle = 'black';
        ctx.drawImage(player.type, 0.1, 0.1, 0.8, 0.8);
        ctx.fillText(`💖 ${player.health}`, 0.5, 1.2);

        if (player === this.player) {
            ctx.strokeStyle = 'black';
            ctx.lineWidth = 0.03;

            ctx.strokeRect(0.1, 0.1, 0.8, 0.8);
        }

        if (!player.active) {
            ctx.fillStyle = `rgba(255, 255, 255, 0.5)`;
            ctx.fillRect(0.1, 0.1, 0.8, 0.8);
        }

        let y = 2;
        for (const item of player) {
            ctx.drawImage(item.type, 0.1, 0.1 + y, 0.8, 0.8);
            y += 1;
        }
    }

    protected override draw(ctx: CanvasRenderingContext2D): void {
        const step = 1 / Object.keys(Players).length;
        const columnWidth = this.width * step;

        ctx.save();
        ctx.scale(step, columnWidth / this.height);
        ctx.font = `0.3px Arial`;
        ctx.fillStyle = 'black';

        for (const i of range(this.players.length)) {
            this.drawPlayer(ctx, i);
            ctx.translate(1, 0);
        }
        ctx.restore();

        ctx.scale(1 / this.width, 1 / this.height);

        ctx.font = `${0.3 * columnWidth}px Arial`;

        const activeTiles = new Set(this.activeTiles.map(({ tile }) => tile));

        this.transferButtons.slice(0, this.players.length)
            .map((tile, playerIdx) => ({ tile, player: this.players[playerIdx] }))
            .filter(({ tile }) => activeTiles.has(tile))
            .forEach(({ tile, player }) => {
                if (player === this.transferTarget) {
                    ctx.fillStyle = 'lime';
                    ctx.fillRect(tile.left, tile.top, tile.width, tile.height);
                }
                ctx.fillStyle = 'black';
                ctx.fillText('🎁', tile.centerX, tile.centerY);
            });

        ctx.fillStyle = 'black';
        this.healButtons.slice(0, this.players.length)
            .filter((tile) => activeTiles.has(tile))
            .forEach((tile) => {
                ctx.fillText('🩹', tile.centerX, tile.centerY);
            });

        activeTiles.forEach((tile) => tile.render(ctx));
    }
}