package com.fsd.hellovelo.service;

import com.fsd.hellovelo.entity.Bike;
import com.fsd.hellovelo.entity.EStatus;
import com.fsd.hellovelo.entity.Station;
import com.fsd.hellovelo.exceptions.ForeignKeyConstraintException;
import com.fsd.hellovelo.exceptions.ResourceNotFoundException;
import com.fsd.hellovelo.payload.request.BikeTransferRequest;
import com.fsd.hellovelo.payload.request.UpdateBikeRequest;
import com.fsd.hellovelo.repository.BikeRepoPagination;
import com.fsd.hellovelo.repository.BikeRepository;
import com.fsd.hellovelo.repository.StationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

import javax.validation.Valid;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class BikeService {

   @Autowired
   private BikeRepository bikeRepository;

   @Autowired
   private BikeRepoPagination bikeRepoPagination;

   @Autowired
   private StationRepository stationRepository;

   // ********************************** LIST Of ALL BIKES FOR ADMIN **********************************

   public List<Bike> findAllBikes(boolean deleted){
      if(deleted == true){
         return bikeRepository.findAllByStatusIs(EStatus.DISABLED);
      }else{
         return bikeRepository.findAllByStatusIs(EStatus.ACTIVE);
      }
   }

   // ****************** LIST Of ALL BIKES (DUE FOR MAINTENANCE) *********************

//   public List<Bike> findAllBikesDueForMaintenance(){
//      List<Bike> bikeList = bikeRepository.findAllByStatusIs(EStatus.ACTIVE);
//      List<Bike> dueList = new ArrayList<>();
//      for (Bike bike:
//           bikeList) {
//         LocalDate date1 = LocalDate.now();
//         LocalDate date2 = bike.getLastMaintenance();
//
//         long diffInDays = Math.abs(ChronoUnit.DAYS.between(date1, date2));
//         System.out.println(diffInDays);
//         if (diffInDays >= 7) {
//            dueList.add(bike);
//         }
//      }
//         return dueList;
//   }

   /************** GET LIST Of ALL BIKES WITH SEARCH, SORT, PAGINATION *********************/

   public Page<Bike> findAllPaginatedBikeListWithSearch(int pageNumber, int pageSize, String sortBy, String sortDir, String searchText){

         return bikeRepoPagination.findAll(
                 PageRequest.of(pageNumber, pageSize,
                         sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
                 ), searchText
                 );
//      }else{
//         return bikeRepoPagination.findAll(status,
//                 PageRequest.of(pageNumber, pageSize,
//                         sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
//                 ), searchText
//         );
//      }
   }
   /************** GET LIST Of ALL BIKES WITH SORT, PAGINATION *********************/

   public Page<Bike> findAllPaginatedBikeListWithoutSearch(boolean deleted, int pageNumber, int pageSize, String sortBy, String sortDir){
      if(deleted == true){
      return bikeRepoPagination.findAllByStatusIs(EStatus.DISABLED,
              PageRequest.of(pageNumber, pageSize,
                      sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
              )
      );
      }else {
         Page<Bike> bikeList = bikeRepoPagination.findAllByStatusIsNot(EStatus.DISABLED,
                 PageRequest.of(pageNumber, pageSize,
                         sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
                 )
         );
         for (Bike bike :
                 bikeList) {
            LocalDate date1 = LocalDate.now();
            LocalDate date2 = bike.getLastMaintenance();

            long diffInDays = Math.abs(ChronoUnit.DAYS.between(date1, date2));
            System.out.println(diffInDays);
            if (diffInDays >= 7 && !bike.isCurrentlyInUse()) {
               bike.setStatus(EStatus.InMAINTENANCE);
               bike.setStation(stationRepository.getById(1L));
               bikeRepoPagination.save(bike);
            }
//         return bikeRepoPagination.findAllByStatusIsNot(EStatus.DISABLED,
//                 PageRequest.of(pageNumber, pageSize,
//                         sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
//                 )
//         );

         }
         return bikeList;
      }
   }

   // ********************************** LIST Of ALL BIKES FOR USERS (ACTIVE BIKES) **********************************

   public List<Bike> findAllBikesUsers(){
      Station station= stationRepository.getById(1L);
      return bikeRepository.findBikesByCurrentlyInUseIsFalseAndStatusIsAndStationIsNot(EStatus.ACTIVE, station);
   }


   // ********************************** ADD A NEW BIKE  **********************************

   public Bike addBike(@Valid Bike newBike) {
      Optional<Bike> bikeBySerialNumber = bikeRepository.findBySerialNumber(newBike.getSerialNumber());
      newBike.setLastMaintenance(LocalDate.now());
      Station initialStation = stationRepository.getById(1L);
      newBike.setStation(initialStation);
      if(bikeBySerialNumber.isPresent()) {
         throw new IllegalArgumentException("This Bike is already added to the Repository");
      }
//      if (newBike.getBikeType().compareTo(Bike.BikeType.ELECTRIC) != 0){
//         throw new IllegalArgumentException("Bike Type should be STANDARD 0r ELECTRIC");
//      }

      if (newBike.getLastMaintenance().isAfter(LocalDate.now())){
         throw new IllegalArgumentException("Last Maintenance date should not be in the future");
      }
      newBike.setCurrentlyInUse(false);
      //newBike.setBikeType(Bike.BikeType.STANDARD);
      return bikeRepository.save(newBike);
   }


   // ********************************** GET ONE BIKE BY ID  ADMIN**********************************
   public Bike getBike(Long bikeId) {
      boolean exists = bikeRepository.existsById(bikeId);
      if (!exists) {
         throw new ResourceNotFoundException("Bike does not exist with id: " + bikeId);
      }
      return bikeRepository.findById(bikeId).get();
   }

   // ********************************** GET ONE BIKE BY ID  USER **********************************

   public Bike getBikeUsers(Long bikeId) {
      boolean exists = bikeRepository.existsById(bikeId);
      if (!exists) {
         throw new ResourceNotFoundException("Bike does not exist with id: " + bikeId);
      }
      Bike bike = bikeRepository.findById(bikeId).get();
      if (bike.getStatus() == EStatus.DISABLED) {
         throw new ResourceNotFoundException("Bike is unavailable with id: " + bikeId);
      }
      if (bike.getStation().getId() == 1) {
         throw new ResourceNotFoundException("Bike is unavailable with id: " + bikeId);
      }
      if (bike.isCurrentlyInUse()) {
         throw new ResourceNotFoundException("Bike is unavailable with id: " + bikeId);
      }
      return bike;
   }

   // ********************************** UPDATE AN EXISTING BIKE **********************************
   public Bike updateBike(Long bikeId, UpdateBikeRequest bikeDetails) {
      boolean exists = bikeRepository.existsById(bikeId);
      if (!exists) {
         throw new ResourceNotFoundException("Bike does not exist with id: " + bikeId);
      }
      Bike bike = bikeRepository.findById(bikeId).get();
      if (!bike.getSerialNumber().equals(bikeDetails.getSerialNumber())) {
         Optional<Bike> bikeBySerialNumber = bikeRepository.findBySerialNumber(bikeDetails.getSerialNumber());
         if(bikeBySerialNumber.isPresent()) {
            throw new IllegalArgumentException("Bike name is already taken");
         }
      }
      if (bikeDetails.getLastMaintenance().isAfter(LocalDate.now())){
         throw new IllegalArgumentException("Last Maintenance date should not be in the future");
      }
      if(bikeDetails.isCurrentlyInUse() && bikeDetails.getStationId() > 0){
         throw new IllegalArgumentException("If bike is currently in use, Enter 0 for station Id");
      }
      if(!bikeDetails.isCurrentlyInUse() && bikeDetails.getStationId() < 1){
         throw new IllegalArgumentException("If bike is not in use, It should be in station with an Id greater than 0 ");
      }
//      Station newStation = stationRepository.findByName(bikeDetails.getStation().getName());
      boolean newStation = stationRepository.existsById(bikeDetails.getStationId());
      if(newStation){
      Station station = stationRepository.getById(bikeDetails.getStationId());
         if(station.getTotalSlots() <= bikeRepository.countAllByStation(station)){
            throw new IllegalArgumentException("This station is full! choose another station or try again later!");
         }
      if(station.getStatus().equals(EStatus.DISABLED)){
         throw new IllegalArgumentException("This station is not working anymore! choose another station");
      }
      bike.setStation(station);
      }
      else if (bikeDetails.getStationId() == 0){
         bike.setStation(null);
      }
      else {
         throw  new IllegalArgumentException("No Station exist with this Id");
      }


      //bike.setIsCurrentlyInUse(bikeDetails.getIsCurrentlyInUse());
      bike.setCurrentlyInUse(bikeDetails.isCurrentlyInUse());
      //bike.setCurrentlyInUse(bikeDetails.getIsCurrentlyInUse());
      bike.setSerialNumber(bikeDetails.getSerialNumber());
      bike.setLastMaintenance(bikeDetails.getLastMaintenance());
      bike.setBikeType(bikeDetails.getBikeType());
      //if (stationRepository.)
      //bike.setCurrentlyInUse(bikeDetails.getCurrentlyInUse());
      //bike.setBikeType(bikeDetails.getBikeType());
      //.setStation(bikeDetails.getStation());
      System.out.println(bike);
      return bikeRepository.save(bike);
   }

   // ********************************** DELETE A BIKE **********************************
//   public void deleteBike(Long bikeId) {
//      boolean exists = bikeRepository.existsById(bikeId);
//      if (!exists) {
//         throw new ResourceNotFoundException("Bike does not exist with id: " + bikeId);
//      }
//      bikeRepository.deleteById(bikeId);
//   }


   public void deleteBike(Long bikeId) {
      boolean exists = bikeRepository.existsById(bikeId);
      if (!exists) {
         throw new ResourceNotFoundException("Bike does not exist with id: " + bikeId);
      }
      Bike bike = bikeRepository.findById(bikeId).get();
      if (bike.isCurrentlyInUse()){
         throw new IllegalArgumentException("You can not delete a bike when it is in use!");
      }
      bike.setStatus(EStatus.DISABLED);
      bike.setStation(null);
      bikeRepository.save(bike);
   }

   // ********************************** TRANSFER BIKES **********************************
   public Long transferBikes(@Valid BikeTransferRequest bikeTransferRequest) {
      Optional <Station> station = stationRepository.findById(bikeTransferRequest.getTransferStation());
      if (!station.isPresent()) {
         throw new ResourceNotFoundException("Station does not exist with id: " + bikeTransferRequest.getTransferStation());
      }
      List<Bike> bikes = bikeRepository.findByIdIn(bikeTransferRequest.getCheckboxOptions());
      if(bikes.isEmpty()) {
         throw new ResourceNotFoundException("Selected bikes do not exist");
      }

      station.get().setNumberOfBikes(bikeRepository.countAllByStation(station.get()));
      station.get().setAvailableSlots(station.get().getTotalSlots() - station.get().getNumberOfBikes());
      if(station.get().getAvailableSlots() < bikes.size()) {
         throw new IllegalArgumentException("There are not enough available slots at this station.");
      }

      for (Bike bike: bikes) {
         bike.setStation(station.get());
      }
      bikeRepository.saveAll(bikes);
      return bikeTransferRequest.getTransferStation();

   }

}
