package com.fsd.hellovelo.service;

import com.fsd.hellovelo.entity.*;
import com.fsd.hellovelo.exceptions.ResourceNotFoundException;
import com.fsd.hellovelo.payload.request.RentalRequest;
import com.fsd.hellovelo.repository.*;
import com.fsd.hellovelo.security.services.UserDetailsImpl;
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.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;

@Service
public class RentalService {

    private final RentalRepository rentalRepository;
    private final BikeRepository bikeRepository;
    private final StationRepository stationRepository;
    private final UserRepository userRepository;
    private final RentalRepoPagination rentalRepoPagination;
    private final OverageChargeRepository overageChargeRepository;

    @Autowired
    public RentalService(RentalRepository rentalRepository, BikeRepository bikeRepository, StationRepository stationRepository, UserRepository userRepository, RentalRepoPagination rentalRepoPagination, OverageChargeRepository overageChargeRepository){
        this.rentalRepository = rentalRepository;
        this.bikeRepository = bikeRepository;
        this.stationRepository = stationRepository;
        this.userRepository = userRepository;
        this.rentalRepoPagination = rentalRepoPagination;
        this.overageChargeRepository = overageChargeRepository;
    }


    public Rental getRental(Long id) {
        return rentalRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Error: Rental with id " + id + " not found."));
    }

    public List<Rental> getRentals() {
        return rentalRepository.findAll();
    }

    public Rental updateRental(RentalRequest rental) {
        Rental currRental = rentalRepository.findById(rental.getId()).orElseThrow(() -> new ResourceNotFoundException("Error: Rental with id \" + id + \" not found."));
        if(!currRental.getStartStation().getId().equals(rental.getStartStationId())){
            Station startStation = stationRepository.findById(rental.getStartStationId()).orElseThrow(() -> new IllegalArgumentException("Error: Start Station does not exist."));
            if(startStation.getStatus().equals(EStatus.DISABLED)){
                throw new IllegalArgumentException("This station does not not exist anymore, choose another one!");
            }
            if(rental.getStartTime().isEqual(LocalDateTime.now()) && startStation.getTotalSlots() <= bikeRepository.countAllByStation(startStation)){
                throw new IllegalArgumentException("This station is full! choose another station or try again later!");
            }
            currRental.setStartStation(startStation);
        }
        if(rental.getEndStationId() ==1 ){
            throw new IllegalArgumentException("End station could not be the warehouse!");
        }
        if(rental.getEndStationId() !=0 && rental.getEndTime() == null ){
            throw new IllegalArgumentException("Error: if bike is not returned, station Id should be 0,otherwise set end time");
        }
        if(rental.getEndStationId() ==0 && rental.getEndTime() != null ){
            throw new IllegalArgumentException("Error: if bike is returned, station Id should be more than one,otherwise do not set end time");
        }
        if (rental.getEndStationId() > 1) {
            //if (!currRental.getEndStation().getId().equals(rental.getEndStationId())) {
                Station endStation = stationRepository.findById(rental.getEndStationId()).orElseThrow(() -> new IllegalArgumentException("Error: End Station does not exist."));
                if(rental.getEndTime().isEqual(LocalDateTime.now()) && endStation.getTotalSlots() <= bikeRepository.countAllByStation(endStation)){
                    throw new IllegalArgumentException("This station is full! choose another station or try again later!");
                }
            if(endStation.getStatus().equals(EStatus.DISABLED)){
                throw new IllegalArgumentException("This station does not not exist anymore, choose another one!");
            }
                currRental.setEndStation(endStation);
            //}
        }else{
            currRental.setEndStation(null);
        }
        if(!currRental.getBike().getId().equals(rental.getBikeId())){
            Bike bike = bikeRepository.findById(rental.getBikeId()).orElseThrow(() -> new IllegalArgumentException("Error: Bike does not exist."));
            if (bike.getStatus().equals(EStatus.DISABLED)){
                throw new IllegalArgumentException("Bike not found., choose another one!");
            }
            currRental.setBike(bike);
        }


        if(!currRental.getUser().getId().equals(rental.getUserId())){
            User user = userRepository.findById(rental.getUserId()).orElseThrow(() -> new IllegalArgumentException("Error: User does not exist."));
            currRental.setUser(user);
        }

        if (rental.getStartTime().isAfter(LocalDateTime.now())){
            throw new IllegalArgumentException("Error: Rental start time should not be in future!");
        }
        if(rental.getEndTime() != null) {
            if (rental.getEndTime().isAfter(LocalDateTime.now())) {
                throw new IllegalArgumentException("Error: Rental End time should not be in future!");
            }
            if (rental.getStartTime().isAfter(rental.getEndTime())) {
                throw new IllegalArgumentException("Error: End time should be after start time");
            }
            currRental.setEndTime(rental.getEndTime());
        }
        else{
            currRental.setEndTime(null);
        }
        currRental.setStartTime(rental.getStartTime());

        rentalRepository.save(currRental);
        return currRental;
    }

/****************************************************** PAGINATED AND SORTED RENTAL LIST **************************************************/

    public Page<Rental> paginatedRentalList(int pageNumber, int pageSize, String sortBy, String sortDir) {

        Page<Rental>  rentalPage= rentalRepoPagination.findAll(
                PageRequest.of(pageNumber, pageSize,
                        sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
                ));
        for (Rental rental:
                rentalPage
        ) {
            OverageCharge overageCharge = overageChargeRepository.findByRentalId(rental.getId());
            if(overageCharge != null) {
                rental.setOverageMinutes(overageCharge.getMinutesOver());
            }else{
                rental.setOverageMinutes(0L);
            }
        }
        return rentalPage;
    }


    /****************************************************** PAGINATED AND SORTED RENTAL LIST WITH SEARCH **************************************************/

    public Page<Rental> paginatedRentalList(int pageNumber, int pageSize, String sortBy, String sortDir, String searchText) {

        Page<Rental>  rentalPage= rentalRepoPagination.findAll(
                PageRequest.of(pageNumber, pageSize,
                        sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
                )
                ,searchText);
        for (Rental rental:
                rentalPage
             ) {
            OverageCharge overageCharge = overageChargeRepository.findByRentalId(rental.getId());
            if(overageCharge != null) {
                rental.setOverageMinutes(overageCharge.getMinutesOver());
            }else{
                rental.setOverageMinutes(0L);
            }
        }
        return rentalPage;



//        return rentalRepoPagination.findAll(
//                PageRequest.of(pageNumber, pageSize,
//                        sortDir.equalsIgnoreCase("asc") ? Sort.by(sortBy).ascending() : Sort.by(sortBy).descending()
//                )
//                ,searchText);
    }

    public HashMap<LocalDate, Integer> getWeeklyStats () {
        LocalDateTime afterDate = LocalDate.now().atStartOfDay().minusWeeks(1L);
        List<Rental> rentals = rentalRepository.findAllByStartTimeIsAfterAndStartTimeIsBefore(afterDate, LocalDate.now().atStartOfDay());
//        HashMap<DayOfWeek, Integer> map = new HashMap<DayOfWeek, Integer>();
        HashMap<LocalDate, Integer> map2 = new HashMap<LocalDate, Integer>();
//        int[] list = new int[7];
        for (Rental rental: rentals)
        {
//            map.put(rental.getStartTime().getDayOfWeek(), map.getOrDefault(rental.getStartTime().getDayOfWeek(), 0) + 1);
            map2.put(rental.getStartTime().toLocalDate(), map2.getOrDefault(rental.getStartTime().toLocalDate(), 0) + 1);

        }
//        for (LocalDate day: map2.keySet()) {
//            String key = day.toString();
//            String value = map2.get(day).toString();
//            System.out.println(key + " " + value);
//        }
//        for (int i = 0; i < list.length; i++) {
//            for (int j = 0; j < rentals.size(); j++) {
//                if(rentals.get(j).getStartTime().getDayOfWeek().getValue() == i + 1) {
//                    list[i] += 1;
//                }
//
//            }
//
//        }


        return map2;
    }
}