package com.example.backend.orders.service;

import java.util.List;
import java.util.Map;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.backend.core.error.NotFoundException;
import com.example.backend.orders.model.OrderEntity;
import com.example.backend.orders.repository.OrderRepository;
import com.example.backend.books.model.BookEntity;
import com.example.backend.users.model.UserEntity;
import com.example.backend.users.service.UserService;
import com.example.backend.ordersbooks.model.OrderBookEntity;
import com.example.backend.books.service.BookService;

@Service
public class OrderService {
    private final OrderRepository repository;
    private final UserService userService;
    private final BookService bookService;

    public OrderService(OrderRepository repository, UserService userService, BookService bookService) {
        this.repository = repository;
        this.userService = userService;
        this.bookService = bookService;
    }

    @Transactional(readOnly = true)
    public List<OrderEntity> getAll(Long userId) {
        userService.get(userId);
        return repository.findByUserId(userId);
    }

    @Transactional(readOnly = true)
    public Page<OrderEntity> getAll(long userId, int page, int size) {
        final Pageable pageRequest = PageRequest.of(page, size);
        if (userId <= 0L) {
            return repository.findAll(pageRequest);
        }
        userService.get(userId);
        return repository.findByUserId(userId, pageRequest);
    }

    @Transactional(readOnly = true)
    public OrderEntity get(long userId, long id) {
        if (userId <= 0L) {
            return repository.findById(id)
                    .orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
        }
        userService.get(userId);
        return repository.findOneByUserIdAndId(userId, id)
                .orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
    }

    @Transactional
    public OrderEntity create(long userId, OrderEntity entity, Map<Long, Integer> booksIdsCount) {
        if (entity == null) {
            throw new IllegalArgumentException("Entity is null");
        }
        final UserEntity existsUser = userService.get(userId);
        entity.setUser(existsUser);
        List<BookEntity> result = bookService.getAll(0L).stream()
                .filter(book -> booksIdsCount.containsKey(book.getId()))
                .toList();
        result.forEach(book -> {
            final OrderBookEntity orderBook = new OrderBookEntity(entity, book, booksIdsCount.get(book.getId()));
            orderBook.setOrder(entity);
            orderBook.setBook(book);
        });
        return repository.save(entity);
    }

    @Transactional
    public OrderEntity delete(long userId, long id) {
        if (userId <= 0L) {
            final OrderEntity existsEntity = get(0, id);
            repository.delete(existsEntity);
            return existsEntity;
        }
        userService.get(userId);
        final OrderEntity existsEntity = get(userId, id);
        repository.delete(existsEntity);
        return existsEntity;
    }

    @Transactional(readOnly = true)
    public Double getSum(long userId, long id) {
        userService.get(userId);
        return get(userId, id).getOrderBooks().stream()
                .mapToDouble(orderBook -> orderBook.getCount() * orderBook.getBook().getPrice())
                .sum();
    }

    @Transactional(readOnly = true)
    public Integer getFullCount(long userId, long id) {
        userService.get(userId);
        return get(userId, id).getOrderBooks().stream()
                .mapToInt(OrderBookEntity::getCount)
                .sum();
    }

    @Transactional(readOnly = true)
    public List<OrderBookEntity> getOrderBooks(long userId, long id) {
        userService.get(userId);
        return get(userId, id).getOrderBooks().stream()
                .toList();
    }
}
