package com.caliverse.admin.dynamodb.service;

import com.caliverse.admin.domain.datacomponent.MetaDataHandler;
import com.caliverse.admin.domain.entity.CLOTHSMALLTYPE;
import com.caliverse.admin.domain.entity.EFilterOperator;
import com.caliverse.admin.domain.entity.ITEMLARGETYPE;
import com.caliverse.admin.domain.response.LandResponse;
import com.caliverse.admin.domain.response.UsersResponse;
import com.caliverse.admin.dynamodb.domain.atrrib.ItemAttrib;
import com.caliverse.admin.dynamodb.domain.doc.ItemDoc;
import com.caliverse.admin.dynamodb.dto.PageResult;
import com.caliverse.admin.dynamodb.entity.KeyParam;
import com.caliverse.admin.dynamodb.repository.*;
import com.caliverse.admin.global.common.annotation.DynamoDBTransaction;
import com.caliverse.admin.global.common.constants.DynamoDBConstants;
import com.caliverse.admin.global.common.utils.CommonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;


@Slf4j
@Service
@RequiredArgsConstructor
public class DynamodbItemService {
    private final ItemRepository itemRepository;
    private final MetaDataHandler metaDataHandler;

    public PageResult<ItemDoc> getItems(String guid, KeyParam pageKey){
        Map<String, AttributeValue> pagingKey = null;
        if(pageKey != null){
            pagingKey = new HashMap<>();
            pagingKey.put(DynamoDBConstants.PK_KEY_NAME, AttributeValue.builder().s(pageKey.getPk()).build());
            pagingKey.put(DynamoDBConstants.SK_KEY_NAME, AttributeValue.builder().s(pageKey.getSk()).build());
        }
        PageResult<ItemDoc> itemList = itemRepository.getItemListWithPaging(guid, "","",null, "",  pagingKey, false);
        return itemList;
    }

    public UsersResponse.InventoryInfo getInvenItems(String guid){
        List<UsersResponse.Item> clothList = new ArrayList<>();
        List<UsersResponse.Item> propList = new ArrayList<>();
        List<UsersResponse.Item> beautyList = new ArrayList<>();
        List<UsersResponse.Item> tattooList = new ArrayList<>();
        List<UsersResponse.Item> currencyList = new ArrayList<>();
        List<UsersResponse.Item> etcList = new ArrayList<>();

        Map<String, AttributeValue> pageKey = null;
        final AtomicInteger processedItems = new AtomicInteger(0);

        do {
            PageResult<ItemDoc> itemList = itemRepository.getItemListWithPaging(guid, "", DynamoDBConstants.ATTRIB_ITEM, EFilterOperator.CONTAINS,"\"equiped_inven_type\":0",  pageKey, false);

            itemList.getItems().forEach(item -> {
                try {
                    processedItems.incrementAndGet();

                    // 2. ItemAttrib 파싱 예외 처리
                    ItemAttrib attrib = null;
                    try {
                        attrib = CommonUtils.stringByObject(item.getAttribValue(), ItemAttrib.class);
                        if (attrib == null) {
                            log.warn("Failed to parse ItemAttrib for pk: {}, sk: {}",
                                    item.getPK(), item.getSK());
                            return;
                        }
                    } catch (Exception e) {
                        log.error("Exception while parsing ItemAttrib for pk: {}, sk: {}, error: {}",
                                item.getPK(), item.getSK(), e.getMessage(), e);
                        return;
                    }

                    // 3. 메타데이터 조회 예외 처리
                    int item_id = attrib.getItemMetaId();
                    String item_name = "";
                    String item_type = "";

                    try {
                        item_name = metaDataHandler.getTextStringData(
                                metaDataHandler.getMetaItemNameData(item_id));
                        if (item_name == null) {
                            item_name = "Unknown Item"; // 기본값 설정
                            log.warn("Item name is null for itemId: {}, guid: {}", item_id, guid);
                        }
                    } catch (Exception e) {
                        item_name = "Unknown Item";
                        log.error("Failed to get item name for itemId: {}, guid: {}, error: {}",
                                item_id, guid, e.getMessage(), e);
                    }

                    try {
                        item_type = metaDataHandler.getMetaItemLargeTypeData(item_id);
                        if (item_type == null) {
                            item_type = ""; // 기본값 설정
                            log.warn("Item type is null for itemId: {}, guid: {}", item_id, guid);
                        }
                    } catch (Exception e) {
                        item_type = "";
                        log.error("Failed to get item type for itemId: {}, guid: {}, error: {}",
                                item_id, guid, e.getMessage(), e);
                    }

                    // 4. UsersResponse.Item 생성 예외 처리
                    UsersResponse.Item inventory = null;
                    try {
                        inventory = UsersResponse.Item.builder()
                                .itemId(item_id)
                                .itemName(item_name)
                                .count(attrib.getItemStackCount())
                                .itemGuid(attrib.getItemGuid())
                                .build();
                    } catch (Exception e) {
                        log.error("Failed to build inventory item for itemId: {}, guid: {}, error: {}",
                                item_id, guid, e.getMessage(), e);
                        return;
                    }

                    // 5. 아이템 분류 예외 처리
                    try {
                        if (item_type.isEmpty()) {
                            etcList.add(inventory);
                        } else {
                            try {
                                ITEMLARGETYPE itemLargeType = ITEMLARGETYPE.valueOf(item_type);
                                switch (itemLargeType) {
                                    case CLOTH -> clothList.add(inventory);
                                    case PROP -> propList.add(inventory);
                                    case BEAUTY -> beautyList.add(inventory);
                                    case TATTOO -> tattooList.add(inventory);
                                    case CURRENCY -> currencyList.add(inventory);
                                    default -> etcList.add(inventory);
                                }
                            } catch (IllegalArgumentException e) {
                                // ENUM 값이 존재하지 않는 경우
                                log.warn("Unknown item type: {} for itemId: {}, guid: {}, adding to etc list",
                                        item_type, item_id, guid);
                                etcList.add(inventory);
                            }
                        }
                    } catch (Exception e) {
                        log.error("Failed to categorize item for itemId: {}, guid: {}, error: {}, adding to etc list",
                                item_id, guid, e.getMessage(), e);
                        etcList.add(inventory);
                    }

                } catch (Exception e) {
                    log.error("Unexpected error processing item for guid: {}, error: {}",
                            guid, e.getMessage(), e);
                }
            });

            pageKey = itemList.getLastEvaluatedKey();
        } while (pageKey != null);

        return UsersResponse.InventoryInfo.builder()
                .cloth(clothList)
                .prop(propList)
                .beauty(beautyList)
                .tattoo(tattooList)
                .currency(currencyList)
                .etc(etcList)
                .build();
    }

    public Map<String, Object> getClothItems(String guid){
        Map<String, Object> resultMap = new HashMap<>();

        UsersResponse.ClothInfo.ClothInfoBuilder clothInfo = UsersResponse.ClothInfo.builder();

        Map<CLOTHSMALLTYPE, BiConsumer<UsersResponse.ClothInfo.ClothInfoBuilder, UsersResponse.ClothItem>> setterMap = new HashMap<>();
        setterMap.put(CLOTHSMALLTYPE.SHIRT, UsersResponse.ClothInfo.ClothInfoBuilder::clothShirt);
        setterMap.put(CLOTHSMALLTYPE.DRESS, UsersResponse.ClothInfo.ClothInfoBuilder::clothDress);
        setterMap.put(CLOTHSMALLTYPE.OUTER, UsersResponse.ClothInfo.ClothInfoBuilder::clothOuter);
        setterMap.put(CLOTHSMALLTYPE.PANTS, UsersResponse.ClothInfo.ClothInfoBuilder::clothPants);
        setterMap.put(CLOTHSMALLTYPE.GLOVES, UsersResponse.ClothInfo.ClothInfoBuilder::clothGloves);
        setterMap.put(CLOTHSMALLTYPE.RING, UsersResponse.ClothInfo.ClothInfoBuilder::clothRing);
        setterMap.put(CLOTHSMALLTYPE.BRACELET, UsersResponse.ClothInfo.ClothInfoBuilder::clothBracelet);
        setterMap.put(CLOTHSMALLTYPE.BAG, UsersResponse.ClothInfo.ClothInfoBuilder::clothBag);
        setterMap.put(CLOTHSMALLTYPE.BACKPACK, UsersResponse.ClothInfo.ClothInfoBuilder::clothBackpack);
        setterMap.put(CLOTHSMALLTYPE.CAP, UsersResponse.ClothInfo.ClothInfoBuilder::clothCap);
        setterMap.put(CLOTHSMALLTYPE.MASK, UsersResponse.ClothInfo.ClothInfoBuilder::clothMask);
        setterMap.put(CLOTHSMALLTYPE.GLASSES, UsersResponse.ClothInfo.ClothInfoBuilder::clothGlasses);
        setterMap.put(CLOTHSMALLTYPE.EARRING, UsersResponse.ClothInfo.ClothInfoBuilder::clothEarring);
        setterMap.put(CLOTHSMALLTYPE.NECKLACE, UsersResponse.ClothInfo.ClothInfoBuilder::clothNecklace);
        setterMap.put(CLOTHSMALLTYPE.SHOES, UsersResponse.ClothInfo.ClothInfoBuilder::clothShoes);
        setterMap.put(CLOTHSMALLTYPE.SOCKS, UsersResponse.ClothInfo.ClothInfoBuilder::clothSocks);
        setterMap.put(CLOTHSMALLTYPE.ANKLET, UsersResponse.ClothInfo.ClothInfoBuilder::clothAnklet);

        Map<String, AttributeValue> pageKey = null;
        do {
            PageResult<ItemDoc> itemList = itemRepository.getItemListWithPaging(guid, "", DynamoDBConstants.ATTRIB_ITEM, EFilterOperator.CONTAINS,"\"equiped_inven_type\":1",  pageKey, false);

            itemList.getItems().forEach(item -> {
                ItemAttrib attrib = CommonUtils.stringByObject(item.getAttribValue(), ItemAttrib.class);
                int item_id = attrib.getItemMetaId();
                String item_name = metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id));

                int pos = attrib.getEquipedPos();
                String smallType = metaDataHandler.getMetaClothSmallTypeData(pos);

                UsersResponse.ClothItem clothItem = UsersResponse.ClothItem.builder()
                        .cloth(item_id)
                        .clothName(item_name)
                        .slotType(metaDataHandler.getMetaClothSlotTypeData(pos))
                        .smallType(smallType)
                        .build();

                // ClothItem을 CLOTHSMALLTYPE 위치에 넣어 준다
                setterMap.getOrDefault(CLOTHSMALLTYPE.valueOf(smallType), (builder, smallItem) -> {})
                        .accept(clothInfo, clothItem);
            });

            resultMap.put("clothInfo", clothInfo.build());

            pageKey = itemList.getLastEvaluatedKey();
        } while (pageKey != null);

        return resultMap;
    }

    public Map<String, Object> getTools(String guid){
        Map<String, Object> resultMap = new HashMap<>();

        UsersResponse.SlotInfo.SlotInfoBuilder slotInfo = UsersResponse.SlotInfo.builder();

        Map<Integer, BiConsumer<UsersResponse.SlotInfo.SlotInfoBuilder, UsersResponse.ToolItem>> setterMap = Map.of(
                0, UsersResponse.SlotInfo.SlotInfoBuilder::Slot1,
                1, UsersResponse.SlotInfo.SlotInfoBuilder::Slot2,
                2, UsersResponse.SlotInfo.SlotInfoBuilder::Slot3,
                3, UsersResponse.SlotInfo.SlotInfoBuilder::Slot4
        );

        Map<String, AttributeValue> pageKey = null;
        do {
            PageResult<ItemDoc> itemList = itemRepository.getItemListWithPaging(guid, "", DynamoDBConstants.ATTRIB_ITEM, EFilterOperator.CONTAINS,"\"equiped_inven_type\":2",  pageKey, false);

            itemList.getItems().forEach(item -> {
                ItemAttrib attrib = CommonUtils.stringByObject(item.getAttribValue(), ItemAttrib.class);
                int item_id = attrib.getItemMetaId();
                String item_name = metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id));

                int pos = attrib.getEquipedPos();

                UsersResponse.ToolItem toolItem = UsersResponse.ToolItem.builder()
                        .toolId(item_id)
                        .toolName(item_name)
                        .build();

                setterMap.getOrDefault(pos, (builder, smallItem) -> {})
                        .accept(slotInfo, toolItem);
            });

            resultMap.put("toolSlotInfo", slotInfo.build());

            pageKey = itemList.getLastEvaluatedKey();
        } while (pageKey != null);

        return resultMap;
    }

    public List<UsersResponse.Tattoo> getTattoo(String guid){
        List<UsersResponse.Tattoo> resTatto = new ArrayList<>();

        Map<String, AttributeValue> pageKey = null;
        do {
            PageResult<ItemDoc> itemList = itemRepository.getItemListWithPaging(guid, "", DynamoDBConstants.ATTRIB_ITEM, EFilterOperator.CONTAINS,"\"equiped_inven_type\":3",  pageKey, false);

            itemList.getItems().forEach(item -> {
                ItemAttrib attrib = CommonUtils.stringByObject(item.getAttribValue(), ItemAttrib.class);
                int item_id = attrib.getItemMetaId();
                String item_name = metaDataHandler.getTextStringData(metaDataHandler.getMetaItemNameData(item_id));

                int pos = attrib.getEquipedPos();

                UsersResponse.Tattoo tattoo = new UsersResponse.Tattoo();

                tattoo.setItemId(item_id);
                tattoo.setItemGuid(attrib.getItemGuid());
                tattoo.setLevel(attrib.getLevel());
                tattoo.setItemName(item_name);
                tattoo.setSlot(pos);
                resTatto.add(tattoo);
            });

            pageKey = itemList.getLastEvaluatedKey();
        } while (pageKey != null);

        return resTatto.stream().sorted(Comparator.comparing(UsersResponse.Tattoo::getSlot)).collect(Collectors.toList());
    }

    @DynamoDBTransaction
    public void updateItemStack(String guid, String itemGuid, int stackCount){
        itemRepository.updateItemStack(guid, itemGuid, stackCount);
    }

    @DynamoDBTransaction
    public void deleteItem(String guid, String itemGuid){
        itemRepository.deleteItem(guid, itemGuid);
    }
}
