package com.wishurgift.service.bean;

import com.wishurgift.persistence.entity.User;
import com.wishurgift.persistence.entity.Wishlist;
import com.wishurgift.persistence.repository.UserRepository;
import com.wishurgift.persistence.repository.WishlistRepository;
import com.wishurgift.service.ContactService;
import com.wishurgift.service.UserService;
import com.wishurgift.service.dto.LoginDTO;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.WishlistHeaderDTO;
import com.wishurgift.service.dto.request.LoginRequestDTO;
import com.wishurgift.service.dto.request.RegisterRequestDTO;
import com.wishurgift.service.dto.request.UpdateUserRequestDTO;
import com.wishurgift.service.dto.transformer.UserTransformer;
import com.wishurgift.service.dto.transformer.WishlistTransformer;
import com.wishurgift.utils.JwtUtils;
import com.wishurgift.utils.PasswordUtils;
import com.wishurgift.utils.StringUtils;
import com.wishurgift.wrapper.Page;
import com.wishurgift.wrapper.Result;

import javax.ejb.Stateless;
import javax.inject.Inject;
import java.security.Principal;
import java.util.List;
import java.util.Optional;
import java.util.Random;

@Stateless
public class UserServiceBean implements UserService {

    private static final String NUMERIC_STRING = "0123456789";
    private static final String ALPHA_NUMERIC_STRING = "abcdefghijqlmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final Random RANDOM = new Random();

    @Inject
    private ContactService contactService;

    @Inject
    private WishlistRepository wishlistRepository;

    @Inject
    private UserRepository repository;

    @Override
    public UserDTO update(UserDTO userDTO, UpdateUserRequestDTO request) {
        // Recup utilisateur en base
        Optional<User> optionalUser = repository.findById(userDTO.getId());
        if (!optionalUser.isPresent()) {
            return null;
        }
        User user = optionalUser.get();
        // MaJ utilisateur
        if (StringUtils.isNotBlank(request.getEmail())) {
            // Verif que l'email est disponible
            Optional<User> optional = repository.findByEmail(request.getEmail());
            if (optional.isPresent()) {
                return null;
            }
            user.setEmail(request.getEmail().toLowerCase());
        }
        if (StringUtils.isNotBlank(request.getName())) {
            user.setName(request.getName().toLowerCase());
        }
        if (StringUtils.isNotBlank(request.getPassword())) {
            user.setPassword(PasswordUtils.hash(request.getPassword()));
        }
        if (request.isPublic() != null) {
            user.setPublic(request.isPublic());
        }
        repository.update(user);
        return UserTransformer.entityToDto(user);
    }

    @Override
    public UserDTO getById(long id) {
        return UserTransformer.entityToDto(repository.getById(id));
    }

    @Override
    public Result getUser(UserDTO principal, long id) {
        // Récupère l'utilisateur
        Optional<User> optionalUser = repository.findById(id);
        if (!optionalUser.isPresent()) {
            return Result.error().data(Result.Error.UNABLE_TO_GET_DATA).build();
        }
        UserDTO user = UserTransformer.entityToDto(optionalUser.get());
        // Regarde si son profil est publique
        if (user.isPublic()) {
            return Result.success().data(user).build();
        }
        // Si l'utilisateur récupèrer est sois meme
        if (principal != null && principal.getId() == id) {
            return Result.success().data(user).build();
        }
        // Regarde si ils sont en contact
        if (principal != null && contactService.isContact(principal, user)) {
            return Result.success().data(user).build();
        }
        return Result.error().data(Result.Error.UNAUTHORIZED).build();
    }

    @Override
    public Page<WishlistHeaderDTO> getWishlist(UserDTO principal, long id) {
        // Recup de l'utilisateur connecté
        User user = null;
        if (principal != null) {
            Optional<User> optionalUser = repository.findById(principal.getId());
            user = optionalUser.orElse(null);
        }
        // Recup l'utilisateur de la liste
        User owner;
        if (user != null && user.getId() == id) {
            owner = user;
        } else {
            Optional<User> optionalUser = repository.findById(id);
            if (!optionalUser.isPresent()) {
                return null;
            }
            owner = optionalUser.get();
        }
        // Recup les listes
        List<Wishlist> wishlist = wishlistRepository.getAllByUser(owner, user);
        return Page.unpaged(WishlistTransformer.entityToHeaderDto(wishlist));
    }

    @Override
    public Page<WishlistHeaderDTO> getWishlist(int page, int size, UserDTO principal, long id) {
        // Recup de l'utilisateur connecté
        User user = null;
        if (principal != null) {
            Optional<User> optionalUser = repository.findById(principal.getId());
            user = optionalUser.orElse(null);
        }
        // Recup l'utilisateur de la liste
        User owner;
        if (user != null && user.getId() == id) {
            owner = user;
        } else {
            Optional<User> optionalUser = repository.findById(id);
            if (!optionalUser.isPresent()) {
                return null;
            }
            owner = optionalUser.get();
        }
        // Recup les listes
        List<Wishlist> wishlist = wishlistRepository.getAllByUser(page, size, owner, user);
        return Page.paged(page, wishlistRepository.countAllByUser(owner, user), WishlistTransformer.entityToHeaderDto(wishlist));
    }

    @Override
    public LoginDTO login(LoginRequestDTO login) {
        Optional<User> userOptional = repository.findByEmail(login.getLogin());
        //Vérifie que l'utilisateur existe
        if (!userOptional.isPresent()) {
            return null;
        }
        //récupération de l'utilisateur en base
        User user = userOptional.get();
        //Vérifie le password
        if (!PasswordUtils.verify(login.getPassword(), user.getPassword())) {
            return null;
        }

        UserDTO connectedUser = UserTransformer.entityToDto(user);
        String token = JwtUtils.generate(connectedUser);
        return new LoginDTO(token, connectedUser);
    }

    @Override
    public String refresh(Principal principal) {
        return JwtUtils.refresh(principal);
    }

    @Override
    public UserDTO register(RegisterRequestDTO register) {
        // Verification que l'email est unique
        if (repository.findByEmail(register.getEmail()).isPresent()) {
            return null;
        }
        // Création de l'utilisateur
        User user = new User();
        user.setEmail(register.getEmail().toLowerCase());
        user.setName(register.getName().toLowerCase());
        user.setPassword(PasswordUtils.hash(register.getPassword()));
        user.setTag(generateUniqueTag());
        // Ajout en base
        repository.insert(user);

        return UserTransformer.entityToDto(user);
    }

    @Override
    public Page<UserDTO> search(String request, int limit) {
        List<User> result = repository.search(request, limit);
        List<UserDTO> users = UserTransformer.entityToDto(result);
        return Page.unpaged(users);
    }

    @Override
    public Page<WishlistHeaderDTO> getShared(UserDTO principal) {
        // Recuperation de l'utilisateur
        Optional<User> optionalUser = repository.findById(principal.getId());
        if (!optionalUser.isPresent()) {
            return null;
        }
        User user = optionalUser.get();
        // Récupération liste
        List<Wishlist> shared = wishlistRepository.getShared(user);
        return Page.unpaged(WishlistTransformer.entityToHeaderDto(shared, true));
    }

    @Override
    public Page<WishlistHeaderDTO> getShared(int page, int size, UserDTO principal) {
        // Recuperation de l'utilisateur
        Optional<User> optionalUser = repository.findById(principal.getId());
        if (!optionalUser.isPresent()) {
            return null;
        }
        User user = optionalUser.get();
        // Récupération liste
        List<Wishlist> shared = wishlistRepository.getShared(page, size, user);
        return Page.paged(page, wishlistRepository.countShared(user), WishlistTransformer.entityToHeaderDto(shared, true));
    }

    private String generateUniqueTag() {
        String tag;
        do {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < 5; i++) {
                int pos = RANDOM.nextInt(NUMERIC_STRING.length());
                builder.append(NUMERIC_STRING.charAt(pos));
            }
            tag = builder.toString();
        } while (repository.findByTag(tag).isPresent());
        return tag;
    }

}
