package org.example.seed.service.impl;

import org.example.seed.catalog.ClientStatus;
import org.example.seed.entity.ClientEntity;
import org.example.seed.event.client.*;
import org.example.seed.mapper.ClientMapper;
import org.example.seed.mapper.TelephoneMapper;
import org.example.seed.repository.ClientRepository;
import org.example.seed.service.ClientService;
import org.example.seed.util.KeyGenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.concurrent.ListenableFuture;


/**
 * Created by PINA on 25/06/2017.
 */
@Service
public class ClientServiceImpl implements ClientService {

  private final ClientMapper clientMapper;
  private final TelephoneMapper telephoneMapper;
  private final ClientRepository clientRepository;

  @Autowired
  public ClientServiceImpl(
    final ClientMapper clientMapper,
    final TelephoneMapper telephoneMapper,
    final ClientRepository clientRepository
  ) {
    this.clientMapper = clientMapper;
    this.telephoneMapper = telephoneMapper;
    this.clientRepository = clientRepository;
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED, readOnly = true)
  public ListenableFuture<ResponseClientsEvent> requestClients(final RequestClientsEvent event) {

    final Page<ClientEntity> clients = this.clientRepository
      .findAll(PageRequest
        .of(event.getPage() - 1, event.getLimit()));

    return new AsyncResult<>(ResponseClientsEvent.builder()
      .clients(this.clientMapper
        .mapListReverse(clients.getContent()))
      .total(clients.getTotalElements())
      .build());
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED)
  public ListenableFuture<ResponseClientEvent> createClient(final CreateClientEvent event) {

    event.getClient().setStatus(ClientStatus.REGISTERED);

    if (this.clientRepository.existsByEmail(event.getClient().getEmail())) {
      throw new RuntimeException("ERROR-00001");
    }

    this.clientRepository
      .save(this.clientMapper
        .map(event.getClient()));

    // TODO: sending activation email

    return new AsyncResult<>(null);
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED)
  public ListenableFuture<ResponseClientEvent> registerClient(final RegisterClientEvent event) {
    return new AsyncResult<>(ResponseClientEvent.builder()
      .client(this.clientMapper
        .map(this.clientRepository
          .findByIdAndStatus(event.getClient().getId(), ClientStatus.REGISTERED)
          .map(clientEntity -> {

            // TODO: to implement keygen activation

            clientEntity.setRating(0F);
            clientEntity.setStatus(ClientStatus.ACTIVATED);
            clientEntity.setSecret(KeyGenUtil.encode(event.getClient().getCredential()));

            this.clientRepository.save(clientEntity);

            // TODO: sending welcome email

            return clientEntity;
          })
          .orElseThrow(() -> new RuntimeException("ERROR-00002"))))
      .build());
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED, readOnly = true)
  public ListenableFuture<ResponseClientEvent> requestClient(final RequestClientEvent event) {
    return new AsyncResult<>(ResponseClientEvent.builder()
      .client(this.clientMapper
        .map(this.clientRepository
          .findById(event.getId())
          .orElseGet(null)))
      .build());
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED)
  public ListenableFuture<ResponseClientEvent> updateClient(final UpdateClientEvent event) {
    return new AsyncResult<>(ResponseClientEvent.builder()
      .client(this.clientMapper
        .map(this.clientRepository
          .findByIdAndStatus(event.getClient().getId(), ClientStatus.ACTIVATED)
          .map(clientEntity -> {

            clientEntity.setRating(event.getClient().getRating());
            clientEntity.setLastName(event.getClient().getLastName());
            clientEntity.setFirstName(event.getClient().getFirstName());
            clientEntity.setTelephone(this.telephoneMapper.map(event.getClient().getTelephone()));

            this.clientRepository.save(clientEntity);

            return clientEntity;
          })
          .orElseThrow(() -> new RuntimeException("ERROR-00002"))))
      .build());
  }

  @Override
  @Async
  @Transactional(isolation = Isolation.READ_COMMITTED)
  public ListenableFuture<ResponseClientEvent> deleteClient(final DeleteClientEvent event) {

    this.clientRepository.deleteById(event.getId());

    return new AsyncResult<>(null);
  }
}

@@ -124,16 +124,64 @@
 Entity;%0A
+import org.example.seed.entity.TelephoneEntity;%0A
 import o
@@ -261,56 +261,8 @@
 er;%0A
-import org.example.seed.mapper.TelephoneMapper;%0A
 impo
@@ -398,16 +398,59 @@
 enUtil;%0A
+import org.example.seed.util.PhoneGenUtil;%0A
 import o
@@ -954,16 +954,40 @@
 uture;%0A%0A
+import java.util.UUID;%0A%0A
 %0A/**%0A * 
@@ -1136,57 +1136,8 @@
 er;%0A
-  private final TelephoneMapper telephoneMapper;%0A
   pr
@@ -1224,21 +1224,16 @@
 iceImpl(
-%0A    
 final Cl
@@ -1260,55 +1260,8 @@
 per,
-%0A    final TelephoneMapper telephoneMapper,%0A   
  fin
@@ -1296,19 +1296,16 @@
 pository
-%0A  
 ) %7B%0A    
@@ -1342,52 +1342,8 @@
 er;%0A
-    this.telephoneMapper = telephoneMapper;%0A
     
@@ -4414,64 +4414,394 @@
 one(
-this.telephoneMapper.map(event.getClient().getTelephone(
+PhoneGenUtil%0A              .map(event.getClient().getTelephone().getNumber(), event.getIp())%0A              .map(phoneDto -%3E TelephoneEntity.builder()%0A                .lada(phoneDto.getLada())%0A                .id(UUID.randomUUID().toString())%0A                .number(phoneDto.getPhoneNumber())%0A                .build())%0A              .orElseThrow(() -%3E new RuntimeException(%22ERROR-00003%22
 )));

add telephone set up using PhoneGenUtil and GeoGenUtil, to validate lada and phone base on region