import {
    EmbedBuilder,
    AttachmentBuilder,
    ContainerBuilder,
    SectionBuilder,
    TextDisplayBuilder,
    MediaGalleryBuilder,
    MediaGalleryItemBuilder,
    ThumbnailBuilder,
    SeparatorBuilder,
    SeparatorSpacingSize,
    ActionRowBuilder,
    ButtonBuilder,
    ButtonStyle,
    StringSelectMenuBuilder,
    StringSelectMenuOptionBuilder,
    MessageFlags,
} from "discord.js";
import { resolveAssetUrl, isLocalAssetUrl } from "@shared/lib/assets";
import { getRarityConfig, defaultName, stripQuery } from "@shared/lib/rarity";
import { ItemType } from "@shared/lib/constants";
import type { ItemUsageData } from "@shared/lib/types";
import { join } from "path";
import { existsSync } from "fs";
import { INVENTORY_CUSTOM_IDS } from "./inventory.types";

export const ITEMS_PER_PAGE = 5;

const RARITY_SORT_ORDER: Record<string, number> = {
    SSR: 0,
    SR: 1,
    R: 2,
    C: 3,
};

export interface InventoryItem {
    id: number;
    name: string;
    description: string | null;
    rarity: string | null;
    type: string;
    price: bigint | null;
    iconUrl: string;
    imageUrl: string;
    usageData: unknown;
}

export interface InventoryEntry {
    quantity: bigint | null;
    item: InventoryItem;
}

export function sortInventoryItems(entries: InventoryEntry[]): InventoryEntry[] {
    return [...entries].sort((a, b) => {
        const rarityA = RARITY_SORT_ORDER[a.item.rarity ?? "C"] ?? 3;
        const rarityB = RARITY_SORT_ORDER[b.item.rarity ?? "C"] ?? 3;
        if (rarityA !== rarityB) return rarityA - rarityB;
        return a.item.name.localeCompare(b.item.name);
    });
}

export function getInventoryListMessage(
    entries: InventoryEntry[],
    username: string,
    page: number,
    viewerId: string,
    ownerId: string,
) {
    const sorted = sortInventoryItems(entries);
    const totalPages = Math.max(1, Math.ceil(sorted.length / ITEMS_PER_PAGE));
    const safePage = Math.min(page, totalPages - 1);
    const pageItems = sorted.slice(safePage * ITEMS_PER_PAGE, (safePage + 1) * ITEMS_PER_PAGE);

    // Accent color from highest-rarity item on page
    const highestRarity = pageItems[0]?.item.rarity ?? "C";
    const accentColor = getRarityConfig(highestRarity).color;

    const container = new ContainerBuilder()
        .setAccentColor(accentColor)
        .addTextDisplayComponents(
            new TextDisplayBuilder().setContent(`# 📦 ${username}'s Inventory`),
            new TextDisplayBuilder().setContent(`-# ${sorted.length} item${sorted.length !== 1 ? "s" : ""} total`)
        );

    container.addSeparatorComponents(
        new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small)
    );

    // Item rows
    const lines = pageItems.map((entry) => {
        const rc = getRarityConfig(entry.item.rarity ?? "C");
        return `${rc.squareEmoji} **${entry.item.name}** — ${rc.label} · ${entry.item.type} · ×${entry.quantity}`;
    });

    container.addTextDisplayComponents(
        new TextDisplayBuilder().setContent(lines.join("\n"))
    );

    container.addSeparatorComponents(
        new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small)
    );

    // Select menu with current page items
    const selectMenu = new StringSelectMenuBuilder()
        .setCustomId(INVENTORY_CUSTOM_IDS.SELECT(viewerId))
        .setPlaceholder("Select an item for details");

    for (const entry of pageItems) {
        const rc = getRarityConfig(entry.item.rarity ?? "C");
        selectMenu.addOptions(
            new StringSelectMenuOptionBuilder()
                .setLabel(entry.item.name)
                .setDescription(`${rc.label} · ${entry.item.type}`)
                .setValue(entry.item.id.toString())
        );
    }

    container.addActionRowComponents(
        new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(selectMenu)
    );

    // Pagination buttons
    const navRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
        new ButtonBuilder()
            .setCustomId(INVENTORY_CUSTOM_IDS.PREV(viewerId))
            .setLabel("◀ Previous")
            .setStyle(ButtonStyle.Secondary)
            .setDisabled(safePage <= 0),
        new ButtonBuilder()
            .setCustomId(INVENTORY_CUSTOM_IDS.PAGE(viewerId))
            .setLabel(`Page ${safePage + 1}/${totalPages}`)
            .setStyle(ButtonStyle.Secondary)
            .setDisabled(true),
        new ButtonBuilder()
            .setCustomId(INVENTORY_CUSTOM_IDS.NEXT(viewerId))
            .setLabel("Next ▶")
            .setStyle(ButtonStyle.Secondary)
            .setDisabled(safePage >= totalPages - 1),
    );

    container.addActionRowComponents(navRow);

    return {
        components: [container] as any,
        files: [] as AttachmentBuilder[],
        flags: MessageFlags.IsComponentsV2,
        embeds: [],
    };
}

export function getEmptyInventoryMessage(username: string) {
    const container = new ContainerBuilder()
        .setAccentColor(0x95A5A6)
        .addTextDisplayComponents(
            new TextDisplayBuilder().setContent(`# 📦 ${username}'s Inventory`),
            new TextDisplayBuilder().setContent("*No items yet. Visit the shop or complete quests to earn items!*")
        );

    return {
        components: [container] as any,
        files: [],
        flags: MessageFlags.IsComponentsV2,
        embeds: [],
    };
}

export function getItemDetailMessage(
    entry: InventoryEntry,
    viewerId: string,
    ownerId: string,
) {
    const { item } = entry;
    const rc = getRarityConfig(item.rarity ?? "C");
    const files: AttachmentBuilder[] = [];

    const container = new ContainerBuilder().setAccentColor(rc.color);

    // Header section with thumbnail
    const section = new SectionBuilder().addTextDisplayComponents(
        new TextDisplayBuilder().setContent(`${rc.squareEmoji} **${item.name}**`),
        new TextDisplayBuilder().setContent(`-# ${rc.label} · ${item.type}`)
    );

    // Resolve icon thumbnail
    const iconUrl = resolveItemUrl(item.iconUrl, files);
    if (iconUrl) {
        section.setThumbnailAccessory(new ThumbnailBuilder().setURL(iconUrl));
    }

    container.addSectionComponents(section);

    // Artwork via MediaGallery
    const imageUrl = resolveItemUrl(item.imageUrl, files);
    if (imageUrl && item.imageUrl !== item.iconUrl) {
        container.addMediaGalleryComponents(
            new MediaGalleryBuilder().addItems(
                new MediaGalleryItemBuilder().setURL(imageUrl)
            )
        );
    }

    // Description
    if (item.description) {
        container.addTextDisplayComponents(
            new TextDisplayBuilder().setContent(item.description)
        );
    }

    container.addSeparatorComponents(
        new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small)
    );

    // Stats row
    const priceText = item.price ? `${item.price} 🪙` : "Not purchasable";
    container.addTextDisplayComponents(
        new TextDisplayBuilder().setContent(
            `Owned: **×${entry.quantity}** · Value: **${priceText}**`
        )
    );

    // Action buttons
    const isOwner = viewerId === ownerId;
    const usageData = item.usageData as ItemUsageData | null;
    const isUsable = isOwner && item.type === ItemType.CONSUMABLE &&
        usageData?.effects && usageData.effects.length > 0;

    const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
        new ButtonBuilder()
            .setCustomId(INVENTORY_CUSTOM_IDS.BACK(viewerId))
            .setLabel("◀ Back")
            .setStyle(ButtonStyle.Primary)
    );

    if (isUsable) {
        actionRow.addComponents(
            new ButtonBuilder()
                .setCustomId(INVENTORY_CUSTOM_IDS.USE(viewerId))
                .setLabel("🧪 Use")
                .setStyle(ButtonStyle.Success)
        );
    }

    if (isOwner) {
        actionRow.addComponents(
            new ButtonBuilder()
                .setCustomId(INVENTORY_CUSTOM_IDS.DISCARD(viewerId))
                .setLabel("🗑 Discard")
                .setStyle(ButtonStyle.Danger)
        );
    }

    container.addActionRowComponents(actionRow);

    return {
        components: [container] as any,
        files,
        flags: MessageFlags.IsComponentsV2,
        embeds: [],
    };
}

export function getDiscardConfirmMessage(entry: InventoryEntry, viewerId: string) {
    const rc = getRarityConfig(entry.item.rarity ?? "C");

    const container = new ContainerBuilder()
        .setAccentColor(0xED4245)
        .addTextDisplayComponents(
            new TextDisplayBuilder().setContent(
                `Are you sure you want to discard 1× **${entry.item.name}**?`
            )
        )
        .addActionRowComponents(
            new ActionRowBuilder<ButtonBuilder>().addComponents(
                new ButtonBuilder()
                    .setCustomId(INVENTORY_CUSTOM_IDS.DISCARD_CONFIRM(viewerId))
                    .setLabel("Confirm")
                    .setStyle(ButtonStyle.Danger),
                new ButtonBuilder()
                    .setCustomId(INVENTORY_CUSTOM_IDS.DISCARD_CANCEL(viewerId))
                    .setLabel("Cancel")
                    .setStyle(ButtonStyle.Secondary)
            )
        );

    return {
        components: [container] as any,
        files: [],
        flags: MessageFlags.IsComponentsV2,
        embeds: [],
    };
}

/**
 * Wraps a use-item result message with a Back button so the user
 * can return to the inventory after seeing the effect result.
 */
export function appendUseBackButton(message: any, viewerId: string): any {
    const backRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
        new ButtonBuilder()
            .setCustomId(INVENTORY_CUSTOM_IDS.USE_BACK(viewerId))
            .setLabel("◀ Back to Inventory")
            .setStyle(ButtonStyle.Primary)
    );

    // If CV2 message with components array, append to the first container
    if (message.components && message.flags === MessageFlags.IsComponentsV2) {
        const container = message.components[0];
        if (container?.addActionRowComponents) {
            container.addActionRowComponents(backRow);
        }
        return message;
    }

    // Embed-based fallback — add as a regular component row
    return {
        ...message,
        components: [...(message.components || []), backRow],
    };
}

/**
 * Resolves an item URL (icon or image) for use in CV2 components.
 * Handles both local assets and remote URLs.
 * Pushes AttachmentBuilders to `files` array for local assets.
 */
function resolveItemUrl(url: string | null | undefined, files: AttachmentBuilder[]): string | null {
    if (!url) return null;

    if (isLocalAssetUrl(url)) {
        const filePath = join(process.cwd(), "bot/assets/graphics", stripQuery(url).replace(/^\/?assets\//, ""));
        if (existsSync(filePath)) {
            const fileName = defaultName(url);
            if (!files.find(f => f.name === fileName)) {
                files.push(new AttachmentBuilder(filePath, { name: fileName }));
            }
            return `attachment://${fileName}`;
        }
        return null;
    }

    return resolveAssetUrl(url);
}

/**
 * Creates a Components V2 message showing the result of opening a lootbox.
 * Falls back to a simple embed for non-lootbox item usage.
 */
export function getLootboxResultMessage(
    results: any[],
    item?: { name: string; iconUrl: string | null; imageUrl: string | null; usageData: any }
) {
    const files: AttachmentBuilder[] = [];
    const otherMessages: string[] = [];
    let lootResult: any = null;

    for (const res of results) {
        if (typeof res === "object" && res.type === "LOOTBOX_RESULT") {
            lootResult = res;
        } else {
            otherMessages.push(typeof res === "string" ? `• ${res}` : `• ${JSON.stringify(res)}`);
        }
    }

    // If no loot result, fall back to a simple embed (non-lootbox item usage)
    if (!lootResult) {
        const embed = new EmbedBuilder()
            .setTitle(item ? `✅ Used ${item.name}` : "✅ Item Used!")
            .setDescription(otherMessages.join("\n") || "Effect applied.")
            .setColor(0x2ecc71)
            .setTimestamp();
        return { embeds: [embed], files, components: undefined, flags: undefined };
    }

    // Determine rarity key for theming
    let rarityKey = "C";
    if (lootResult.rewardType === "ITEM" && lootResult.item) {
        rarityKey = lootResult.item.rarity || "C";
    } else if (lootResult.rewardType === "CURRENCY") {
        rarityKey = "CURRENCY";
    } else if (lootResult.rewardType === "XP") {
        rarityKey = "XP";
    } else {
        rarityKey = "NOTHING";
    }

    const config = getRarityConfig(rarityKey);
    const container = new ContainerBuilder().setAccentColor(config.color);

    // Header: lootbox name
    if (item?.name) {
        container.addTextDisplayComponents(
            new TextDisplayBuilder().setContent(`-# Opened: ${item.name}`)
        );
    }

    // Build title and description based on reward type
    let title = "";
    let description = "";

    if (lootResult.rewardType === "ITEM" && lootResult.item) {
        const i = lootResult.item;
        const amountStr = lootResult.amount > 1 ? ` ×${lootResult.amount}` : "";
        title = `${config.emoji} ${config.label} — ${i.name}${amountStr}`;
        description = i.description || "";
        description += (description ? "\n\n" : "") + `**${config.label}** · ×${lootResult.amount || 1} added to inventory`;
    } else if (lootResult.rewardType === "CURRENCY") {
        title = `${config.emoji} You found ${lootResult.amount.toLocaleString()} AU!`;
        description = "Coins have been added to your balance.";
    } else if (lootResult.rewardType === "XP") {
        title = `${config.emoji} You gained ${lootResult.amount.toLocaleString()} XP!`;
        description = "Experience has been added to your profile.";
    } else {
        title = `${config.emoji} Empty...`;
        description = lootResult.message || "You found nothing inside.";
    }

    // Main section with optional thumbnail
    const section = new SectionBuilder().addTextDisplayComponents(
        new TextDisplayBuilder().setContent(`# ${title}`),
        new TextDisplayBuilder().setContent(description)
    );

    // Thumbnail from iconUrl (use reward item's icon for ITEM, lootbox icon otherwise)
    let thumbnailUrl: string | null = null;
    const iconSource = lootResult.rewardType === "ITEM" ? lootResult.item?.iconUrl : item?.iconUrl;
    if (iconSource) {
        if (isLocalAssetUrl(iconSource)) {
            const iconPath = join(process.cwd(), "bot/assets/graphics", stripQuery(iconSource).replace(/^\/?assets\//, ""));
            if (existsSync(iconPath)) {
                const iconName = defaultName(iconSource);
                files.push(new AttachmentBuilder(iconPath, { name: iconName }));
                thumbnailUrl = `attachment://${iconName}`;
            }
        } else {
            thumbnailUrl = resolveAssetUrl(iconSource);
        }
    }

    if (thumbnailUrl) {
        section.setThumbnailAccessory(new ThumbnailBuilder().setURL(thumbnailUrl));
    }

    container.addSectionComponents(section);

    // Media gallery for full item art (if imageUrl differs from iconUrl)
    if (lootResult.rewardType === "ITEM" && lootResult.item) {
        const imgSource = lootResult.item.imageUrl;
        const iconSrc = lootResult.item.iconUrl;
        if (imgSource && imgSource !== iconSrc) {
            let displayImageUrl: string | null = null;
            if (isLocalAssetUrl(imgSource)) {
                const imagePath = join(process.cwd(), "bot/assets/graphics", stripQuery(imgSource).replace(/^\/?assets\//, ""));
                if (existsSync(imagePath)) {
                    const imageName = defaultName(imgSource);
                    if (!files.find(f => f.name === imageName)) {
                        files.push(new AttachmentBuilder(imagePath, { name: imageName }));
                    }
                    displayImageUrl = `attachment://${imageName}`;
                }
            } else {
                displayImageUrl = resolveAssetUrl(imgSource);
            }
            if (displayImageUrl) {
                container.addMediaGalleryComponents(
                    new MediaGalleryBuilder().addItems(
                        new MediaGalleryItemBuilder().setURL(displayImageUrl)
                    )
                );
            }
        }
    }

    // Other effects (non-lootbox results like temp roles, XP boosts)
    if (otherMessages.length > 0) {
        container.addSeparatorComponents(new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small));
        container.addTextDisplayComponents(
            new TextDisplayBuilder().setContent(`**Other Effects**\n${otherMessages.join("\n")}`)
        );
    }

    return {
        // TODO: remove cast once discord.js types include ContainerBuilder in MessageEditOptions
        components: [container] as any,
        files,
        flags: MessageFlags.IsComponentsV2,
        embeds: undefined,
    };
}
