import type { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel.ts";
import type { IVoidTearParticipantInfo, IVoidTearWaveInfo } from "../types/requestTypes.ts";
import type { TRarity } from "warframe-public-export-plus";
import { ExportRelics, ExportRewards } from "warframe-public-export-plus";
import type { IRngResult } from "../services/rngService.ts";
import { getRandomWeightedReward } from "../services/rngService.ts";
import { logger } from "../utils/logger.ts";
import { addItem, addMiscItems, combineInventoryChanges } from "../services/inventoryService.ts";
import type { IInventoryChanges } from "../types/purchaseTypes.ts";
import type { ITypeCount } from "../types/commonTypes.ts";
import { fromStoreItem } from "../services/itemDataService.ts";

export const crackRelic = async (
    inventory: TInventoryDatabaseDocument,
    participant: IVoidTearParticipantInfo,
    inventoryChanges: IInventoryChanges = {}
): Promise<IRngResult> => {
    const relic = ExportRelics[participant.VoidProjection];
    let weights = refinementToWeights[relic.quality];
    if (relic.quality == "VPQ_SILVER" && inventory.exceptionalRelicsAlwaysGiveBronzeReward) {
        weights = { COMMON: 1, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
    } else if (relic.quality == "VPQ_GOLD" && inventory.flawlessRelicsAlwaysGiveSilverReward) {
        weights = { COMMON: 0, UNCOMMON: 1, RARE: 0, LEGENDARY: 0 };
    } else if (relic.quality == "VPQ_PLATINUM" && inventory.radiantRelicsAlwaysGiveGoldReward) {
        weights = { COMMON: 0, UNCOMMON: 0, RARE: 1, LEGENDARY: 0 };
    }
    logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
    let reward = getRandomWeightedReward(
        ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
        weights
    )!;
    if (inventory.relicRewardItemCountMultiplier && inventory.relicRewardItemCountMultiplier != 1) {
        reward = {
            ...reward,
            itemCount: reward.itemCount * inventory.relicRewardItemCountMultiplier
        };
    }
    logger.debug(`relic rolled`, reward);
    participant.Reward = reward.type;

    // Remove relic
    const miscItemChanges = [
        {
            ItemType: participant.VoidProjection,
            ItemCount: -1
        }
    ];
    addMiscItems(inventory, miscItemChanges);
    combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges });

    // Give reward
    combineInventoryChanges(inventoryChanges, await addItem(inventory, fromStoreItem(reward.type), reward.itemCount));

    const platinumValue =
        getRelicPlatinumBonusForRarity(inventory, reward.rarity) * (inventory.relicRewardItemCountMultiplier ?? 1);
    if (platinumValue != 0) {
        inventory.pendingPremiumCredits ??= 0;
        inventory.pendingPremiumCredits += platinumValue;
    }

    // Client has picked its own reward (for lack of choice)
    participant.ChosenRewardOwner = participant.AccountId;

    return reward;
};

const getRelicPlatinumBonusForRarity = (inventory: TInventoryDatabaseDocument, rarity: TRarity): number => {
    switch (rarity) {
        case "COMMON":
            return inventory.relicPlatinumBonusCommon ?? 0;
        case "UNCOMMON":
            return inventory.relicPlatinumBonusUncommon ?? 0;
        case "RARE":
            return inventory.relicPlatinumBonusRare ?? 0;
        default:
            return 0;
    }
};

const refinementToWeights = {
    VPQ_BRONZE: {
        COMMON: 0.76,
        UNCOMMON: 0.22,
        RARE: 0.02,
        LEGENDARY: 0
    },
    VPQ_SILVER: {
        COMMON: 0.7,
        UNCOMMON: 0.26,
        RARE: 0.04,
        LEGENDARY: 0
    },
    VPQ_GOLD: {
        COMMON: 0.6,
        UNCOMMON: 0.34,
        RARE: 0.06,
        LEGENDARY: 0
    },
    VPQ_PLATINUM: {
        COMMON: 0.5,
        UNCOMMON: 0.4,
        RARE: 0.1,
        LEGENDARY: 0
    }
};

const getTypeCountForParticiantReward = (participant: IVoidTearParticipantInfo): ITypeCount => {
    const relic = ExportRelics[participant.VoidProjection];
    const reward = ExportRewards[relic.rewardManifest][0].find(x => x.type == participant.Reward)!;
    return {
        ItemType: reward.type,
        ItemCount: reward.itemCount
    };
};

export const ensureRelicRewardIsCorrect = async (
    inventory: TInventoryDatabaseDocument,
    wi: IVoidTearWaveInfo
): Promise<void> => {
    if (inventory.MissionRelicRewards && inventory.MissionRelicRewards.length >= wi.Wave) {
        const userParticipantInfo = wi.Participants.find(x => inventory.accountOwnerId.equals(x.AccountId))!;
        const userReward = inventory.MissionRelicRewards[wi.Wave - 1];
        if (userParticipantInfo.ChosenRewardOwner) {
            const chosenParticipantInfo = wi.Participants.find(
                x => x.AccountId == userParticipantInfo.ChosenRewardOwner
            )!;
            const chosenReward = getTypeCountForParticiantReward(chosenParticipantInfo);
            chosenReward.ItemCount *= inventory.relicRewardItemCountMultiplier ?? 1;
            if (chosenReward.ItemType != userReward.ItemType || chosenReward.ItemCount != userReward.ItemCount) {
                logger.debug(`fixing up wave ${wi.Wave} reward for ${inventory.accountOwnerId.toString()}`, {
                    toRemove: userReward,
                    toAdd: chosenReward
                });
                await addItem(inventory, fromStoreItem(userReward.ItemType), userReward.ItemCount * -1);
                await addItem(inventory, fromStoreItem(chosenReward.ItemType), chosenReward.ItemCount);
                inventory.MissionRelicRewards[wi.Wave - 1] = chosenReward;
            }
        }
    }
};
