package premium_store.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import premium_store.controller.DTO.ClientDTO;
import premium_store.controller.DTO.SupportClientDTO;
import premium_store.controller.DTO.UserSignupDto;
import premium_store.model.GameClient;
import premium_store.model.Tank;
import premium_store.model.UserRole;
import premium_store.repository.GameClientRepository;
import premium_store.repository.TankRepository;
import premium_store.service.exception.ClientNotFoundException;
import premium_store.util.validation.ValidatorUtil;

import javax.validation.ValidationException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Service
public class GameClientService implements UserDetailsService {
    private final GameClientRepository gameClientRepository;
    private final TankRepository tankRepository;
    private final ValidatorUtil validatorUtil;
    private final PasswordEncoder passwordEncoder;

    public GameClientService(GameClientRepository gameClientRepository, TankRepository tankRepository, ValidatorUtil validatorUtil, PasswordEncoder passwordEncoder){
        this.gameClientRepository = gameClientRepository;
        this.tankRepository = tankRepository;
        this.validatorUtil = validatorUtil;
        this.passwordEncoder = passwordEncoder;
    }

    @Transactional
    public GameClient addClient(String login, String email, String password, Integer ballance, String passwordConfirm, UserRole role) {
        //проверка на наличие такого же логина
        if (findByLogin(login) != null) {
            throw new ValidationException(String.format("User '%s' already exists", login));
        }

        //проверка на соответствие пароля?
        if (!Objects.equals(password, passwordConfirm)) {
            throw new ValidationException("Passwords not equals");
        }

        final GameClient gameClient = new GameClient(login, email, passwordEncoder.encode(password), ballance, role);
        validatorUtil.validate(gameClient);

        return gameClientRepository.save(gameClient);
    }

    @Transactional
    public GameClient addClient(UserSignupDto userSignupDto) {
        //проверка на наличие такого же логина
        if (findByLogin(userSignupDto.getLogin()) != null) {
            throw new ValidationException(String.format("User '%s' already exists", userSignupDto.getLogin()));
        }

        if (!Objects.equals(userSignupDto.getPassword(), userSignupDto.getPasswordConfirm())) {
            throw new ValidationException("Passwords not equals");
        }

        final GameClient client = new GameClient(userSignupDto);

        validatorUtil.validate(client);

        return gameClientRepository.save(client);
    }

    @Transactional(readOnly = true)
    public GameClient findClient(Long id) {
        final Optional<GameClient> client = gameClientRepository.findById(id);

        return client.orElseThrow(() -> new ClientNotFoundException(id));
    }

    public GameClient findByLogin(String login) {
        return gameClientRepository.findOneByLoginIgnoreCase(login);
    }

    @Transactional(readOnly = true)
    public List<GameClient> findAllClients() {
        return gameClientRepository.findAll();
    }

    //метод для реализации пагинации на странице с пользователями
    public Page<GameClient> findAllPages(int page, int size) {
        return gameClientRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
    }

    @Transactional
    public GameClient updateClient(Long id, String login, String newPassword, String newEmail, Integer newBalance, Tank newTank) {
        if (id <= 0) {
            throw new IllegalArgumentException("Invalid id");
        }

        final GameClient currentGameClient = findClient(id);

        if (StringUtils.hasText(login)){
            currentGameClient.setLogin(login);
        }

        if(StringUtils.hasText(newPassword)){
            currentGameClient.setPassword(newPassword);
        }

        if(StringUtils.hasText(newEmail)){
            currentGameClient.setEmail(newEmail);
        }

        if(newBalance != null){
            currentGameClient.setBalance(newBalance);
        }

        if(newTank != null){
            currentGameClient.setTanks(newTank);
        }

        return gameClientRepository.save(currentGameClient);
    }

    @Transactional
    public GameClient updateClient(ClientDTO clientDTO) {
        if (clientDTO.getId() <= 0) {
            throw new IllegalArgumentException("Invalid id");
        }

        final GameClient currentGameClient = findClient(clientDTO.getId());
        final GameClient sameClient = findByLogin(clientDTO.getLogin());

        //проверка логина и пароля
        if (sameClient != null && !Objects.equals(sameClient.getId(), currentGameClient.getId())) {
            throw new ValidationException(String.format("User '%s' already exists", clientDTO.getLogin()));
        }

        if (!passwordEncoder.matches(clientDTO.getPassword(), currentGameClient.getPassword())) {
            throw new ValidationException("Incorrect password");
        }

        currentGameClient.setLogin(clientDTO.getLogin());
        currentGameClient.setPassword(clientDTO.getPassword());
        currentGameClient.setEmail(clientDTO.getEmail());
        currentGameClient.setBalance(clientDTO.getBalance());

        /*if(clientDTO.getTankId() != null){
            currentGameClient.setTanks(tankRepository.getById(clientDTO.getTankId()));
        }*/

        return gameClientRepository.save(currentGameClient);
    }

    @Transactional
    public GameClient deleteClient(Long id) {
        final GameClient currentGameClient = findClient(id);
        gameClientRepository.delete(currentGameClient);

        return currentGameClient;
    }

    //прямой sql-запрос на удаление всех записей в таблице
    @Transactional
    public void deleteAllClients() {
        gameClientRepository.deleteAll();
    }

    //метод загрузки пользователя по его логину
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        final GameClient userEntity = findByLogin(username);
        if (userEntity == null) {
            throw new UsernameNotFoundException(username);
        }
        return new org.springframework.security.core.userdetails.User(
                userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
    }
}
