package com.example.backend.books.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.springframework.data.domain.Limit;
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.data.domain.Sort.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.backend.books.model.BookEntity;
import com.example.backend.books.repository.BookRepository;
import com.example.backend.core.error.NotFoundException;

@Service
public class BookService {
    private final BookRepository repository;

    public BookService(BookRepository repository) {
        this.repository = repository;
    }

    @Transactional(readOnly = true)
    public List<BookEntity> getAll(long categoryId) {
        if (categoryId <= 0L) {
            return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
        }
        return repository.findByCategoryId(categoryId);
    }

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

    @Transactional(readOnly = true)
    public Page<BookEntity> getAllByFilters(long categoryId, int page, int size, String sortType, String searchInfo) {
        Sort sort = Sort.by(new Order(Sort.Direction.DESC, "publicationYear"));
        if (sortType != null && !sortType.isEmpty()) {
            sort = Sort.by(getSortInfo(sortType));
        }

        Pageable pageRequest = PageRequest.of(page, size, sort);
        Long categoryIdWrapper = categoryId > 0L ? categoryId : null;
        String searchInfoWrapper = searchInfo != null && !searchInfo.isEmpty() ? searchInfo.toLowerCase() : null;

        return repository.findAllByFilters(categoryIdWrapper, searchInfoWrapper, pageRequest);
    }

    @Transactional(readOnly = true)
    public List<BookEntity> getByIds(List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return new ArrayList<>();
        }
        List<BookEntity> allEntities = repository.findByIdIn(ids);

        Map<Long, BookEntity> entityMap = allEntities.stream()
                .collect(Collectors.toMap(BookEntity::getId, entity -> entity));
        List<BookEntity> sortedEntities = new ArrayList<>();
        for (Long id : ids) {
            BookEntity entity = entityMap.get(id);
            if (entity != null) {
                sortedEntities.add(entity);
            }
        }

        return sortedEntities;
    }

    @Transactional(readOnly = true)
    public List<BookEntity> getNewest(int limit) {
        if (limit > 0) {
            return repository.findSomeNewest(Limit.of(limit));
        }
        return Collections.emptyList();
    }

    @Transactional(readOnly = true)
    public BookEntity get(long id) {
        return repository.findById(id)
                .orElseThrow(() -> new NotFoundException(BookEntity.class, id));
    }

    @Transactional
    public BookEntity create(BookEntity entity) {
        if (entity == null) {
            throw new IllegalArgumentException("Entity is null");
        }
        return repository.save(entity);
    }

    @Transactional
    public BookEntity update(Long id, BookEntity entity) {
        final BookEntity existsEntity = get(id);
        existsEntity.setCategory(entity.getCategory());
        existsEntity.setTitle(entity.getTitle());
        existsEntity.setAuthor(entity.getAuthor());
        existsEntity.setPrice(entity.getPrice());
        existsEntity.setDescription(entity.getDescription());
        existsEntity.setAnnotation(entity.getAnnotation());
        existsEntity.setBookCode(entity.getBookCode());
        existsEntity.setPublisher(entity.getPublisher());
        existsEntity.setSeries(entity.getSeries());
        existsEntity.setPublicationYear(entity.getPublicationYear());
        existsEntity.setPagesNum(entity.getPagesNum());
        existsEntity.setSize(entity.getSize());
        existsEntity.setCoverType(entity.getCoverType());
        existsEntity.setCirculation(entity.getCirculation());
        existsEntity.setWeight(entity.getWeight());
        existsEntity.setImage(entity.getImage());
        return repository.save(existsEntity);
    }

    @Transactional
    public BookEntity delete(Long id) {
        final BookEntity existsEntity = get(id);
        repository.delete(existsEntity);
        return existsEntity;
    }

    private Order getSortInfo(String type) {
        switch (type) {
            case "cheap":
                return new Order(Sort.Direction.ASC, "price");
            case "expensive":
                return new Order(Sort.Direction.DESC, "price");
            case "new":
                return new Order(Sort.Direction.DESC, "publicationYear");
            default:
                return new Order(Sort.Direction.DESC, "publicationYear");
        }
    }

}
