import { BadgeRarityKey } from './BadgeLeaderboardApi';

/**
 * Badge rarity tiers as the official client numbers them
 * (com.sulake.habbo.communication.enum, "BadgeRarity"): the selected-badges
 * packet carries the tier id per slot and every label / colour rule keys
 * off that id. The uncommon tier only counts as its own tier when the
 * `badge_rarity.uncommon` configuration flag is on; otherwise it collapses
 * into "common", exactly like the AIR client does.
 */
export const BadgeRarityTier = {
    COMMON: 0,
    UNCOMMON: 1,
    RARE: 2,
    EPIC: 3,
    MYTHICAL: 4,
    LEGENDARY: 5,
    UNIQUE: 6
} as const;

export type BadgeRarityTierId = (typeof BadgeRarityTier)[keyof typeof BadgeRarityTier];

export type BadgeRarityTierKey = BadgeRarityKey | 'uncommon';

const TIER_BY_KEY: Record<BadgeRarityTierKey, BadgeRarityTierId> = {
    common: BadgeRarityTier.COMMON,
    uncommon: BadgeRarityTier.UNCOMMON,
    rare: BadgeRarityTier.RARE,
    epic: BadgeRarityTier.EPIC,
    mythical: BadgeRarityTier.MYTHICAL,
    legendary: BadgeRarityTier.LEGENDARY,
    unique: BadgeRarityTier.UNIQUE
};

const LOCALIZATION_KEY_BY_TIER: Record<number, string> = {
    [BadgeRarityTier.UNCOMMON]: 'badge.rarity.uncommon',
    [BadgeRarityTier.RARE]: 'badge.rarity.rare',
    [BadgeRarityTier.EPIC]: 'badge.rarity.epic',
    [BadgeRarityTier.MYTHICAL]: 'badge.rarity.mythical',
    [BadgeRarityTier.LEGENDARY]: 'badge.rarity.legendary',
    [BadgeRarityTier.UNIQUE]: 'badge.rarity.unique'
};

/* Display colours straight from the official enum (getDisplayColor). */
const DISPLAY_COLOR_BY_TIER: Record<number, number> = {
    [BadgeRarityTier.UNCOMMON]: 16758605,
    [BadgeRarityTier.RARE]: 8780159,
    [BadgeRarityTier.EPIC]: 6732543,
    [BadgeRarityTier.MYTHICAL]: 12809942,
    [BadgeRarityTier.LEGENDARY]: 14036772,
    [BadgeRarityTier.UNIQUE]: 13406720
};

const UNCOMMON_GLOW_COLOR = 11759111;
const NEUTRAL_TAG_COLOR = 7829367;

/* How much the white-background tag darkens each display colour. */
const TAG_DARKEN_BY_TIER: Record<number, number> = {
    [BadgeRarityTier.RARE]: 0.35,
    [BadgeRarityTier.EPIC]: 0.2,
    [BadgeRarityTier.MYTHICAL]: 0.15,
    [BadgeRarityTier.LEGENDARY]: 0.1
};

export const BADGE_RARITY_COMMON_LABEL_KEY = 'badge.rarity.common';

/** Rarity data as UserCurrentBadges (1087) carries it per worn slot. */
export interface BadgePacketRarity {
    ownerCount: number;
    tier: number;
}

export interface BadgePacketRarityInput {
    badgeCode: string;
    ownerCount: number;
    badgeRarityId: number;
}

const PACKET_RARITY_BY_CODE = new Map<string, BadgePacketRarity>();

export const isBadgeRarityTierId = (tier: number): tier is BadgeRarityTierId =>
    Number.isInteger(tier) && tier >= BadgeRarityTier.COMMON && tier <= BadgeRarityTier.UNIQUE;

/**
 * Remembers the owner count and tier the server sent for the worn badges of
 * a user, so the badge views can label them without the leaderboard fetch.
 * Slots with an unknown tier id are ignored.
 */
export const rememberBadgeRarityFromPacket = (details: readonly BadgePacketRarityInput[] | null | undefined): void => {
    if (!details?.length) return;

    for (const detail of details) {
        if (!detail?.badgeCode || !isBadgeRarityTierId(detail.badgeRarityId)) continue;

        PACKET_RARITY_BY_CODE.set(detail.badgeCode, { ownerCount: Math.max(0, Math.floor(detail.ownerCount || 0)), tier: detail.badgeRarityId });
    }
};

/** BadgeReceived carries the rarity too; older servers send no owner count. */
export const rememberReceivedBadgeRarity = (badge: { badgeCode: string; ownerCount?: number; rarityTier?: number } | null | undefined): void => {
    if (!badge?.badgeCode || !(badge.ownerCount > 0)) return;

    rememberBadgeRarityFromPacket([{ badgeCode: badge.badgeCode, ownerCount: badge.ownerCount, badgeRarityId: badge.rarityTier }]);
};

export const getBadgeRarityFromPacket = (badgeCode: string | null | undefined): BadgePacketRarity | null =>
    (badgeCode && PACKET_RARITY_BY_CODE.get(badgeCode)) || null;

export const clearBadgeRarityFromPacket = (): void => PACKET_RARITY_BY_CODE.clear();

export const getBadgeRarityTier = (key: string | null | undefined): BadgeRarityTierId => {
    if (!key) return BadgeRarityTier.COMMON;

    const tier = TIER_BY_KEY[key.toLowerCase() as BadgeRarityTierKey];

    return tier === undefined ? BadgeRarityTier.COMMON : tier;
};

export const isBadgeRarityRareOrHigher = (tier: number): boolean => tier >= BadgeRarityTier.RARE;

/**
 * A "standalone" tier gets its own label, colour and glow; anything below
 * is presented as a plain common badge.
 */
export const isBadgeRarityStandaloneTier = (tier: number, uncommonEnabled = false): boolean =>
    isBadgeRarityRareOrHigher(tier) || (uncommonEnabled && tier === BadgeRarityTier.UNCOMMON);

export const getBadgeRarityLocalizationKey = (tier: number, uncommonEnabled = false): string => {
    if (tier === BadgeRarityTier.UNCOMMON) return uncommonEnabled ? LOCALIZATION_KEY_BY_TIER[tier] : '';

    return LOCALIZATION_KEY_BY_TIER[tier] ?? '';
};

export const getBadgeRarityLabelKey = (tier: number, uncommonEnabled = false): string =>
    isBadgeRarityStandaloneTier(tier, uncommonEnabled) ? getBadgeRarityLocalizationKey(tier, uncommonEnabled) : BADGE_RARITY_COMMON_LABEL_KEY;

export const getBadgeRarityDisplayColor = (tier: number, uncommonEnabled = false): number => {
    if (tier === BadgeRarityTier.UNCOMMON) return uncommonEnabled ? DISPLAY_COLOR_BY_TIER[tier] : 0;

    return DISPLAY_COLOR_BY_TIER[tier] ?? 0;
};

export const getBadgeRarityGlowColor = (tier: number, uncommonEnabled = false): number =>
    uncommonEnabled && tier === BadgeRarityTier.UNCOMMON ? UNCOMMON_GLOW_COLOR : getBadgeRarityDisplayColor(tier, uncommonEnabled);

const darkenColor = (color: number, amount: number): number => {
    const factor = 1 - amount;
    const red = Math.floor(((color >> 16) & 0xff) * factor);
    const green = Math.floor(((color >> 8) & 0xff) * factor);
    const blue = Math.floor((color & 0xff) * factor);

    return (red << 16) | (green << 8) | blue;
};

/** Colour of the rarity tag drawn on the white badge-details bubble. */
export const getBadgeRarityTagColor = (tier: number, uncommonEnabled = false): number => {
    switch (tier) {
        case BadgeRarityTier.COMMON:
            return NEUTRAL_TAG_COLOR;
        case BadgeRarityTier.UNCOMMON:
            return uncommonEnabled ? getBadgeRarityDisplayColor(tier, uncommonEnabled) : NEUTRAL_TAG_COLOR;
        case BadgeRarityTier.RARE:
        case BadgeRarityTier.EPIC:
        case BadgeRarityTier.MYTHICAL:
        case BadgeRarityTier.LEGENDARY:
            return darkenColor(getBadgeRarityDisplayColor(tier, uncommonEnabled), TAG_DARKEN_BY_TIER[tier]);
        default:
            return getBadgeRarityDisplayColor(tier, uncommonEnabled);
    }
};

export const badgeRarityColorToCss = (color: number): string => `#${(color & 0xffffff).toString(16).padStart(6, '0')}`;

/** The official bubble only prints an owner count while it is meaningful. */
export const shouldShowBadgeOwnerCount = (ownerCount: number): boolean => Number.isFinite(ownerCount) && ownerCount > 0 && ownerCount < 1000;

export const formatBadgeOwnerCount = (ownerCount: number): string => (ownerCount >= 1000 ? '1000+' : String(Math.max(0, Math.floor(ownerCount || 0))));
