"""Collection app celery tasks."""
import logging

from celery import shared_task

from utils.methods import transform_into_section_name

logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)


@shared_task
def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
    """Generate guide elements."""
    from collection.models import Guide
    from establishment.models import Establishment

    guide = Guide.objects.get(id=guide_id)
    queryset_values = Establishment.objects.filter(**filter_set).values()
    try:
        guide.change_state(Guide.BUILDING)
        for instance in queryset_values:
            populate_establishment_guide(guide_id, instance.get('id'))
    except Exception as e:
        guide.change_state(Guide.WAITING)
        logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                     f'DETAIL: Guide ID {guide_id} - {e}')
    else:
        guide.update_count_related_objects()
        guide.change_state(Guide.BUILT)


@shared_task
def populate_establishment_guide(guide_id: int, establishment_id: int):
    """Extend guide."""
    from collection.models import GuideElement, Guide
    from establishment.models import Establishment

    guide = Guide.objects.get(id=guide_id)
    try:
        guide.change_state(Guide.BUILDING)
        establishment_qs = Establishment.objects.filter(id=establishment_id)
        if establishment_qs.exists():
            establishment = establishment_qs.first()
            root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
            if root_node:
                city_node, _ = GuideElement.objects.get_or_create_city_node(root_node.id,
                                                                            establishment.address.city_id)
                if city_node:
                    section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
                        city_node.id,
                        transform_into_section_name(establishment.establishment_type.index_name),
                        guide.id,
                    )
                    if section_node:
                        params = {
                            'restaurant_section_node_id': section_node.id,
                            'guide_id': guide.id,
                            'establishment_id': establishment.id,
                        }
                        if establishment.last_published_review:
                            params.update({'review_id': establishment.last_published_review.id})
                        GuideElement.objects.get_or_create_establishment_node(**params)
                    else:
                        logger.error(
                            f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                            f'DETAIL: Guide ID {guide_id} - SectionNode is not exists.')
                else:
                    logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                                 f'DETAIL: Guide ID {guide_id} - CityNode is not exists.')
            else:
                logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                             f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
        else:
            logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                         f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
    except Exception as e:
        guide.change_state(Guide.WAITING)
        logger.error(f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
                     f'DETAIL: Guide ID {guide_id} - {e}')
    else:
        guide.update_count_related_objects()
        guide.change_state(Guide.BUILT)


@shared_task
def remove_establishment_guide(guide_id: int, establishment_id: int):
    """Extend guide."""
    from collection.models import GuideElement, Guide
    from establishment.models import Establishment

    guide = Guide.objects.get(id=guide_id)
    try:
        guide.change_state(Guide.REMOVING)
        establishment_qs = Establishment.objects.filter(id=establishment_id)
        if establishment_qs.exists():
            establishment = establishment_qs.first()
            if hasattr(establishment, 'establishment_type') and establishment.establishment_type.index_name:
                establishment_node_qs = GuideElement.objects.filter(establishment=establishment,
                                                                    guide=guide)
                if establishment_node_qs.exists():
                    establishment_node_qs.first().delete()
                else:
                    logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
                                 f'DETAIL: Guide ID {guide_id} - EstablishmentNode {establishment_id} id is not exists.')
            else:
                logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
                             f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} '
                             f'has not establishment type or establishment type index name.')
        else:
            logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
                         f'DETAIL: Guide ID {guide_id} - Establishment {establishment_id} id is not exists.')
    except Exception as e:
        guide.change_state(Guide.WAITING)
        logger.error(f'METHOD_NAME: {remove_establishment_guide.__name__}\n'
                     f'DETAIL: Guide ID {guide_id} - {e}')
    else:
        guide.update_count_related_objects()
        guide.change_state(Guide.BUILT)


@shared_task
def generate_product_guide_elements(guide_id: int, filter_set: dict):
    """Generate guide elements."""
    from collection.models import GuideElement, Guide
    from product.models import Product

    guide = Guide.objects.get(id=guide_id)
    queryset_values = Product.objects.filter(**filter_set).values()
    try:
        guide.change_state(Guide.BUILDING)
        for instance in queryset_values:
            wine_id = instance.get('id')
            wine_qs = Product.objects.filter(id=wine_id)
            if wine_qs.exists():
                wine = wine_qs.first()
                root_node, _ = GuideElement.objects.get_or_create_root_node(guide_id)
                if root_node:
                    wine_region_node, _ = GuideElement.objects.get_or_create_wine_region_node(
                        root_node.id,
                        wine.wine_region.id,
                        guide.id)
                    if wine_region_node:
                        yard_node, _ = GuideElement.objects.get_or_create_yard_node(
                            wine.id,
                            wine_region_node.id,
                            guide.id)
                        if yard_node:
                            wine_color_qs = wine.wine_colors
                            if wine_color_qs.exists():
                                wine_color_section, _ = GuideElement.objects.get_or_create_color_wine_section_node(
                                    wine_color_qs.first().value,
                                    yard_node.id,
                                    guide.id
                                )
                                if wine_color_section:
                                    params = {
                                        'color_wine_section_node_id': wine_color_section.id,
                                        'wine_id': wine.id,
                                        'guide_id': guide.id,
                                    }
                                    if wine.last_published_review:
                                        params.update({'review_id': wine.last_published_review.id})
                                    GuideElement.objects.get_or_create_wine_node(**params)
                            else:
                                logger.error(
                                    f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
                                    f'DETAIL: Guide ID {guide_id} - Wine {wine.id} has not colors.')
                    else:
                        logger.error(
                            f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
                            f'DETAIL: Guide ID {guide_id} - WineRegionNode is not exists.')
                else:
                    logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
                                 f'DETAIL: Guide ID {guide_id} - RootNode is not exists.')
            else:
                logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
                             f'DETAIL: Guide ID {guide_id} - Product {wine_id} id is not exists.')
    except Exception as e:
        guide.change_state(Guide.WAITING)
        logger.error(f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
                     f'DETAIL: Guide ID {guide_id} - {e}')
        guide.update_count_related_objects()
        guide.change_state(Guide.BUILT)


@shared_task
def export_guide(guide_id, user_id, file_type='csv'):
    from collection.models import GuideElement, Guide
    from collection.serializers import GuideElementExportSerializer
    from account.models import User
    from utils.export import SendGuideExport

    user = User.objects.get(id=user_id)
    guide = Guide.objects.get(id=guide_id)
    root = GuideElement.objects.get_root_node(guide)
    if root:
        nodes = root.get_descendants().select_related(
            'review', 'establishment', 'wine_region',
            'product', 'city', 'wine_color_section',
            'section', 'label_photo', 'guide',
            'city__country', 'establishment__establishment_type')
        serializer = GuideElementExportSerializer(nodes, many=True)
        data = serializer.data
        SendGuideExport(
            data=data, guide=guide, user=user, file_type=file_type
        ).send()
