package com.alterdekim.hearthhack.service;

import com.alterdekim.Protocol;
import com.alterdekim.hearthhack.dto.BoosterDTO;
import com.alterdekim.hearthhack.dto.UserCardDTO;
import com.alterdekim.hearthhack.dto.UserDTO;
import com.alterdekim.hearthhack.entity.*;
import com.alterdekim.hearthhack.repository.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class UserService implements IService {

    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final HeroXPRepository heroXPRepository;
    private final DeckRepository deckRepository;
    private final DeckContentRepository deckContentRepository;
    private final CardBackRepository cardBackRepository;
    private final BoosterRepository boosterRepository;
    private final UserCardRepository userCardRepository;


    public void saveUser(UserDTO userDto) {
        User user = new User();
        user.setUsername(userDto.getUsername());
        user.setDisplayName(userDto.getUsername());
        user.setPassword(passwordEncoder.encode(userDto.getPassword()));
        Role role = roleRepository.findByName("ROLE_USER");
        if(role == null){
            role = checkRoleExist();
        }
        user.setRoles(Collections.singletonList(role));
        userRepository.save(user);
        CardBack back = new CardBack(0, user.getId());
        cardBackRepository.save(back);
        userRepository.updateCardBackOfUser(user.getId(), back.getId());
        boosterRepository.save(new Booster(user.getId(), 10));
        Deck d = deckRepository.save(new Deck(user.getId(), "Hunter", 0L, 637, Protocol.DeckType.NORMAL_DECK.getNumber(), false, false, false, 1461490210L, 1461490210L, Protocol.DeckSourceType.DECK_SOURCE_TYPE_BASIC_DECK.getNumber()));
        deckContentRepository.save(new DeckContent(user.getId(), d.getId(), 670L, false));
    }

    public Deck createDeck(Long userId, String name, Integer hero, Boolean isPremium) {
        return deckRepository.save(new Deck(userId, name, 0L, hero, Protocol.DeckType.NORMAL_DECK.getNumber(), isPremium, false, false, 1461490210L, 1461490210L, Protocol.DeckSourceType.DECK_SOURCE_TYPE_NORMAL.getNumber()));
    }

    public void updateDeckName(Long userId, Long deckId, String name) {
        deckRepository.updateDeckName(userId, deckId, name);
    }

    public void updateDeckHero(Long userId, Long deckId, Integer heroId, Boolean isPremium) {
        deckRepository.updateDeckHero(userId, deckId, heroId, isPremium);
    }

    public void updateDeckCardBack(Long userId, Long deckId, Integer cardBack) {
        deckRepository.updateDeckCardBack(userId, deckId, cardBack);
    }

    public void clearDeckData(Long userId, Long deckId) {
        deckContentRepository.clearDeck(userId, deckId);
    }

    public void addCardToDeck(Long userId, Long deckId, Long assetId, Boolean isPremium) {
        deckContentRepository.save(new DeckContent(userId, deckId, assetId, isPremium));
    }

    public List<Deck> getDecksForUser(Long userId) {
        deckRepository.findAll().stream().forEach(d -> log.info("getDecksForUser: userId={}; deckId={}", d.getUserId(), d.getId()));
        return deckRepository.findDecksByUserId(userId);
    }

    public void deleteDeck(Long userId, Long deckId) {
        deckContentRepository.clearDeck(userId, deckId);
        deckRepository.deleteDeck(userId, deckId);
    }

    public List<DeckContent> getDeckContentForDeckId(Long userId, Long deckId) {
        return deckContentRepository.findCardsByDeckId(userId, deckId);
    }

    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    public List<CardBack> getCardBacksForUserId(Long userId) {
        return cardBackRepository.findByUserId(userId);
    }

    public CardBack getCardBackById(Long id) {
        return cardBackRepository.findById(id).orElse(new CardBack());
    }

    public void setDefaultUserCardBack(Long userId, Integer cardBack) {
        CardBack cb = cardBackRepository.findByUserIdAndBackId(userId, cardBack).get();
        userRepository.updateCardBackOfUser(userId, cb.getId());
    }

    public boolean isUserHasCardBack(Long userId, Integer cardBack) {
        return cardBackRepository.isUserHasCardBack(userId, cardBack);
    }

    public void addBooster(Long userId, Integer boosterType) {
        boosterRepository.save(new Booster(userId, boosterType));
    }


    public Integer getGoldForUserId(Long userId) {
        return userRepository.findById(userId).get().getGoldBalance();
    }

    public void setGoldForUserId(Long userId, Integer gold) {
        userRepository.updateGoldOfUser(userId, gold);
    }

    public void setDustForUserId(Long userId, Integer dust) {
        userRepository.updateDustOfUser(userId, dust);
    }

    public Integer getDustForUserId(Long userId) {
        return userRepository.findById(userId).get().getDustBalance();
    }

    public Integer getDefaultCardBackForUserId(Long userId) {
        return cardBackRepository.findById(
                userRepository.findById(userId).get().getDefaultCardBack()
        ).get().getBackId();
    }

    public List<BoosterDTO> getBoostersByUserId(Long userId) {
        return boosterRepository.findBoostersByUserId(userId);
    }

    public void removeBooster(Long userId, Integer boosterType) {
        boosterRepository.deleteOneByType(userId, boosterType);
    }

    public void addCardToCollection(Long userId, Long assetId, Boolean isPremium, Boolean hasSeen) {
        this.userCardRepository.save(new UserCard(userId, assetId, isPremium, hasSeen));
    }

    public void removeCardToCollection(Long userId, Long assetId, Boolean isPremium) {
        this.userCardRepository.remove(userId, assetId, isPremium);
    }

    public List<UserCardDTO> getCollectionOfUser(Long userId) {
        return this.userCardRepository.getCollectionOfUser(userId);
    }

    public void updateCardSeenCollection(Long userId, Long assetId, Boolean isPremium, Boolean hasSeen) {
        this.userCardRepository.updateSeenByUserIdAndCard(userId, assetId, isPremium, hasSeen);
    }

    public List<UserDTO> findAllUsers() {
        List<User> users = userRepository.findAll();
        return users.stream().map(this::convertEntityToDto)
                .collect(Collectors.toList());
    }

    public boolean authMe(String token) {
        String[] arr = token.split("\\-");
        if( arr.length != 3 ) return false;
        String hash = arr[1];
        long id = 0L;
        try {
            id = Long.parseLong(arr[2]);
        } catch (Exception e) {
            return false;
        }
        Optional<User> user = this.userRepository.findById(id);
        if( user.isEmpty() ) return false;
        String raw = user.get().getId() + user.get().getPassword() + user.get().getUsername();
        return passwordEncoder.matches(raw, hash);
    }

    public Long extractId(String token) {
        String[] arr = token.split("\\-");
        if(arr.length != 3) return 0L;
        return Long.parseLong(arr[2]);
    }

    public String genHash(Long id) throws Exception {
        Optional<User> user = this.userRepository.findById(id);
        if( user.isEmpty() ) throw new Exception();
        String hash = passwordEncoder.encode(user.get().getId() + user.get().getPassword() + user.get().getUsername());
        return "HH-"+hash+"-"+id;
    }


    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    private UserDTO convertEntityToDto(User user){
        UserDTO userDto = new UserDTO();
        userDto.setUsername(user.getUsername());
        return userDto;
    }

    private Role checkRoleExist() {
        Role role = new Role();
        role.setName("ROLE_USER");
        return roleRepository.save(role);
    }
}

