package emu.grasscutter.game.shop;

import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ShopGoodsData;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

import static emu.grasscutter.config.Configuration.*;

import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ShopSystem extends BaseGameSystem {
    private final Int2ObjectMap<List<ShopInfo>> shopData;
    private final List<ShopChestTable> shopChestData;
    private final List<ShopChestBatchUseTable> shopChestBatchUseData;

    private static final int REFRESH_HOUR = 4; // In GMT+8 server
    private static final String TIME_ZONE = "Asia/Shanghai"; // GMT+8 Timezone

    public ShopSystem(GameServer server) {
        super(server);
        this.shopData = new Int2ObjectOpenHashMap<>();
        this.shopChestData = new ArrayList<>();
        this.shopChestBatchUseData = new ArrayList<>();
        this.load();
    }

    public Int2ObjectMap<List<ShopInfo>> getShopData() {
        return shopData;
    }

    public List<ShopChestTable> getShopChestData() {
        return shopChestData;
    }

    public List<ShopChestBatchUseTable> getShopChestBatchUseData() {
        return shopChestBatchUseData;
    }

    public static int getShopNextRefreshTime(ShopInfo shopInfo) {
        return switch (shopInfo.getShopRefreshType()) {
            case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
            case SHOP_REFRESH_WEEKLY ->  Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
            case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
            default -> 0;
        };
    }

    private void loadShop() {
        try (Reader fileReader = DataLoader.loadReader("Shop.json")) {
            getShopData().clear();
            List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
            if (banners.size() > 0) {
                for (ShopTable shopTable : banners) {
                    for (ShopInfo cost : shopTable.getItems()) {
                        if (cost.getCostItemList() != null) {
                            Iterator<ItemParamData> iterator = cost.getCostItemList().iterator();
                            while (iterator.hasNext()) {
                                ItemParamData ipd = iterator.next();
                                if (ipd.getId() == 201) {
                                    cost.setHcoin(cost.getHcoin() + ipd.getCount());
                                    iterator.remove();
                                }
                                if (ipd.getId() == 203) {
                                    cost.setMcoin(cost.getMcoin() + ipd.getCount());
                                    iterator.remove();
                                }
                            }
                        }
                    }
                    getShopData().put(shopTable.getShopId(), shopTable.getItems());
                }
                Grasscutter.getLogger().debug("Shop data successfully loaded.");
            } else {
                Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0.");
            }

            if (GAME_OPTIONS.enableShopItems) {
                GameData.getShopGoodsDataEntries().forEach((k, v) -> {
                    if (!getShopData().containsKey(k.intValue()))
                        getShopData().put(k.intValue(), new ArrayList<>());
                    for (ShopGoodsData sgd : v) {
                        var shopInfo = new ShopInfo(sgd);
                        getShopData().get(k.intValue()).add(shopInfo);
                    }
                });
            }
        } catch (Exception e) {
            Grasscutter.getLogger().error("Unable to load shop data.", e);
        }
    }

    private void loadShopChest() {
        try (Reader fileReader = DataLoader.loadReader("ShopChest.json")) {
            getShopChestData().clear();
            List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
            if (shopChestTableList.size() > 0) {
                getShopChestData().addAll(shopChestTableList);
                Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
            } else {
                Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
            }
        } catch (Exception e) {
            Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
        }
    }

    private void loadShopChestBatchUse() {
        try (Reader fileReader = DataLoader.loadReader("ShopChestBatchUse.json")) {
            getShopChestBatchUseData().clear();
            List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
            if (shopChestBatchUseTableList.size() > 0) {
                getShopChestBatchUseData().addAll(shopChestBatchUseTableList);
                Grasscutter.getLogger().debug("ShopChestBatchUse data successfully loaded.");
            } else {
                Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data. ShopChestBatchUse data size is 0.");
            }
        } catch (Exception e) {
            Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data.", e);
        }
    }

    public synchronized void load() {
        loadShop();
        loadShopChest();
        loadShopChestBatchUse();
    }

    public GameServer getServer() {
        return server;
    }
}
