package ru.kayashov.bar.mapper;

import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ru.kayashov.bar.controller.dto.cocktail.CocktailForIngredientModalDto;
import ru.kayashov.bar.controller.dto.cocktail.CocktailForListResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.CocktailModalDto;
import ru.kayashov.bar.controller.dto.cocktail.RatingResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto;
import ru.kayashov.bar.controller.dto.ingredient.IngredientSimpleResponseDto;
import ru.kayashov.bar.model.entity.CocktailEntity;
import ru.kayashov.bar.model.entity.IngredientEntity;
import ru.kayashov.bar.model.entity.ReceiptEntity;
import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.VisitorsRepository;

import java.util.List;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class CocktailMapper {

    private final VisitorsRepository visitorsRepository;

    @Transactional
    public List<CocktailForListResponseDto> cocktailsToDtoList(List<CocktailEntity> cocktails, Boolean all, boolean withReceipts) {
        return cocktails.stream()
                .map(c -> cocktailToDto(c, withReceipts))
                .toList();
    }

    public CocktailForListResponseDto cocktailToFullDto(CocktailEntity e) {
        return CocktailForListResponseDto.builder()
                .id(e.getId())
                .name(e.getName())
                .image(e.getImage())
                .category(e.getCategory().getName())
                .alcoholic(e.getAlcoholic().getValue())
                .glass(e.getGlass().getName())
                .instructions(e.getInstructions())
                .video(e.getVideo())
                .receipt(createReceiptDtoList(e.getReceipt()))
                .build();
    }

    private CocktailForListResponseDto cocktailToDto(CocktailEntity e, boolean withReceipts) {
        boolean hasError = false;
        int volume = 0;
        Float abv = 0f;
        Integer p = null;
        for (ReceiptEntity receipt : e.getReceipt()) {
            if (receipt.getCount() == null) {
                hasError = true;
                break;
            }
            if (receipt.getUnit() == null) {
                hasError = true;
                break;
            }
            if (!receipt.getUnit().getId().equals(74L) && abv != null) {
                abv = null;
            }
            if (abv != null) {
                IngredientEntity ingredient = receipt.getIngredient();
                if (ingredient.getAlcohol() && ingredient.getAbv() == null) {
                    hasError = true;
                    break;
                }
                if (ingredient.getAlcohol()) {
                    abv += ingredient.getAbv() * receipt.getCount();
                }
                volume += receipt.getCount();
            }
        }

        if (!hasError && abv != null) {
            p = Math.round(abv / volume);
        }

        return CocktailForListResponseDto.builder()
                .id(e.getId())
                .name(e.getName())
                .image(e.getImage())
                .hasError(hasError)
                .volume(p)
                .category(e.getCategory().getName())
                .alcoholic(e.getAlcoholic().getValue())
                .glass(e.getGlass().getName())
                .components(containCocktailComponents(e.getReceipt()))
                .rating(createRatingDto(e))
                .isAllowed(e.getAllowed())
                .receipt(!withReceipts ? null : e.getReceipt().stream().map(ReceiptResponseDto::mapToDto).toList())
                .build();
    }

    private RatingResponseDto createRatingDto(CocktailEntity entity) {
        RatingResponseDto result = new RatingResponseDto();
        result.setRating(entity.getRating());
        result.setFavourite(entity.getIsFavorite());
        return result;
    }

    private String containCocktailComponents(List<ReceiptEntity> receipts) {
        return receipts.stream()
                .map(ReceiptEntity::getIngredient)
                .map(IngredientEntity::getName)
                .collect(Collectors.joining(", "));
    }

    private Visitor getCurrentVisitor() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        Long visitorId;
        if (authentication.getPrincipal() instanceof Visitor) {
            visitorId = ((Visitor) authentication.getPrincipal()).getId();
        } else {
            visitorId = 1L;
        }
        return visitorsRepository.findById(visitorId).orElseThrow(RuntimeException::new);
    }

    public CocktailModalDto cocktailToModalDto(CocktailEntity e) {
        return CocktailModalDto.builder()
                .id(e.getId())
                .name(e.getName())
                .image(e.getImage())
                .instructions(e.getInstructions())
                .rating(createRatingDto(e))
                .receipt(createReceiptDtoList(e.getReceipt()))
                .build();
    }

    private List<ReceiptResponseDto> createReceiptDtoList(List<ReceiptEntity> receipts) {
        return receipts.stream()
                .map(this::createReceiptDto)
                .toList();
    }

    private ReceiptResponseDto createReceiptDto(ReceiptEntity e) {
        return ReceiptResponseDto.builder()
                .id(e.getId())
                .ingredient(createIngredientResponseDto(e.getIngredient()))
                .count(e.getCount())
                .unit(e.getUnit())
                .measure(e.getMeasure())
                .build();
    }

    private IngredientSimpleResponseDto createIngredientResponseDto(IngredientEntity i) {
        return new IngredientSimpleResponseDto(i.getId(),
                i.getName(),
                "https://thecocktaildb.com/images/ingredients/" + i.getEnName() + "-Medium.png",
                i.getIsHave());
    }


    public CocktailForIngredientModalDto cocktailToIngredientDtoList(CocktailEntity e) {
        return CocktailForIngredientModalDto.builder()
                .id(e.getId())
                .name(e.getName())
                .image(e.getImage())
                .rating(createRatingDto(e))
                .build();
    }
}
