package ru.kayashov.bar.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import ru.kayashov.bar.controller.dto.bar.BarResponseDto;
import ru.kayashov.bar.controller.dto.ingredient.IngredientSimpleResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto;
import ru.kayashov.bar.model.entity.BarEntity;
import ru.kayashov.bar.model.entity.BarResident;
import ru.kayashov.bar.repository.BarResidentRepository;
import ru.kayashov.bar.model.entity.CategoryEntity;
import ru.kayashov.bar.model.entity.CocktailEntity;
import ru.kayashov.bar.model.entity.GlassEntity;
import ru.kayashov.bar.model.entity.SessionEntity;
import ru.kayashov.bar.model.entity.UserRole;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.BarEntityRepository;
import ru.kayashov.bar.repository.CategoryRepository;
import ru.kayashov.bar.repository.CocktailRepository;
import ru.kayashov.bar.repository.GlassRepository;
import ru.kayashov.bar.repository.RatingRepository;
import ru.kayashov.bar.repository.SessionRepository;
import ru.kayashov.bar.repository.VisitorsRepository;

import java.util.Comparator;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SessionService {

    private final SessionRepository sessionRepository;
    private final VisitorsRepository visitorsRepository;
    private final GlassRepository glassRepository;
    private final CategoryRepository categoryRepository;
    private final CocktailRepository cocktailRepository;
    private final RatingRepository ratingRepository;
    private final VisitorService visitorService;
    private final BarEntityRepository barEntityRepository;
    private final BarResidentRepository barResidentRepository;

    /**
     * Поиск матрицы коктейлей
     * Справочник состоит из количества недостающих компонентов и списка коктейлей
     */
//    public Map<Long, List<CocktailEntity>> getCocktailMatrix() {
//        return cocktailRepository.findAll().stream()
//                .collect(Collectors.groupingBy(this::getRemainingIngredient));
//    }

    /**
     * Поиск активной сессии
     */
    public SessionEntity findActiveSession() {
        List<SessionEntity> sessions = sessionRepository.findAll()
                .stream()
                .sorted(Comparator.comparingLong(SessionEntity::getId).reversed())
                .limit(1)
                .toList();
        if (!sessions.isEmpty()) {
            return sessions.get(0);
        }
        return createEmptySession();
    }

    public Visitor getVisitor() {
        Long id = ((Visitor) SecurityContextHolder.getContext()
                .getAuthentication()
                .getPrincipal())
                .getId();
        return visitorsRepository.findById(id)
                .orElseThrow();
    }

    public SessionEntity createEmptySession() {
        BarEntity bar = visitorService.getCurrentVisitor().getResidents()
                .stream()
                .filter(BarResident::getActive)
                .map(BarResident::getBar)
                .toList()
                .get(0);
        SessionEntity session = new SessionEntity();
        session.setBar(bar);
        session.setIsActive(true);
        return sessionRepository.save(session);
    }

//    private long getRemainingIngredient(CocktailEntity cocktail) {
//        return cocktail.getReceipt()
//                .stream()
//                .map(ReceiptEntity::getIngredient)
////                .map(IngredientEntity::getIsHave)
//                .filter(b -> !b).count();
//    }

    public List<GlassEntity> getGlassList() {
        return glassRepository.findAll();
    }

    public List<CategoryEntity> getCategoryList() {
        return categoryRepository.findAll();
    }

    public List<ReceiptResponseDto> getReceiptList(Long cocktailId) {
        CocktailEntity cocktail = cocktailRepository.findById(cocktailId).orElseThrow();
        return cocktail.getReceipt().stream()
                .map(e -> ReceiptResponseDto.builder()
                        .id(e.getId())
                        .ingredient(IngredientSimpleResponseDto.mapToDto(e.getIngredient()))
                        .measure(e.getMeasure())
                        .build())
                .toList();
    }

    public List<BarResponseDto> getBarList(Boolean my) {
        List<BarResident> residents = visitorService.getCurrentVisitor().getResidents();
        if (my) {
            return residents.stream()
                    .map(r -> mapBarToDto(r.getBar(), r, true))
                    .toList();
        }
        List<Long> myBar = residents.stream()
                .map(BarResident::getBar)
                .map(BarEntity::getId)
                .toList();
        return barEntityRepository.findAll().stream()
                .filter(b -> !myBar.contains(b.getId()))
                .map(b -> mapBarToDto(b, null, false))
                .toList();
    }

    public void addToMyList(BarResponseDto dto) {
        Visitor visitor = visitorService.getCurrentVisitor();
        BarEntity bar = barEntityRepository.findById(dto.getId()).orElseThrow();

        boolean noEnter = visitor.getResidents().stream().filter(BarResident::getActive).toList().isEmpty();

        BarResident resident = new BarResident();
        resident.setBar(bar);
        resident.setInvited(true);
        resident.setRole(UserRole.USER);
        resident.setVisitor(visitor);
        resident.setActive(noEnter);
        barResidentRepository.save(resident);
    }


    public void enterChange(Long id, Boolean value) {
        Visitor visitor = visitorService.getCurrentVisitor();
        List<BarResident> residents = visitor.getResidents()
                .stream()
                .filter(r -> r.getBar().getId().equals(id))
                .toList();
        if(residents.isEmpty()) {
            throw new RuntimeException("Бар с id " + id + " отсутствует в списке баров пользователя с id " + visitor.getId());
        }
        residents.stream()
                .peek(r -> r.setActive(value))
                .forEach(barResidentRepository::save);

        log.info("Пользователь {}-{} {} {} c id {}",
                visitor.getId(),
                visitor.getName().strip(),
                visitor.getLastName().strip(),
                value ? "вошел в бар" : "вышел из бара",
                id);
    }

    private BarResponseDto mapBarToDto(BarEntity bar, BarResident resident, Boolean my) {
        BarResponseDto dto = new BarResponseDto();
        dto.setId(bar.getId());
        dto.setName(bar.getName());
        boolean barOpen = !bar.getSessions().stream().filter(SessionEntity::getIsActive).toList().isEmpty();
        if (my) {
            boolean invited = resident.getInvited();
            dto.setOpen(barOpen && invited);
            dto.setEnter(resident.getActive());
            dto.setMyRole(resident.getRole().toString());
        } else {
            dto.setOpen(barOpen);
            dto.setEnter(false);
            dto.setMyRole(UserRole.USER.toString());
        }

        return dto;
    }
}
