package com.alterdekim.game.service;

import com.alterdekim.game.component.ReferenceLoader;
import com.alterdekim.game.component.game.*;
import com.alterdekim.game.component.game.avatar.IBodyAvatarItem;
import com.alterdekim.game.component.game.avatar.BodyPartType;
import com.alterdekim.game.component.game.avatar.GoodClothType;
import com.alterdekim.game.component.game.avatar.InitMagicAbility;
import com.alterdekim.game.component.game.inventory.InventoryGroup;
import com.alterdekim.game.component.game.inventory.InventoryItem;
import com.alterdekim.game.controller.result.signup.AvatarBodyPart;
import com.alterdekim.game.controller.result.signup.AvatarBodyPartGroup;
import com.alterdekim.game.entity.*;
import com.alterdekim.game.repository.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.alterdekim.game.utils.GameUtils.EmptyGoodId;
import static com.alterdekim.game.utils.GameUtils.EmptyMRId;

@Slf4j
@Service
public class AvatarInventoryService {

    @Autowired
    private ReferenceLoader referenceLoader;

    @Autowired
    private AvatarInventoryRepository inventoryRepository;

    @Autowired
    private GoodRepository goodRepository;

    @Autowired
    private BodyPartRepository bodyPartRepository;

    @Autowired
    private GoodMagicRepository goodMagicRepository;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private AchievementService achievementService;

    @Autowired
    private SmileService smileService;

    @Autowired
    private BackgroundService backgroundService;

    @Autowired
    private PhoneIconService phoneIconService;

    public void setDefault(Integer userId, Long goodId, AvatarInventoryType type) {
        this.inventoryRepository.resetUsed(userId, type);
        this.inventoryRepository.setUsed(userId, goodId, type);
    }

    public void setUsed(Integer userId, Long goodId, AvatarInventoryType type) {
        this.inventoryRepository.setUsed(userId, goodId, type);
    }

    public void resetUsed(Integer userId, AvatarInventoryType type) {
        this.inventoryRepository.resetUsed(userId, type);
    }

    public void setColor(Integer userId, Long goodId, AvatarInventoryType type, Integer color) {
        this.inventoryRepository.setColor(userId, goodId, type, color);
    }

    public boolean isSpecificItemExists(Integer userId, AvatarInventoryType type, Long goodId) {
        return !this.inventoryRepository.findSpecificitem(userId, type, goodId).isEmpty();
    }

    public InventoryGroup getBodyPartInventoryGroup(Integer userId, BodyPartType type, boolean addEmpty) {
        return new InventoryGroup(
                type.getValue(),
                type.getTextResourceId(),
                false,
                type.getMediaResourceId(),
                type.getLayerId(),
                addEmpty ? Stream.of(bodyPartRepository.findInventoryItems(userId, type), Collections.singletonList(new InventoryItem() {
                    @Override
                    public Long getId() {
                        return EmptyGoodId;
                    }

                    @Override
                    public Long getTextResourceId() {
                        return 0L;
                    }

                    @Override
                    public Long getBodyPartId() {
                        return EmptyGoodId;
                    }

                    @Override
                    public Integer getMediaResourceId() {
                        return EmptyMRId;
                    }

                    @Override
                    public Integer getIsUsed() {
                        return 0;
                    }

                    @Override
                    public Integer getIsColorable() {
                        return 0;
                    }

                    @Override
                    public Integer getColor() {
                        return 0;
                    }

                    @Override
                    public Integer getIsLimited() {
                        return 0;
                    }

                    @Override
                    public String getDate() {
                        return "";
                    }

                    @Override
                    public Integer getPollution() {
                        return 0;
                    }

                    @Override
                    public Integer getIsBodyPart() {
                        return 1;
                    }

                    @Override
                    public Long getPhoneId() {
                        return 0L;
                    }
                })).flatMap(Collection::stream).collect(Collectors.toList()) : bodyPartRepository.findInventoryItems(userId, type)
        );
    }

    public InventoryGroup getClothInventoryGroup(Integer userId, GoodClothType type) {
        return new InventoryGroup(
                type.getVal().getGoodType(),
                type.getTextResourceId(),
                false,
                0,
                type.getLayerId(),
                inventoryRepository.findInventoryItems(userId, type)
        );
    }

    public List<IBodyAvatarItem> getWholeAvatarByUserId(Integer userId) {
        return Stream.of(this.inventoryRepository.findUsedBodyPartsByUserId(userId),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Clothes),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Caps),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Glasses),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Bangles),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Boots),
                        this.inventoryRepository.findUsedClothesByUserId(userId, GoodClothType.Suits))
                .flatMap(List::stream).collect(Collectors.toList());
    }

    public List<InitMagicAbility> getMagicAbilitiesItemsByUserId(Integer userId) {
        return this.inventoryRepository.findMagicAbilitiesByUserId(userId);
    }

    public List<Smile> getSmilesByUserId(Integer userId) {
        return this.inventoryRepository.findSmilesByUserId(userId);
    }

    public List<InventoryItem> getBackgroundsByUserId(Integer userId) {
        return this.inventoryRepository.findBackgrounds(userId);
    }

    public List<InventoryItem> getPhonesByUserId(Integer userId) {
        return this.inventoryRepository.findPhones(userId);
    }

    public void addPhoneToInventory(Integer userId, Long phoneId) {
        this.inventoryRepository.save(new AvatarInventory(referenceLoader.getUserReference(userId), phoneId, true, AvatarInventoryType.Phones, 0));
    }

    public void addBackgroundToInventory(Integer userId, Long backgroundId) {
        this.inventoryRepository.save(new AvatarInventory(referenceLoader.getUserReference(userId), backgroundId, true, AvatarInventoryType.Backgrounds, 0));
    }

    public void saveBodyPart(BodyPart bodyPart) {
        this.bodyPartRepository.save(bodyPart);
    }

    private List<AvatarBodyPart> getAvatarBodyPartsByType(BodyPartType type) {
        return this.bodyPartRepository.findByBodyPartType(type)
                .stream()
                .filter(b -> resourceService.getUrlByMediaResourceId(b.getMediaResourceId().longValue()).isPresent())
                .map(b -> new AvatarBodyPart(
                                b.getId().intValue(),
                                0,
                                false,
                                resourceService.getUrlByMediaResourceId(b.getMediaResourceId().longValue()).get()
                        )
                )
                .collect(Collectors.toList());
    }

    public Long getSelectedPhoneId(Integer userId) {
        return this.inventoryRepository.findUsedItemsByUserIdAndType(userId, AvatarInventoryType.Phones)
                .stream()
                .map(AvatarInventory::getGoodId)
                .findFirst().orElse(0L);
    }

    public Long getUsedBackground(Integer userId) {
        return this.inventoryRepository.findUsedItemsByUserIdAndType(userId, AvatarInventoryType.Backgrounds)
                .stream()
                .map(AvatarInventory::getGoodId)
                .findFirst().orElse(0L);
    }

    public Long getUsedHouse(Integer userId) {
        return this.inventoryRepository.findUsedItemsByUserIdAndType(userId, AvatarInventoryType.House)
                .stream()
                .map(AvatarInventory::getGoodId)
                .findFirst().orElse(0L);
    }

    public String getAchievementsByUserId(Integer userId) {
        return this.inventoryRepository.findItemsByUserIdAndType(userId, AvatarInventoryType.Achievements)
                .stream()
                .map(a -> a.getGoodId().toString())
                .collect(Collectors.joining(","));
    }

    private List<AvatarBodyPartGroup> getAvatarBodyPartGroups(boolean isShape) {
        List<AvatarBodyPartGroup> groups = new ArrayList<>();
        for( BodyPartType type : BodyPartType.values() ) {
            if( type.isShape() != isShape ) continue;
            groups.add(
                    new AvatarBodyPartGroup(
                            type.getValue(),
                            type.name(),
                            resourceService.getUrlByMediaResourceId(type.getMediaResourceId()).get(),
                            type.getLayerId(),
                            isShape ? this.getAvatarBodyPartsByType(type) : Stream.of(
                                    this.getAvatarBodyPartsByType(type),
                                    Collections.singletonList(
                                            new AvatarBodyPart(EmptyGoodId.intValue(), 0, true, this.resourceService.getUrlByMediaResourceId(EmptyMRId.longValue()).get())
                                    )
                            )
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList())
                    )
            );
        }
        return groups;
    }

    public List<AvatarBodyPartGroup> getShapeGroups() {
        return this.getAvatarBodyPartGroups(true);
    }

    public List<AvatarBodyPartGroup> getBodyPartGroups() {
        return this.getAvatarBodyPartGroups(false);
    }

    private List<AvatarBodyPart> getRandomStuffByGoodType(GoodClothType type) {
        List<Good> g = this.goodRepository.findByGoodTypeId(type.getVal().getGoodType());
        int l = (int) Math.round(Math.random() * (g.size() - 4));
        return g.subList(l, l+3)
                .stream()
                .map(i -> new AvatarBodyPart(
                        i.getId().intValue(),
                        0,
                        false,
                        resourceService.getUrlByMediaResourceId(i.getMRId().longValue()).get()
                    )
                )
                .collect(Collectors.toList());
    }

    public List<AvatarBodyPartGroup> getAvatarInventoryGroups() {
        List<AvatarBodyPartGroup> groups = new ArrayList<>();
        for( GoodClothType type : GoodClothType.values() ) {
            groups.add(
                    new AvatarBodyPartGroup(
                            type.getVal().getGoodType(),
                            type.name(),
                            resourceService.getUrlByMediaResourceId(type.getTabMediaResourceId()).get(),
                            type.getLayerId(),
                            Stream.of(
                                    this.getRandomStuffByGoodType(type),
                                    Collections.singletonList(
                                            new AvatarBodyPart(EmptyGoodId.intValue(), 0, true, this.resourceService.getUrlByMediaResourceId(EmptyMRId.longValue()).get())
                                    )
                            )
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList())
                    )
            );
        }
        return groups;
    }

    public void addGoodToInventory(AvatarInventory avatarInventory) {
        this.inventoryRepository.save(avatarInventory);
    }
}
