from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login as auth_login, logout
from django.contrib.auth.decorators import user_passes_test, login_required
from rest_framework.decorators import api_view
from django.views.decorators.http import require_POST
from django.forms.models import inlineformset_factory
from django.db.models import Sum, Avg, DateTimeField
from django.core.exceptions import ValidationError
from django.db.models import F
import razorpay
import hmac
import hashlib
import json
import twilio
from django.views.decorators.csrf import csrf_exempt
from datetime import datetime, date, timezone
from .signals.custom_signals import shop_add_status_change
import requests, random
from itertools import chain

from django.shortcuts import get_object_or_404
from django.conf import settings
from django.http import JsonResponse, HttpResponse
from django.contrib import messages
from django.utils import timezone
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.apps import apps

from cms.models import FAQ, AboutUs, AppUrl, ContactUs, PrivacyPolicy, TermsAndConditions
from .forms import AboutUsForm, AppUrlForm, ContactUsForm, FAQForm, PrivacyPolicyForm, ShopForm, StoreUserForm, \
    TermsAndConditionsForm, UserEditForm, UserForm, UserModalForm, BankDetailsForm, CategoryForm, \
    ProductSubCategoryForm, ProductForm, TagForm, DynamicFilterForm, SpecialListForm, DeliveryBoyForm, SKUForm, \
    CourierDetailsForm, CouponForm, DeliverySlotForm, DiscountForm, AdForm, CustomProductForm, ProductionUnitForm, \
    CustomOrderForm, MessageForm, YouMayAlsoLikeForm, DeliverySettingsForm, DeliveryDiscountForm, CustomCouponSettingsForm
from shops.models import Shop, BankDetails, ProductionUnit
from products.models import ProductCategory, ProductSubCategory, Products, SalesUnitProductSelection, ProductImage, \
    ProductVideo, Tags, DynamicFiltering, SpecialList, SKU, CustomProduct, CustomProductImage, CustomProductVideo, \
    YouMayAlsoLike
from accounts.models import *
from orders.models import DeliveryBoys, Orders, OrderDelivery, Coupons, DeliverySlot, Discount, Ads, Payment, \
    Communication, Message, DeliveryDiscount, DeliverySettings, CustomCouponSettings, CustomCoupon
from django.core.mail import send_mail
from NavyaBackers.settings import EMAIL_HOST_USER
import string, secrets
from datetime import timedelta
# from accounts.views import send_notification
from accounts.views import initialize_fcm_app

from datetime import datetime
from django.urls import reverse
from django.contrib.auth.models import Group
from django.db.models import Count, Q, F
from django.template.loader import render_to_string
from dal import autocomplete
import requests

from rest_framework.permissions import IsAuthenticated
import jwt
from django.conf import settings
from django.core.mail import send_mail
from django.urls import reverse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
import logging

from django.core.mail import EmailMessage
import os
from celery import chain
from accounts.models import LoggingOperation
from adminportal.tasks import send_whatsapp_message_delivery_boy, email_sending, send_notification, send_whatsapp_message, sms, shop_admin_status_update, shop_admin_status_update_only_one_shop,\
    category_based_shop_admin_status_celery, sub_category_based_shop_admin_status_celery, category_product_list, product_availability_shop_status_change_celery, master_product_edit_celery_task_to_update_shop_availability,\
    update_sku_long_distance_status_check_celery, long_distance_status_check_celery, sku_delete_long_distance_celery, long_distance_master_product_edit
from celery import group



# send email with the map

def EmailSend(subject, message, email_list, fail_silently):
    # change to celery
    # email_data = email_sending.s(subject=subject,
    #                              message=message,
    #                              from_email=settings.EMAIL_HOST_USER,
    #                              recipient_list=[delivery_boy.email],
    #                              fail_silently=False, )
    try:
        email_data = email_sending.apply_async(args=[subject, message, settings.EMAIL_HOST_USER, email_list, False],
                                               countdown=5)
        return email_data
    except Exception as e:
        logging.info(f"Email Send Failed for ({subject}): {e} ")




# notification sending function

def NotificationInit(fcm_token, order, title, description, user_uuid):
    try:
        initialize_fcm_app()
        send_notification.apply_async(args=[fcm_token, order.order_ID, title, description, user_uuid.pk, order.uuid], countdown=5)
        return JsonResponse({"status": 1 , "msg": "notification send successfully"})
    except Exception as e:
        logging.error(f"Notification send Failed on ({description}): {e}")
        return None




# whatsapp message sending function
# not implemented or tested yet
def WhatsappMessage(phone_number, message_name, name, msg):

    try:
        data = send_whatsapp_message.apply_async(args=[phone_number, message_name, name, msg],countdown=5)

        return data

    except Exception as e:
        logging.error(f"Send Whatsapp message to Shipper Failed: {e}")
        return None



# generate random password for account login
def generate_random_password(length=8):
    characters = string.ascii_letters + string.digits
    return ''.join(secrets.choice(characters) for _ in range(length))



# admin side login logic
# we have 4 types of login super admin nbc adminn and pu admin and shop admin
def login(request):
    error = {}
    if request.method == "POST":
        email = request.POST.get("username", "")
        password = request.POST.get("password", "")

        # fetch email and password and authenticate

        user = authenticate(request, username=email, password=password)
        if user is not None:
            auth_login(request, user)
            return redirect("homepage")
        else:
            error = {"message": "invalid username or password"}
    return render(request, "login.html", error)


# function to check the user role
# user roles are shop admin, pu admin, nbc admin
def check_user_role(allowed_roles):
    def inner_function(user):
        return user.user_type in allowed_roles

    return inner_function



#get avergae failed orders with number of failed orders and orders today
def get_average_failed_orders(orders_today, failed_order_today):
    if orders_today:
        avg_failed_orders = round((failed_order_today / orders_today) * 100, 2)
    else:
        avg_failed_orders = 0

    return avg_failed_orders



# percent of week revenue with the last week revenue
def get_week_difference_output(week_revenue, week_before_last_week_revenue):
    revenue_difference_output = {}
    if week_revenue['week_revenue'] and week_before_last_week_revenue['last_week_revenue']:
        week_revenue_percent_with_week = round(
            (week_revenue['week_revenue'] / week_before_last_week_revenue['last_week_revenue']) * 100, 2)
        if week_revenue_percent_with_week > 100:
            week_revenue_percent_with_week = week_revenue_percent_with_week - 100
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'
        else:
            week_revenue_percent_with_week = 100 - week_revenue_percent_with_week
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'decrease'

    else:
        if week_revenue['week_revenue'] and not week_before_last_week_revenue['last_week_revenue']:
            week_revenue_percent_with_week = 100
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'
        else:
            week_revenue_percent_with_week = 0
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'
    return revenue_difference_output


# today's  average with last week revenue
def get_revenue_percent_data(today_revenue, last_week_revenue_avg):
    revenue_percent_data = {}
    if today_revenue['today_revenue'] and last_week_revenue_avg['last_week_avg_revenue']:
        today_revenue_percent_with_week = round((today_revenue['today_revenue'] / last_week_revenue_avg[
            'last_week_avg_revenue']) * 100, 2)
        if today_revenue_percent_with_week > 100:
            today_revenue_percent_with_week = today_revenue_percent_with_week - 100
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
        else:
            today_revenue_percent_with_week = 100 - today_revenue_percent_with_week
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'decrease'

    else:
        if today_revenue['today_revenue'] and not last_week_revenue_avg['last_week_avg_revenue']:
            today_revenue_percent_with_week = 100
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
        else:
            today_revenue_percent_with_week = 0
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
    return revenue_percent_data

# count average data
# average of orders today with last week count
def get_revenue_count_data(orders_today, last_week_revenue_count):
    revenue_count_data = {}
    if orders_today and last_week_revenue_count:
        today_count_percent_with_week = round((orders_today / last_week_revenue_count), 2) * 100
        if today_count_percent_with_week > 100:
            today_count_percent_with_week = today_count_percent_with_week - 100
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'
        else:
            today_count_percent_with_week = 100 - today_count_percent_with_week
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'decrease'
    else:
        if orders_today and not last_week_revenue_count:
            today_count_percent_with_week = 100
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'
        else:
            today_count_percent_with_week = 0
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'
    return revenue_count_data




# monthly revenue calculation
def get_month_revenue(today, shop_id):
    month_revenue = Orders.objects.filter(created_date__year=today.year,
                                          created_date__month=today.month, store_uuid=shop_id.uuid).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(
        month_revenue=Sum('grand_total'))
    month_revenue['month_revenue'] = round(
        month_revenue['month_revenue'] if month_revenue['month_revenue'] else 0, 2)
    return month_revenue



# shop admin homepage
def shop_admin_homepage(request, today):

    shop_id_verify = None


    shop_id_verify = Shop.objects.prefetch_related('unit_admin_user').filter(
        unit_admin_user__uuid=request.user.uuid)
    if shop_id_verify:

        shop_id = shop_id_verify[0]
        request.session['product_view'] = 'Yes'

        # order today data
        orders_today = Orders.objects.exclude(order_status__in=["Failed", "New Order"]).filter(
            created_date__date=today, store_uuid=shop_id.uuid).count()

        # new orders today
        new_order_data = list(Orders.objects.filter(created_date__date=today, order_status='Confirmed',
                                                    store_uuid=shop_id.uuid).values("order_ID", ))
        new_order_today = len(new_order_data)

        # failed orders today

        failed_order_today = Orders.objects.filter(created_date__date=today, order_status="Failed",
                                                   store_uuid=shop_id.uuid).count()

        avg_failed_orders = get_average_failed_orders(orders_today, failed_order_today)


        # delivered orders today

        fulfiled_order = Orders.objects.filter(created_date__date=today, order_status='Delivered',
                                               store_uuid=shop_id.uuid).count()

        # unassigned orders today
        currently_unassigned_order = Orders.objects.filter(created_date__date=today,
                                                           order_status__in=["Confirmed", "Viewed", "Order Packed"],
                                                           store_uuid=shop_id.uuid).count()

        # total orders today and revenue
        total_orders = Orders.objects.filter(store_uuid=shop_id.uuid).exclude(
            order_status__in=["New Order"]).count()
        today_revenue = Orders.objects.filter(created_date__date=today, store_uuid=shop_id.uuid).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            today_revenue=Sum('grand_total'))
        today_revenue_data = 0
        if today_revenue['today_revenue']:
            today_revenue_data = round(today_revenue['today_revenue'] if today_revenue['today_revenue'] else 0, 2)
        last_week_start = today - timedelta(days=7)
        week_before_last_week = last_week_start - timedelta(days=7)
        week_revenue = Orders.objects.filter(created_date__date__gte=last_week_start,
                                             created_date__date__lte=today, store_uuid=shop_id.uuid).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            week_revenue=Sum('grand_total'))

        week_before_last_week_revenue = Orders.objects.filter(created_date__date__gte=week_before_last_week,
                                                              created_date__date__lte=last_week_start,
                                                              store_uuid=shop_id.uuid).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            last_week_revenue=Sum('grand_total'))

        revenue_difference_output = get_week_difference_output(week_revenue, week_before_last_week_revenue)


        last_week_revenue_avg = Orders.objects.filter(store_uuid=shop_id.uuid,
                                                      created_date__date__gte=last_week_start,
                                                      created_date__date__lte=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(last_week_avg_revenue=Avg('grand_total'))
        revenue_percent_data = get_revenue_percent_data(today_revenue, last_week_revenue_avg)

        last_week_revenue_count = Orders.objects.filter(store_uuid=shop_id.uuid,
                                                        created_date__date__gte=last_week_start,
                                                        created_date__date__lte=today).exclude(
            order_status__in=["New Order", "Failed"]).count()
        revenue_count_data = get_revenue_count_data(orders_today, last_week_revenue_count)

        month_revenue = get_month_revenue(today, shop_id)
        delivery_success_percentage = 0
        if fulfiled_order and orders_today:
            delivery_success_percentage = round((fulfiled_order / orders_today) * 100, 2)
        avg_today_revenue = Orders.objects.filter(store_uuid=shop_id.uuid, created_date__date=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            today_revenue=Avg('grand_total'))
        avg_today_revenue_data = 0
        if avg_today_revenue['today_revenue']:
            avg_today_revenue_data = round(avg_today_revenue['today_revenue'], 2)
        results = Orders.objects.filter(store_uuid=shop_id.uuid, created_date__date=today).exclude(
            order_status__in=["New Order", "Failed"]).values('store_uuid__unit_name').annotate(
            total_amount=Sum('grand_total')).order_by(
            '-total_amount')[:5]

        for i in results:
            if i['total_amount']:
                i['total_amount'] = round(i['total_amount'], 2)
        context = {"orders_today": orders_today, 'new_order_today': new_order_today,
                   "fulfiled_order": fulfiled_order,
                   'currently_unassigned_order': currently_unassigned_order,
                   "failed_order": failed_order_today,
                   "avg_failed_order": avg_failed_orders,
                   'total_orders': total_orders, 'today_revenue': today_revenue_data,
                   'month_revenue': month_revenue, "grand_total_by_store": results,
                   'today_count_percent_with_week': revenue_count_data,
                   'today_revenue_percent_with_week': revenue_percent_data,
                   'delivery_success_percentage': delivery_success_percentage,
                   'avg_today_revenue': avg_today_revenue_data,
                   'revenue_difference_output': revenue_difference_output, 'home_active': "active",
                   "new_order_data": new_order_data}
        return context


def nbc_admin_homepage(request, today):


        request.session["product_view"] = 'No'
        orders_today = Orders.objects.exclude(order_status__in=["Failed", "New Order"]).filter(
            created_date__date=today).count()
        new_order_data = list(Orders.objects.filter(created_date__date=today,order_status='Confirmed',).values("order_ID", ))
        new_order_today = len(new_order_data)
        failed_order_today = Orders.objects.filter(created_date__date=today, order_status="Failed").count()
        avg_failed_orders = get_average_failed_orders(orders_today, failed_order_today)
        fulfiled_order = Orders.objects.filter(created_date__date=today, order_status='Delivered').count()
        currently_unassigned_order = Orders.objects.filter(order_status__in=["Confirmed", "Viewed", "Order Packed"],
                                                           created_date__date=today).count()
        last_week_start = today - timedelta(days=7)

        week_before_last_week = last_week_start - timedelta(days=7)
        week_revenue = Orders.objects.filter(created_date__date__gte=last_week_start,
                                             created_date__date__lte=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            week_revenue=Sum('grand_total'))
        week_before_last_week_revenue = Orders.objects.filter(created_date__date__gte=week_before_last_week,
                                                              created_date__date__lte=last_week_start).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            last_week_revenue=Sum('grand_total'))
        revenue_difference_output = {}

        if week_revenue['week_revenue'] and week_before_last_week_revenue['last_week_revenue']:
            week_revenue_percent_with_week = round(
                (week_revenue['week_revenue'] / week_before_last_week_revenue['last_week_revenue']) * 100, 2)
            if week_revenue_percent_with_week > 100:
                week_revenue_percent_with_week = week_revenue_percent_with_week - 100
                revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
                revenue_difference_output['flag'] = 'increase'
            else:
                week_revenue_percent_with_week = 100 - week_revenue_percent_with_week
                revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
                revenue_difference_output['flag'] = 'decrease'

        else:
            if week_revenue['week_revenue'] and not week_before_last_week_revenue['last_week_revenue']:
                week_revenue_percent_with_week = 100
                revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
                revenue_difference_output['flag'] = 'increase'
            else:
                week_revenue_percent_with_week = 0
                revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
                revenue_difference_output['flag'] = 'increase'

        last_week_revenue_avg = Orders.objects.filter(created_date__date__gte=last_week_start,
                                                      created_date__date__lte=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(
            last_week_avg_revenue=Avg('grand_total'))
        today_revenue = Orders.objects.filter(created_date__date=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(today_revenue=Sum('grand_total'))
        today_revenue_data = 0
        if today_revenue['today_revenue']:
            today_revenue_data = round(today_revenue['today_revenue'], 2)
        revenue_percent_data = {}
        if today_revenue['today_revenue'] and last_week_revenue_avg['last_week_avg_revenue']:
            today_revenue_percent_with_week = round(
                (today_revenue['today_revenue'] / last_week_revenue_avg['last_week_avg_revenue']) * 100, 2)
            if today_revenue_percent_with_week > 100:
                today_revenue_percent_with_week = today_revenue_percent_with_week - 100
                revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
                revenue_percent_data['flag'] = 'increase'
            else:
                today_revenue_percent_with_week = 100 - today_revenue_percent_with_week
                revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
                revenue_percent_data['flag'] = 'decrease'

        else:
            if today_revenue['today_revenue'] and not last_week_revenue_avg['last_week_avg_revenue']:
                today_revenue_percent_with_week = 100
                revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
                revenue_percent_data['flag'] = 'increase'
            else:
                today_revenue_percent_with_week = 0
                revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
                revenue_percent_data['flag'] = 'increase'
        last_week_revenue_count = Orders.objects.filter(created_date__date__gte=last_week_start,
                                                        created_date__date__lte=today).exclude(
            order_status__in=["New Order", "Failed"]).count()
        revenue_count_data = {}
        if orders_today and last_week_revenue_count:
            today_count_percent_with_week = round((orders_today / last_week_revenue_count) * 100, 2)
            if today_count_percent_with_week > 100:
                today_count_percent_with_week = today_count_percent_with_week - 100
                revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
                revenue_count_data['flag'] = 'increase'
            else:
                today_count_percent_with_week = 100 - today_count_percent_with_week
                revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
                revenue_count_data['flag'] = 'decrease'
        else:
            if orders_today and not last_week_revenue_count:
                today_count_percent_with_week = 100
                revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
                revenue_count_data['flag'] = 'increase'
            else:
                today_count_percent_with_week = 0
                revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
                revenue_count_data['flag'] = 'increase'

        delivery_success_percentage = 0

        if fulfiled_order and orders_today:
            delivery_success_percentage = round((fulfiled_order / orders_today) * 100, 2)
        avg_today_revenue = Orders.objects.filter(created_date__date=today).exclude(
            order_status__in=["New Order", "Failed"]).aggregate(today_revenue=Avg('grand_total'))

        avg_today_revenue_data = 0
        if avg_today_revenue['today_revenue']:
            avg_today_revenue_data = round(avg_today_revenue['today_revenue'], 2)

        total_orders = Orders.objects.exclude(order_status__in=["New Order"]).count()

        month_revenue = Orders.objects.filter(created_date__year=today.year,
                                              created_date__month=today.month, order_status="Delivered").aggregate(
            month_revenue=Sum('grand_total'))
        month_revenue['month_revenue'] = round(month_revenue['month_revenue'] if month_revenue['month_revenue'] else 0,
                                               2)
        results = Orders.objects.filter(created_date__date=today, order_type="Local Orders").exclude(
            order_status__in=["New Order", "Failed"]).values('store_uuid__unit_name').annotate(
            total_amount=Sum('grand_total')).order_by(
            '-total_amount')[:5]
        for i in results:
            if i['total_amount']:
                i['total_amount'] = round(i['total_amount'], 2)
        context = {"orders_today": orders_today, 'new_order_today': new_order_today, "fulfiled_order": fulfiled_order,
                   'currently_unassigned_order': currently_unassigned_order,
                   # 'orders_over_5_hours': orders_over_5_hours,
                   "failed_order": failed_order_today,
                   "avg_failed_orders": avg_failed_orders,
                   'total_orders': total_orders, 'today_revenue': today_revenue_data,
                   'month_revenue': month_revenue, "grand_total_by_store": results,
                   'today_count_percent_with_week': revenue_count_data,
                   'today_revenue_percent_with_week': revenue_percent_data,
                   'delivery_success_percentage': delivery_success_percentage,
                   'avg_today_revenue': avg_today_revenue_data, 'revenue_difference_output': revenue_difference_output,
                   'home_active': "active", "new_order_data": new_order_data}

        return context


def pu_admin_home_page(request, today):
    request.session["product_view"] = 'No'
    orders_today = Orders.objects.exclude(order_status__in=["Failed", "New Order"]).filter(
        order_type="Long Distance Orders",
        created_date__date=today).count()

    new_order_data = list(Orders.objects.filter(created_date__date=today, order_status='Confirmed',
                                                order_type="Long Distance Orders").values("order_ID", ))
    new_order_today = len(new_order_data)

    fulfiled_order = Orders.objects.filter(created_date__date=today, order_status='Delivered',
                                           order_type="Long Distance Orders").count()
    failed_order_today = Orders.objects.filter(created_date__date=today, order_status="Failed",
                                               order_type="Long Distance Orders").count()

    avg_failed_orders = get_average_failed_orders(orders_today, failed_order_today)
    currently_unassigned_order = Orders.objects.filter(order_status__in=["Confirmed", "Viewed", "Order Packed"],
                                                       created_date__date=today,
                                                       order_type="Long Distance Orders").count()
    last_week_start = today - timedelta(days=7)

    week_before_last_week = last_week_start - timedelta(days=7)
    week_revenue = Orders.objects.filter(order_type="Long Distance Orders", created_date__date__gte=last_week_start,
                                         created_date__date__lte=today).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(
        week_revenue=Sum('grand_total'))
    week_before_last_week_revenue = Orders.objects.filter(order_type="Long Distance Orders",
                                                          created_date__date__gte=week_before_last_week,
                                                          created_date__date__lte=last_week_start).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(
        last_week_revenue=Sum('grand_total'))
    revenue_difference_output = {}
    if week_revenue['week_revenue'] and week_before_last_week_revenue['last_week_revenue']:
        week_revenue_percent_with_week = round(
            (week_revenue['week_revenue'] / week_before_last_week_revenue['last_week_revenue']) * 100, 2)
        if week_revenue_percent_with_week > 100:
            week_revenue_percent_with_week = week_revenue_percent_with_week - 100
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'
        else:
            week_revenue_percent_with_week = 100 - week_revenue_percent_with_week
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'decrease'

    else:
        if week_revenue['week_revenue'] and not week_before_last_week_revenue['last_week_revenue']:
            week_revenue_percent_with_week = 100
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'
        else:
            week_revenue_percent_with_week = 0
            revenue_difference_output['revenue_difference'] = round(week_revenue_percent_with_week, 2)
            revenue_difference_output['flag'] = 'increase'

    last_week_revenue_avg = Orders.objects.filter(order_type="Long Distance Orders",
                                                  created_date__date__gte=last_week_start,
                                                  created_date__date__lte=today).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(
        last_week_avg_revenue=Avg('grand_total'))
    today_revenue = Orders.objects.filter(order_type="Long Distance Orders", created_date__date=today).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(today_revenue=Sum('grand_total'))
    today_revenue_data = 0
    if today_revenue['today_revenue']:
        today_revenue_data = round(today_revenue['today_revenue'], 2)
    revenue_percent_data = {}
    if today_revenue['today_revenue'] and last_week_revenue_avg['last_week_avg_revenue']:
        today_revenue_percent_with_week = round((today_revenue['today_revenue'] / last_week_revenue_avg[
            'last_week_avg_revenue']) * 100, 2)
        if today_revenue_percent_with_week > 100:
            today_revenue_percent_with_week = today_revenue_percent_with_week % 100
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
        else:
            today_revenue_percent_with_week = 100 % today_revenue_percent_with_week
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'decrease'

    else:
        if today_revenue['today_revenue'] and not last_week_revenue_avg['last_week_avg_revenue']:
            today_revenue_percent_with_week = 100
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
        else:
            today_revenue_percent_with_week = 0
            revenue_percent_data['today_revenue_percent_with_week'] = round(today_revenue_percent_with_week, 2)
            revenue_percent_data['flag'] = 'increase'
    last_week_revenue_count = Orders.objects.filter(order_type="Long Distance Orders",
                                                    created_date__date__gte=last_week_start,
                                                    created_date__date__lte=today, order_status="Delivered").count()
    revenue_count_data = {}
    if orders_today and last_week_revenue_count:
        today_count_percent_with_week = round((orders_today / last_week_revenue_count) * 100, 2)
        if today_count_percent_with_week > 100:
            today_count_percent_with_week = today_count_percent_with_week - 100
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'
        else:
            today_count_percent_with_week = 100 - today_count_percent_with_week
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'decrease'
    else:
        if orders_today and not last_week_revenue_count:
            today_count_percent_with_week = 100
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'
        else:
            today_count_percent_with_week = 0
            revenue_count_data['today_count_percent_with_week'] = round(today_count_percent_with_week, 2)
            revenue_count_data['flag'] = 'increase'

    delivery_success_percentage = 0

    if fulfiled_order and orders_today:
        delivery_success_percentage = round((fulfiled_order / orders_today) * 100, 2)
    avg_today_revenue = Orders.objects.filter(order_type="Long Distance Orders", created_date__date=today).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(today_revenue=Avg('grand_total'))
    avg_today_revenue_data = 0
    if avg_today_revenue['today_revenue']:
        avg_today_revenue_data = round(avg_today_revenue['today_revenue'], 2)

    total_orders = Orders.objects.filter(order_type="Long Distance Orders").exclude(
        order_status__in=["New Order"]).count()

    month_revenue = Orders.objects.filter(order_type="Long Distance Orders", created_date__year=today.year,
                                          created_date__month=today.month).exclude(
        order_status__in=["New Order", "Failed"]).aggregate(
        month_revenue=Sum('grand_total'))
    month_revenue['month_revenue'] = round(month_revenue['month_revenue'] if month_revenue['month_revenue'] else 0,
                                           2)
    results = Orders.objects.filter(created_date__date=today, order_type="Long Distance Orders").exclude(
        order_status__in=["New Order", "Failed"]).values('pu_uuid__pu_name').annotate(
        total_amount=Sum('grand_total')).order_by(
        '-total_amount')[:5]
    for i in results:
        if i['total_amount']:
            i['total_amount'] = round(i['total_amount'], 2)
    context = {"orders_today": orders_today, 'new_order_today': new_order_today, "fulfiled_order": fulfiled_order,
               'currently_unassigned_order': currently_unassigned_order,
               "failed_order": failed_order_today,
               "avg_failed_orders": avg_failed_orders,
               'total_orders': total_orders, 'today_revenue': today_revenue_data,
               'month_revenue': month_revenue, "grand_total_by_pu": results,
               'today_count_percent_with_week': revenue_count_data,
               'today_revenue_percent_with_week': revenue_percent_data,
               'delivery_success_percentage': delivery_success_percentage,
               'avg_today_revenue': avg_today_revenue_data, 'revenue_difference_output': revenue_difference_output,
               'home_active': "active", "new_order_data": new_order_data}
    return context



# homepage redirect
# homepage for every roles are handled here
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin", "Shop Admin", "PU Admin"]))
def home_page(request):
    context = {}
    today = timezone.now().date()

    # shop admin homepage context
    if request.user.user_type == "Shop Admin":
        context = shop_admin_homepage(request, today)

    # nbc admin homepage context
    elif request.user.user_type == "NBC Admin" or request.user.user_type == "Super Admin":
        context = nbc_admin_homepage(request, today)

    # pu admin homepage context
    elif request.user.user_type == "PU Admin":
        context = pu_admin_home_page(request, today)

    return render(request, "index.html", context)

#...............................start shop views .................................................#


# shop listing on nbc admin, super admin dashboard
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def shop_list(request):
    shops = Shop.objects.order_by("-created_at")
    paginator = Paginator(shops, 10)
    page = request.GET.get("page")
    try:
        shops = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        shops = paginator.get_page(1)
    except EmptyPage:
        shops = paginator.get_page(paginator.num_pages)
    context = {"shop_data": shops, "sales_unit_active": "active"}
    return render(request, "shop_list.html", context)


# on creating shop assign a default shop status
def shop_status_initialization(shop):
    product_enter_list = []
    # shop status assigning for newly created shop....
    for i in SKU.objects.filter():
        sales_status = SalesUnitProductSelection.objects.filter(sku=i).first()
        if sales_status:
            status = sales_status.status
        else:
            status = i.sku_status

        if i.sku_status == "Visible":
            product_enter_list.append(
                SalesUnitProductSelection(sales_unit=shop, sku=i, status=status, shop_admin_status=status))
        elif i.sku_status == "Disabled":
            product_enter_list.append(
                SalesUnitProductSelection(sales_unit=shop, sku=i, status=status, shop_admin_status=status))
        elif i.sku_status == "Out of Stock":
            product_enter_list.append(
                SalesUnitProductSelection(sales_unit=shop, sku=i, status=status, shop_admin_status=status))
    return product_enter_list


# function to find the error page
def shop_add_error_page_find(shop_form, bank_form):
    shop_error_columns = list(shop_form.errors.keys())
    bank_error_columns = list(bank_form.errors.keys())
    error_list = shop_error_columns + bank_error_columns
    step1_columns = ["unit_name", "unit_code", "unit_location", "email", "contact_no", "status",
                     "unit_admin_user", "delivery_mode", "delivery_radius"]
    step2_columns = ["latitude", "longitude", "street", "city", "state_or_province", "district", "pin_code"]
    step3_columns = ["account_name", "bank_name", "branch_name", "ifsc_code", "account_number", "gst"]
    step1 = set(error_list) & set(step1_columns)
    step2 = set(error_list) & set(step2_columns)
    step3 = set(error_list) & set(step3_columns)
    page = 0
    if step1:
        page = 0
    elif step2:
        page = 1
    elif step3:
        page = 2
    return page



# shop adding from shop list page
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def shop_add(request):
    errors = []
    user_modal_form = UserModalForm()
    if request.method == "POST":
        shop_form = ShopForm(request.POST)
        bank_form = BankDetailsForm(request.POST)
        # saving shop details and bank details with the form
        if shop_form.is_valid() and bank_form.is_valid():
            shop = shop_form.save()
            bank_details = bank_form.save(commit=False)
            bank_details.shop = shop
            bank_details.save()
            # on creating shop assign a default shop status
            product_enter_list = shop_status_initialization(shop)
            SalesUnitProductSelection.objects.bulk_create(product_enter_list)
            # sending notification for unit admin users for shop creation
            subject = "Shop created"
            message = "New shop assigned you as the unit admin user"
            for user in shop.unit_admin_user.all():
                send_mail(
                    subject, message, EMAIL_HOST_USER, [user.email], fail_silently=True
                )
            try:
                log_user = request.user
                log_type = "shop"
                message = "Shop Created"
                description = f"Shop {shop.unit_name} created with code {shop.unit_code}"
                LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                                description=description)
            except:
                pass

            return redirect("shop_list")
        else:
            errors.append(shop_form.errors)
            # shop creation is in multiple form pages so on error we need to redirect to the error page
            # logic for same
            page = shop_add_error_page_find(shop_form, bank_form)

            context = {
                "errors": errors,
                "shop_form": shop_form,
                "bank_form": bank_form,
                "user_modal_form": user_modal_form,
                "show_modal": "modal_hide",
                "sales_unit_active": "active",
                "page": page
            }
            return render(request, "shop_add.html", context)
    else:
        shop_form = ShopForm()
        bank_form = BankDetailsForm()
        errors = []
    context = {
        "errors": errors,
        "shop_form": shop_form,
        "bank_form": bank_form,
        "user_modal_form": user_modal_form,
        "show_modal": "modal_hide",
        "sales_unit_active": "active",
        "page": 0,
    }
    return render(request, "shop_add.html", context)


# shop delete logic
@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def shop_delete(request, shop_id):
    shop = Shop.objects.filter(pk=shop_id)
    if shop:
        # if shop exist delete the shop
        # if shop delete then its status information should also delete
        # status information and product assign to a shop is in the Sales unit product selection table
        SalesUnitProductSelection.objects.filter(sales_unit=shop_id).delete()
        shop_data = shop.first()
        shop.delete()
        try:
            try:
                # shop delete status change information
                shop_add_status_change.send(sender="Shop Delete", shop_id=shop_data.uuid)
            except Exception as e:
                logging.error(f"Signal Status Updation on Shop Delete Failed: {e}")

            log_user = request.user
            log_type = "shop"
            message = "Shop Deleted"
            description = f"Shop {shop_data.unit_name}  with code {shop_data.unit_code} deleted"
            LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message, description=description)
        except Exception as e:
            # print(e)
            logging.error(f"Shop Deletion Error: {e}")
            pass
    return redirect("shop_list")


def log(log_user, log_type, message, description):
    try:
        LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message, description=description)
    except:
        pass


# category delete logic...

@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def category_delete(request, category_id):
    category = get_object_or_404(ProductCategory, uuid=category_id)
    sub_category = ProductSubCategory.objects.filter(category=category)
    # if sub category exist restrict delete logic and alert user
    if sub_category:
        messages.warning(request, "Couldn't delete subcategories exists")

    elif category:
        category.delete()

        # log details
        log_user = request.user
        log_type = "product"
        message = "Category Deleted"
        description = f"Cagegory {category.category_name}  with code {category.category_code} deleted"
        log(log_user, log_type, message, description)

        messages.success(request, "Category deleted successfully")
    else:
        messages.warning(request, "Something went wrong")
    return redirect("list_category")


def sub_category_delete(request, sub_category_id):
    sub_category = ProductSubCategory.objects.get(uuid=sub_category_id)
    category_id = sub_category.category.uuid
    if sub_category:
        # if sub category exists delete sub category
        sub_category.delete()

        # log details
        try:
            log_user = request.user
            log_type = "product"
            message = "Sub category deleted"
            description = f"Sub category {sub_category.sub_category_name}  with code {sub_category.sub_category_code} deleted for category {sub_category.category.category_name} with code {sub_category.category.category_code}"
            log(log_user, log_type, message, description)
        except:
            pass

    return redirect(f"/adminportal/add-sub-category/{str(category_id)}/")

def send_email_on_status_change(existing_shop_status, shop):
    if existing_shop_status != shop.status:
        for user in shop.unit_admin_user.all():
            subject = "Shop status updated"
            message = f"Your shop {shop.unit_name} changed their status to {shop.status}"
            send_mail(
                subject, message, EMAIL_HOST_USER, [user.email], fail_silently=True
            )

# shop edit
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def shop_edit(request, shop_id=None):

    # fetch shop and bank details
    shop = get_object_or_404(Shop, pk=shop_id)
    existing_shop_status = shop.status
    bank_details = BankDetails.objects.filter(shop=shop).first()
    errors = []
    if request.method == "POST":
        shop_form = ShopForm(request.POST, instance=shop)
        bank_form = BankDetailsForm(request.POST, instance=bank_details)
        # both shop form and bank form valid
        if shop_form.is_valid() and bank_form.is_valid():
            shop = shop_form.save()
            bank_details = bank_form.save(commit=False)
            bank_details.shop = shop
            bank_details.save()
            # if shop status change on editing
            # change the status and send email to unit admin user
            send_email_on_status_change(existing_shop_status, shop)

            # log details
            try:
                log_user = request.user
                log_type = "shop"
                message = "Shop Updated"
                f"Shop {shop.unit_name}  with code {shop.unit_code} deleted"
                description = f"Shop {shop.unit_name}  with code {shop.unit_code} updated"
                log(log_user, log_type, message, description)
            except Exception as e:
                logging.error(f"Shop Updation Error(logging section): {e}")
                # print(e)

                pass
            return redirect("shop_list")
        else:
            errors.append(shop_form.errors)
            errors.append(bank_form.errors)
            page = shop_add_error_page_find(shop_form, bank_form)

            context = {"shop_form": shop_form, "bank_form": bank_form, "sales_unit_active": "active", "page": page}
            return render(request, "shop_edit.html", context)
    else:
        shop_form = ShopForm(instance=shop)
        bank_form = BankDetailsForm(instance=bank_details)
    context = {"shop_form": shop_form, "bank_form": bank_form, "sales_unit_active": "active", "page": 0}
    return render(request, "shop_edit.html", context)

#...............................end shop views .................................................#


#..............................start user views .................................................#


# listing users
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def users(request):
    users = Users.objects.exclude(user_type__exact='Customer').order_by("-created_date")
    paginator = Paginator(users, 10)
    page = request.GET.get("page")
    try:
        users = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        users = paginator.get_page(1)
    except EmptyPage:
        users = paginator.get_page(paginator.num_pages)

    context = {"users": users, "users_active": "active"}
    return render(request, "users.html", context)

# edit users
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def user_edit(request, user_id):
    errors = []
    user = get_object_or_404(Users, pk=user_id)
    if request.method == "POST":
        form = UserEditForm(request.POST, instance=user)
        if form.is_valid():
            form.save()
            return redirect("users")

        else:
            errors.append(form.errors)
    else:
        form = UserEditForm(instance=user)
    context = {"errors": errors, "form": form, "public_users_active": "active", "user_id": user_id}
    return render(request, "edit_users.html", context)


# user adding
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def user_add(request):
    errors = []
    if request.method == "POST":
        user_form = UserForm(request.POST)
        if user_form.is_valid():
            first_name = user_form.cleaned_data["first_name"]
            last_name = user_form.cleaned_data["last_name"]
            email = user_form.cleaned_data["email"]
            user_type = user_form.cleaned_data["user_type"]
            phone_number = user_form.cleaned_data["phone_number"]
            status = user_form.cleaned_data["status"]
            user_obj = Users(
                first_name=first_name,
                last_name=last_name,
                email=email,
                user_type=user_type,
                phone_number=phone_number,
                status=status
            )

            # password creation
            my_password = generate_random_password()
            user_obj.set_password(my_password)
            subject = "Orderpicky Registration Successful "
            message = f"Congratulations, your account has been successfully created. Your password is {my_password}"
            recepient = str(user_obj.email)
            # send email
            try:
                send_mail(
                    subject, message, EMAIL_HOST_USER, [recepient], fail_silently=False
                )
                user_obj.save()
                return redirect("users")
            except:

                errors = [{'email': ['Please enter valid email']}]

                context = {
                    "errors": errors,
                    "user_form": user_form,
                }
                return render(request, "add_user.html", context)

        else:
            errors.append(user_form.errors)
    else:
        user_form = UserForm()

        errors = []

    context = {
        "errors": errors,
        "user_form": user_form,
    }
    return render(request, "add_user.html", context)


# deleting the user
@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def user_delete(request, user_id):
    user = Users.objects.filter(pk=user_id)
    if user:
        user.delete()
    return redirect("users")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def user_add_modal(request):
    errors = []
    shop_form = ShopForm()
    if request.method == "POST":

        user_modal_form = UserModalForm(request.POST)
        # validating form
        if user_modal_form.is_valid():
            user_obj = user_modal_form.save()

            # generating password
            my_password = generate_random_password()
            user_obj.password = my_password

            # sending email with password
            subject = "Orderpicky Registration Successful "
            message = f"Congratulations, your account has been successfully created. Your password is {my_password}"
            recepient = str(user_obj.email)
            send_mail(
                subject, message, EMAIL_HOST_USER, [recepient], fail_silently=True
            )

            # resetting the form
            user_modal_form = UserModalForm()
            context = {
                "show_modal": "modal_hide",
                "shop_form": shop_form,
                "user_modal_form": user_modal_form,
            }
            return render(request, "shop_add.html", context)
        else:
            errors.append(user_modal_form.errors)
    else:
        user_modal_form = UserModalForm()
        errors = []

    context = {
        "shop_form": shop_form,
        "user_modal_form": user_modal_form,
        "errors": errors,
        "show_modal": "modal_show",
    }
    return render(request, "shop_add.html", context)


# logout from dashboard
@login_required
def admin_logout(request):
    logout(request)
    return redirect('/adminportal/login/')

# resetting password logic
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def reset_password(request, user_id):
    errors = []
    user = get_object_or_404(Users, pk=user_id)

    # generating new password and setting it
    my_password = generate_random_password()
    user.set_password(my_password)
    user.save()

    # send the new password to the user
    subject = "Navya Bakers Registration Successful "
    message = f"Congratulations, your account has been successfully created. Your password is {my_password}"
    recipient = str(user.email)
    send_mail(subject, message, EMAIL_HOST_USER, [recipient], fail_silently=False)

    # add a notification with django flash
    messages.add_message(request, messages.SUCCESS,
                         'Password Reset Successfully. You will receive a mail with the password.')
    # ressetting the form
    form = UserEditForm(instance=user)
    context = {"errors": errors, "form": form, "public_users_active": "active", "user_id": user_id}
    return render(request, "edit_users.html", context)



#..............................end user views .................................................#



#..............................start product views .................................................#

# category listing
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def category_list(request):


    # fetch category data
    category_data = ProductCategory.objects.order_by("category_name")
    paginator = Paginator(category_data, 10)
    page = request.GET.get("page")
    try:
        category = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        category = paginator.get_page(1)
    except EmptyPage:
        category = paginator.get_page(paginator.num_pages)
    context = {"category_list": category, "category_active": "active"}
    return render(request, "category_list.html", context)


# editing category data
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def category_edit(request, category_id):
    errors = []
    category = get_object_or_404(ProductCategory, pk=category_id)
    sub_category_list = ProductSubCategory.objects.filter(category=category)

    sub_category_form = ProductSubCategoryForm()
    if request.method == "POST":
        form = CategoryForm(request.POST, request.FILES, instance=category)
        if form.is_valid():
            if request.POST.get('image1', ''):
                form.instance.icon = None
            if request.POST.get('image2', ''):
                form.instance.standard_image = None
            if request.POST.get('image3', ''):
                form.instance.banner_image = None
            category = form.save()
            try:
                # logging the operation
                log_user = request.user
                log_type = "product"
                message = "Category updated"
                description = f"Category {category.category_name}  with code {category.category_code} updated"
                log(log_user, log_type, message, description)

            except:
                pass
            return redirect("list_category")

        else:
            errors.append(form.errors)
            context = {"category_form": form, "subcategory_status": True, "category_id": category.uuid,
                       "sub_category_form": sub_category_form, "subcategory_add": True,
                       "sub_category": list(sub_category_list)}
            return render(request, "category_add.html", context)
    else:
        form = CategoryForm(instance=category)
    context = {"category_form": form, "subcategory_status": True, "category_id": category.uuid,
               "sub_category_form": sub_category_form}
    return render(request, "category_add.html", context)


#creating category record
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def add_category(request, category_id=None):
    errors = []
    if request.method == "POST":
        category_form = CategoryForm(request.POST, request.FILES)
        sub_category_form = ProductSubCategoryForm()
        if category_form.is_valid():
            category = category_form.save()
            # logging operations
            try:
                log_user = request.user
                log_type = "product"
                message = "Category created"
                description = f"Category {category.category_name}  with code {category.category_code} created"
                log(log_user, log_type, message, description)
            except:
                pass
            category_instance = get_object_or_404(ProductCategory, pk=category.uuid)
            category_form = CategoryForm(instance=category_instance)
            context = {"category_form": category_form, "subcategory_status": True, "category_id": category.uuid,
                       "sub_category_form": sub_category_form}
            return render(request, "category_add.html", context)
        else:
            errors.append(category_form.errors)
            context = {
                "errors": errors,
                "category_form": category_form,
                "category_id": ""

            }
            return render(request, "category_add.html", context)
    else:
        category_form = CategoryForm()
        errors = []
    context = {
        "errors": errors,
        "category_form": category_form,
        "subcategory_status": False,
        "category_id": ""
    }
    return render(request, "category_add.html", context)


# add subcategory from the same category adding screen
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def add_sub_category(request, category_id):
    errors = []
    # fetch the category
    category = get_object_or_404(ProductCategory, uuid=category_id)
    if request.method == "POST":
        # creating the sub category
        sub_category_form = ProductSubCategoryForm(request.POST)

        # fetch the subcategory associated with the category for listing it in the category listing screen

        sub_category_list = ProductSubCategory.objects.filter(category=category)
        category_form = CategoryForm(instance=category)
        if sub_category_form.is_valid():
            sub_category = sub_category_form.save(commit=False)
            sub_category.category = category
            sub_category.save()

            # logging operation
            try:
                log_user = request.user
                log_type = "product"
                message = "Sub category created"
                description = f"Sub category {sub_category.sub_category_name}  with code {sub_category.sub_category_code} created for category {sub_category.category.category_name} with code {sub_category.category.category_code}"
                log(log_user, log_type, message, description)
            except:
                pass
            sub_category_form = ProductSubCategoryForm()

            context = {"sub_category_form": sub_category_form, "subcategory_status": True,
                       "category_form": category_form, "category_id": category_id, "subcategory_add": True,
                       "sub_category": list(sub_category_list)}
            return render(request, "category_add.html", context)
        else:

            # showing the error in the same viewing page
            errors.append(sub_category_form.errors)
            if sub_category_list:
                context = {
                    "errors": errors,
                    "sub_category_form": sub_category_form,
                    "subcategory_status": True, "category_form": category_form, "category_id": category_id,
                    "subcategory_add": True, "sub_category": list(sub_category_list)

                }
            else:
                context = {
                    "errors": errors,
                    "sub_category_form": sub_category_form,
                    "subcategory_status": True, "category_form": category_form, "category_id": category_id

                }

            return render(request, "category_add.html", context)
    else:
        # viewing the category data
        category_form = CategoryForm(instance=category)
        sub_category_form = ProductSubCategoryForm()
        sub_category_list = ProductSubCategory.objects.filter(category=category)
        errors = []
        context = {"sub_category_form": sub_category_form, "subcategory_status": True, "category_form": category_form,
                   "category_id": category_id, "subcategory_add": True, "sub_category": list(sub_category_list)}

    return render(request, "category_add.html", context)


# editing created sub category data
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def edit_sub_category_row(request, sub_category_id):
    # on clicking on the edit button in sub category list
    # new screen opens up
    # we can edit details from there
    sub_category = get_object_or_404(ProductSubCategory, uuid=sub_category_id)
    category_id = sub_category.category.uuid
    errors = []
    if request.method == "POST":
        form = ProductSubCategoryForm(request.POST, request.FILES, instance=sub_category)
        if form.is_valid():
            form.save()
            try:
                # logging operation
                log_user = request.user
                log_type = "product"
                message = "Sub category updated"
                description = f"Sub category {sub_category.sub_category_name}  with code {sub_category.sub_category_code} updated for category {sub_category.category.category_name} with code {sub_category.category.category_code}"
                log(log_user, log_type, message, description)
            except:
                pass
            return redirect(f"/adminportal/add-sub-category/{str(category_id)}/")

        else:
            errors.append(form.errors)
    else:
        form = ProductSubCategoryForm(instance=sub_category)
    context = {"errors": errors, "sub_category_form": form, "public_users_active": "active",
               "sub_category_id": sub_category_id, "category_id": category_id}
    return render(request, "edit_sub_category.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def master_product_add(request):
    """
    In the products listing on side bar. we have master product listing section.
    On clicking the master product, master product listing will open.
    On the listing page we have a add button.
    We can add an new master product by clicking this button.
    :param request:
    :return:
    """
    errors = []
    sku_active = False
    details_active = True
    product = None

    if request.method == "POST":
        product_id = request.POST.get('product_id')  # get product id
        # if product id exists update the product.
        if product_id:
            # Update existing product
            product = get_object_or_404(Products, pk=product_id)
            product_form = ProductForm(request.POST, request.FILES, instance=product)
        else:
            # Create new product
            product_form = ProductForm(request.POST, request.FILES)
        if product_form.is_valid():
            product = product_form.save()

            # Handle image uploads
            images = request.FILES.getlist('images')
            for image in images:
                ProductImage.objects.create(product=product, image=image)

            # Handle video uploads
            videos = request.FILES.getlist('videos')
            for video in videos:
                ProductVideo.objects.create(product=product, video=video)

            # Change active tabs
            sku_active = True
            details_active = False

            # logging operation
            try:
                log_user = request.user
                log_type = "product"
                message = "Master Product Added"
                description = f"Product {product.item_name}  with code {product.item_code} added for category {product.item_category.category_name} ({product.item_category.category_code}) and sub category {product.item_sub_category.sub_category_name} ({product.item_sub_category.sub_category_code}) "
                log(log_user, log_type, message, description)
            except:
                pass

            return JsonResponse({
                'success': True,
                'sku_active': sku_active,
                'details_active': details_active,
                'product': {
                    'id': product.id
                }
            })
        else:
            errors.append(product_form.errors)
            return JsonResponse({'success': False, 'errors': product_form.errors})
    else:
        product_form = ProductForm()

    context = {
        "errors": errors,
        "product_form": product_form,
        "category_active": "active",
        "sku_active": sku_active,
        "details_active": details_active,
        "product": product
    }

    return render(request, "product_add.html", context)


# adding the sku
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def add_sku(request, product_id):
    """
    Sku is different sub category of product.
    for eg: if we consider a biscuit. Same biscuit have 50g, 60g packet etc......
    We can add sku from submitting a modal from from master product create and edit form
    in master product listing

    :param request:
    :param product_id:
    :return:
    """
    # we will add a sku to a particular product

    # fetch product details
    product = get_object_or_404(Products, pk=product_id)

    if request.method == "POST":

        # add the sku

        # create the sku with  "sku_name", "sku_code", "sku_quantity","sku_unit", "sku_mrp",
        #             "sku_expiry_duration", "sku_bulk_qty_limit", "sku_status"
        form = SKUForm(request.POST)
        if form.is_valid():
            sku = form.save(commit=False)
            # check expiry date if > 30 check availability
            if sku.sku_expiry_duration > 30 and sku.sku_status == "Visible":
                sku.long_distance_availability = True

            sku.product = product
            sku_data = sku.save()

            # if a sku is created we should assign it to every shop initially
            # for each shop there is a shop admin status
            # 3 status visible, disabled, out of stock
            # for default we mark it as visible
            # a sku status is also there

            for i in Shop.objects.all():
                sales_product, _ = SalesUnitProductSelection.objects.get_or_create(
                    sales_unit=i, sku=sku,
                    defaults={'shop_admin_status': sku.sku_status, 'status': sku.sku_status}
                )

                # from the iteration we will get the shop id
                # since visible we assign the shop id to the category, sub category, sku, product
                # fetch the product sku and category and sub category instance from sku assign the shop
                # print(f"SKU: {sku_data}")
                # print(f"SKU Product: {sku.product}")
                if sku.sku_status == "Visible":
                    sku.product.sales_unit.add(i)  # adding shop to product
                    sku.sales_unit.add(i)  # adding shop to sku
                    sku.product.item_category.sales_unit.add(i)  # adding shop to item_category
                    sku.product.item_sub_category.sales_unit.add(i)  # adding shop to sub category

                if sku.long_distance_availability:
                    Products.objects.filter(id=sku.product.id).update(long_distance_availability=True)
                    ProductCategory.objects.filter(uuid=sku.product.item_category.uuid).update(
                        long_distance_availability=True)
                    ProductSubCategory.objects.filter(uuid=sku.product.item_sub_category.uuid).update(
                        long_distance_availability=True)
                # check if already also
                # not needed duplicate skipped
                sku.save()

            # log the operation for future needs
            try:
                log_user = request.user
                log_type = "product"
                message = "SKU Added"
                description = f"SKU {sku.sku_name}  with code {sku.sku_code} added for product {sku.product.item_name}({sku.product.item_code}) "
                log(log_user, log_type, message, description)
            except:
                pass
            return JsonResponse({
                'status': 'success',
                'sku_name': sku.sku_name,  # Assuming your model has these fields
                'sku_code': sku.sku_code,
                'sku_quantity': sku.sku_quantity,
                'sku_unit': sku.sku_unit,
                'sku_mrp': sku.sku_mrp,
                'sku_id': sku.id
            })
        else:
            return JsonResponse({'status': 'error', 'errors': form.errors})
    return JsonResponse({'status': 'error', 'message': 'Invalid request method'})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def get_skus(request, product_id):
    """
    Used to list the skus in master product add and edit form.
    Master product add : product_add.html,
    Master product edit : product_edit_01.html
    :param request:
    :param product_id:
    :return:
    """
    skus = SKU.objects.filter(product_id=product_id).order_by('created_at')  # Adjust according to your SKU model
    sku_list = [{
        'sku_id': sku.id,  # type: ignore
        'sku_name': sku.sku_name,
        'sku_code': sku.sku_code,
        'sku_quantity': sku.sku_quantity,  # Adjust according to your model
        'sku_unit': sku.sku_unit,  # Adjust according to your model
        'sku_mrp': sku.sku_mrp  # Adjust according to your model
    } for sku in skus]

    return JsonResponse({'status': 'success', 'skus': sku_list})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delete_sku(request):
    """
    Delete Sku function used to delete an sku from product edit and product add section.
    product edit : product_edit_01.html,
    product add : product_add.html
    :param request:
    :return:
    """
    if request.method == "POST":
        # get sku id
        sku_id = request.POST.get('sku_id')
        sku = get_object_or_404(SKU, id=sku_id)  # Adjust according to your SKU model

        # print(f"SKU_TO_DELETE: {sku}")

        sku_product_id  = sku.product.id  # getting product from sku
        # print(f"SKU PRODUCT: {sku.product}")
        sku_category_id = sku.product.item_category.uuid  # getting category from sku
        # print(f"SKU CATEGORY : {sku_category}")
        sku_sub_category_id = sku.product.item_sub_category.uuid  # getting sub category from sku
        # print(f"SKU SUB CATEGORY: {sku_sub_category}")

        sku.delete()

        # product exists looking for availablility
        long_distance_status_check_celery.apply_async(args=[sku_product_id,sku_sub_category_id, sku_category_id ], countdown=5)
        sku_delete_long_distance_celery.apply_async(args=[sku_product_id, sku_sub_category_id, sku_category_id], countdown=5)


        # log information
        try:
            log_user = request.user
            log_type = "product"
            message = "SKU Deleted"
            description = f"SKU {sku.sku_name}  with code {sku.sku_code} deleted for product {sku.product.item_name}({sku.product.item_code}) "
            log(log_user, log_type, message, description)
        except:
            pass
        return JsonResponse({'status': 'success'})
    return JsonResponse({'status': 'error', 'message': 'Invalid request'})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def get_sku(request, sku_id):
    """
    Used to get a sku detail with the sku id.
    """
    try:

        sku = SKU.objects.get(id=sku_id)

        data = {
            'status': 'success',
            'sku': {
                'sku_name': sku.sku_name,
                'sku_code': sku.sku_code,
                'sku_quantity': sku.sku_quantity,
                'sku_unit': sku.sku_unit,
                'sku_mrp': sku.sku_mrp,
                'sku_expiry_duration': sku.sku_expiry_duration,
                'sku_bulk_qty_limit': sku.sku_bulk_qty_limit,
                'same_day_delivery': sku.same_day_delivery,
                'customization_available': sku.customization_available,
                'sku_status': sku.sku_status,
            }
        }
        return JsonResponse(data)
    except SKU.DoesNotExist:
        return JsonResponse({'status': 'error', 'message': 'SKU not found.'})



def sku_update_status_changes(sku_data):
    """
    On sku change we need to update the status to every shop the sku is assigned
    to.
    Based on the sku and shop admin status we are checking the availability of product.
    If shop_admin status is not visible we check for long distance availability based on
    sku status.
    :param sku:
    :param sku_data:
    :return:
    """

    # iterate on each shop
    for i in Shop.objects.all():

        # update or creating  status
        sales_product, _ = SalesUnitProductSelection.objects.get_or_create(
            sales_unit=i, sku=sku_data,
            defaults={'shop_admin_status': sku_data.sku_status, 'status': sku_data.sku_status}
        )

        sales_product.shop_admin_status = sku_data.sku_status
        sales_product.status = sku_data.sku_status
        sales_product.save()
        update_sku_long_distance_status_check_celery.apply_async(args=[sku_data.id], countdown=5)

        # check the current sku status
        if sku_data.sku_status == "Visible":
            for shop_data in Shop.objects.all():
                sales_unit_status_check = SalesUnitProductSelection.objects.filter(sku=sku_data,
                                                                                   sales_unit=shop_data).first()
                if sales_unit_status_check:

                    # check shop admin status

                    if sales_unit_status_check.shop_admin_status == "Visible":

                        # if visible add the shop to the sku.
                        sku_data.product.sales_unit.add(shop_data)  # adding shop to product
                        sku_data.sales_unit.add(shop_data)  # adding shop to sku
                        sku_data.product.item_category.sales_unit.add(shop_data)  # adding shop to item_category
                        sku_data.product.item_sub_category.sales_unit.add(shop_data)  # adding shop to sub category
                    else:
                        sku_data.sales_unit.remove(shop_data)  # removing shop from product

                        shop_admin_status_update.apply_async(args=[sku_data.id], countdown=5)
                else:
                    for shop in Shop.objects.all():
                        sku_data.sales_unit.remove(shop)  # removing shop from product
                    shop_admin_status_update.apply_async(args=[sku_data.id], countdown=5)
        # if sku status not visible
        else:
            for shop_data in Shop.objects.all():
                sku_data.sales_unit.remove(shop_data)  # adding shop to product
            shop_admin_status_update.apply_async(args=[sku_data.id], countdown=5)


# update sku
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def update_sku(request, sku_id):
    """
    Update an sku listed in the master product listing.
    update the sku in edit or add sku page.
    product edit : product_edit_01.html,
    product add : product_add.html

    :param request:
    :param sku_id:
    :return:
    """
    sku = get_object_or_404(SKU, id=sku_id)  # get sku instance

    if request.method == 'POST':
        form = SKUForm(request.POST, instance=sku)  # Bind the form to the SKU instance
        if form.is_valid():
            sku_data = form.save()  # Save the updated SKU

            # if status change on update the sku we need to change the
            # shop status and long distance availability status
            sku_update_status_changes(sku_data)

            try:
                # log operation
                log_user = request.user
                log_type = "product"
                message = "SKU Updated"
                description = f"SKU {sku_data.sku_name}  with code {sku_data.sku_code} updated for product {sku_data.product.item_name}({sku_data.product.item_code}) "
                log(log_user, log_type, message, description)
            except Exception as e:
                # print(e)
                logging.error(f"SKU Updated Error (logging): {e}")

                pass

            return JsonResponse({'status': 'success', 'message': 'SKU updated successfully.'})
        else:
            return JsonResponse({'status': 'error', 'errors': form.errors})

    return JsonResponse({'status': 'error', 'message': 'Invalid request method'})


def update_sku_from_product_list(request, sku_id):
    """
    Sku status update from product listing.
    When we click on the drop down on product listing, sku will list.
    On the sku list we have the option to change the sku status.

    :param request:
    :param sku_id:
    :return:
    """
    sku = get_object_or_404(SKU, id=sku_id)

    if request.method == 'POST':
        status = request.POST.get('sku_status')

        # updating the sku status from here
        if status is not None:
            sku.sku_status = status
            sku.save()

            # if status change on update the sku we need to change the
            # shop status and long distance availability status
            sku_update_status_changes(sku)
            try:
                # log the operation
                log_user = request.user
                log_type = "product"
                message = "SKU Status Updated"
                description = f"SKU {sku.sku_name}  with code {sku.sku_code} updated status to {status} for product {sku.product.item_name}({sku.product.item_code}) "
                log(log_user, log_type, message, description)
            except:
                pass

            return JsonResponse({'status': 'success', 'message': 'SKU status updated successfully.'})
        else:
            return JsonResponse({'status': 'error', 'message': 'No status provided in the request.'})

    return JsonResponse({'status': 'error', 'message': 'Invalid request method. Only POST is allowed.'})



@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def master_product_edit(request, product_id):
    """
        Editing the product from the product listing page.
        In the side bar we have master product section
        On opening the master product we have the option to edit
        the product.
    :param request:
    :param product_id:
    :return:
    """
    product = get_object_or_404(Products, pk=product_id)  # get product using product id
    errors = []
    sku_active = False
    details_active = True

    # for removing the shop list if we change the category and sub category during editing
    existing_category = product.item_category
    existing_sub_category = product.item_sub_category
    to_category = request.POST.get("item_category")
    to_sub_category = request.POST.get("item_sub_category")
    # print(existing_category)
    # print(existing_sub_category)
    # print(to_category)
    # print(to_sub_category)

    if request.method == "POST":
        product_form = ProductForm(request.POST, request.FILES, instance=product)
        if product_form.is_valid():
            product = product_form.save()

            # Handle image uploads
            images = request.FILES.getlist('images')
            for image in images:
                ProductImage.objects.create(product=product, image=image)

            # Handle video uploads
            videos = request.FILES.getlist('videos')
            for video in videos:
                ProductVideo.objects.create(product=product, video=video)

            # Change active tabs
            sku_active = True
            details_active = False

            # before updated

            # after updated

            long_distance_master_product_edit.apply_async(args=[existing_category.uuid,existing_sub_category.uuid,product.id])
            product_availability_shop_status_change_celery.apply_async(args = [existing_sub_category.uuid,existing_category.uuid,to_sub_category, to_category ], countdown=5)

            try:
                log_user = request.user
                log_type = "product"
                message = "Master Product Updated"
                description = f"Product {product.item_name}  with code {product.item_code} updated for category {product.item_category.category_name} ({product.item_category.category_code}) and sub category {product.item_sub_category.sub_category_name} ({product.item_sub_category.sub_category_code}) "
                log(log_user, log_type, message, description)
            except:
                pass

            return JsonResponse({
                'success': True,
                'sku_active': sku_active,
                'details_active': details_active,
                'product': {
                    'id': product.id
                }
            })
        else:
            errors.append(product_form.errors)
            return JsonResponse({'success': False, 'errors': product_form.errors})

    product_form = ProductForm(instance=product)

    context = {
        "errors": errors,
        "product_form": product_form,
        "category_active": "active",
        "sku_active": sku_active,
        "details_active": details_active,
        "product": product,
        "product_id": product_id
    }

    return render(request, "product_edit_01.html", context)

#tomarrow
@login_required
def delete_image(request, image_id):
    """
    deleting product image
    :param request:
    :param image_id:
    :return:
    """
    image = get_object_or_404(ProductImage, id=image_id)
    product_id = image.product.id  # type: ignore
    image.delete()
    # logging the operation
    try:
        log_user = request.user
        log_type = "product"
        message = "Master Product Image deleted"
        description = f"Product {image.product.item_name}  with code {image.product.item_code} deleted its image for category {image.product.item_category.category_name} ({image.product.item_category.category_code}) and sub category {image.product.item_sub_category.sub_category_name} ({image.product.item_sub_category.sub_category_code}) "
        log(log_user, log_type, message, description)
    except:
        pass
    return redirect('edit_master_product', product_id=product_id)


@login_required
def delete_video(request, video_id):
    """
    deleting the product video
    :param request:
    :param video_id:
    :return:
    """
    video = get_object_or_404(ProductVideo, id=video_id)
    product_id = video.product.id  # type: ignore
    video.delete()
    try:
        log_user = request.user
        log_type = "product"
        message = "Master Product Video deleted"
        description = f"Product {video.product.item_name}  with code {video.product.item_code} deleted its video for category {video.product.item_category.category_name} ({video.product.item_category.category_code}) and sub category {video.product.item_sub_category.sub_category_name} ({video.product.item_sub_category.sub_category_code}) "
        log(log_user, log_type, message, description)
    except:
        pass
    return redirect('edit_master_product', product_id=product_id)



def long_distance_master_product_delete(long_dist_sku_sub_category, product_category, product_sub_category):
    """
    long distance product availability check and update the availability
    :param long_dist_sku_sub_category:
    :param product_category:
    :param product_sub_category:
    :return:
    """
    if not long_dist_sku_sub_category:
        ProductSubCategory.objects.filter(uuid=product_sub_category.uuid).update(
            long_distance_availability=False)
    else:

        long_dist_sub_category_flag = False
        for i in long_dist_sku_sub_category:
            long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=i, status="Visible").first()
            if long_dist_sales_check_sub:
                ProductSubCategory.objects.filter(uuid=product_sub_category.uuid).update(
                    long_distance_availability=True)
                long_dist_sub_category_flag = True
                break
        if not long_dist_sub_category_flag:
            ProductSubCategory.objects.filter(uuid=product_sub_category.uuid).update(
                long_distance_availability=False)

    # category exists looking for availablility
    long_dist_sku_category = SKU.objects.filter(product__item_category=product_category,
                                                sku_status="Visible", sku_expiry_duration__gt=30)
    if not long_dist_sku_category:
        ProductCategory.objects.filter(uuid=product_category.uuid).update(
            long_distance_availability=False)
    else:
        long_dist_sku_category_flag = False
        for i in long_dist_sku_category:
            long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=i, status="Visible").first()
            if long_dist_sales_check_cat:
                ProductCategory.objects.filter(uuid=product_category.uuid).update(
                    long_distance_availability=True)
                long_dist_sku_category_flag = True
                break
        if not long_dist_sku_category_flag:
            ProductCategory.objects.filter(uuid=product_category.uuid).update(
                long_distance_availability=False)
    return


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def master_product_delete(request, product_id):
    """
    deleting the master product
    :param request:
    :param product_id:
    :return:
    """
    product = Products.objects.filter(pk=product_id)  # fetch product by id
    product_data = product.first()
    product_category = product_data.item_category
    product_sub_category = product_data.item_sub_category
    if product:
        # if ads found for product don't delete the product
        ads = Ads.objects.filter(Product=product_id)
        if ads:
            messages.add_message(request, messages.WARNING,
                                 "Your Product used in ads! couldn't delete product")
            # messages.error(request, "Your coupon used in ads! couldn't delete coupon")
        else:
            product.delete()
            # on product delete we delete the skus also
            # on removing the sku changes the category and sub category availability

            # sub category exists looking for availablility
            long_dist_sku_sub_category = SKU.objects.filter(product__item_sub_category=product_sub_category,
                                                            sku_status="Visible", sku_expiry_duration__gt=30)

            long_distance_master_product_delete(long_dist_sku_sub_category, product_category, product_sub_category)

            product_availability_shop_status_change_celery.apply_async(args = [product_sub_category.uuid,product_category.uuid ], countdown=5)

            # if we delete the product skus will delete
            # if not skus we check for category and sub category
            # if skus exist for category and subcategory shop will not remove
            # other wise remove shop

            try:
                log_user = request.user
                log_type = "product"
                message = "Master Product deleted"
                description = f"Product {product_data.item_name}  with code {product_data.item_code} deleted for category {product_data.item_category.category_name} ({product_data.item_category.category_code}) and sub category {product_data.item_sub_category.sub_category_name} ({product_data.item_sub_category.sub_category_code}) "
                log(log_user, log_type, message, description)
            except Exception as e:
                # print(e)
                logging.error(f"Master Product Delete Error(logging): {e}")
                pass

    return redirect("list_master_product")


from django.http import JsonResponse




def subcategory_product_list(request):
    """
    this is a function to filter and list the products based on sub category
    we have this drop down menu having the list of sub categories
    if we select one sub category based on that sub category product will list on the below table.
    :param request:
    :return:
    """
    # print("Sub category product list here")
    category_id = request.GET.get('category_id')
    subcategory_id = request.GET.get('subcategory_id')
    shop_id = request.GET.get('shop_id')
    product_type = request.GET.get('product_type')
    # filter the products
    product_list = Products.objects.filter(product_type=product_type).order_by('-created_date')
    if subcategory_id:
        product_list = product_list.filter(item_category=category_id, item_sub_category=subcategory_id)
    else:
        product_list = product_list.filter(item_category=category_id)
    list_data = []
    for i in product_list.filter():
        product_data = {"pk": i.pk, "item_name": i.item_name, "item_code": i.item_code,
                        "category_name": i.item_category.category_name,
                        "sub_category_name": i.item_sub_category.sub_category_name, "sku": []}
        sku_list = []
        for j in i.skus.all():
            sku_data = {"pk": j.pk, "sku_name": j.sku_name, "sku_code": j.sku_code, "sku_quantity": j.sku_quantity,
                        "sku_unit": j.sku_unit, "sku_mrp": j.sku_mrp, "sku_status": j.sku_status}
            try:
                # check whether shop status created if not create one with default status
                sales_product, _ = SalesUnitProductSelection.objects.get_or_create(
                    sales_unit=shop_id, sku=j,
                    defaults={'shop_admin_status': 'Visible', 'status': 'Visible'}
                )
                # add the shop status for easy reference
                if sales_product.shop_admin_status == "Visible":  # remove not included not needed in listing
                    j.sales_unit.add(sales_product.sales_unit)
                    j.product.sales_unit.add(sales_product.sales_unit)
                    j.product.item_category.sales_unit.add(sales_product.sales_unit)
                    j.product.item_sub_category.sales_unit.add(sales_product.sales_unit)
                else:
                    shop_admin_status_update_only_one_shop.apply_async(args=[j.id, sales_product.sales_unit.uuid], countdown=5)


                if sales_product.status == "Visible":
                    if j.sku_expiry_duration > 30:
                        j.long_distance_availability = True
                        j.save()
                        Products.objects.filter(id=j.product.id).update(long_distance_availability=True)
                        ProductCategory.objects.filter(uuid=j.product.item_category.uuid).update(
                            long_distance_availability=True)
                        ProductSubCategory.objects.filter(uuid=j.product.item_sub_category.uuid).update(
                            long_distance_availability=True)
                    else:
                        SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                        # product exists looking for availablility
                        long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                                   sku_status="Visible",
                                                                   sku_expiry_duration__gt=30)
                        if not long_dist_sku_product:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)
                        else:
                            long_dist_product_flag = False
                            for x in long_dist_sku_product:
                                long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=x, status= "Visible").first()
                                if long_dist_sales_check:
                                    Products.objects.filter(id=j.product.id).update(
                                        long_distance_availability=True)
                                    long_dist_product_flag = True
                                    break
                            if not long_dist_product_flag:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=False)

                        # sub category exists looking for availablility
                        long_dist_sku_sub_category = SKU.objects.filter(
                            product__item_sub_category=j.product.item_sub_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_sub_category:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)
                        else:

                            long_dist_sub_category_flag = False
                            for a in long_dist_sku_sub_category:
                                long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=a, status="Visible").first()
                                if long_dist_sales_check_sub:
                                    ProductSubCategory.objects.filter(
                                        uuid=j.product.item_sub_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sub_category_flag = True
                                    break
                            if not long_dist_sub_category_flag:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=False)

                        # category exists looking for availablility
                        long_dist_sku_category = SKU.objects.filter(
                            product__item_category=j.product.item_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_category:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)
                        else:
                            long_dist_sku_category_flag = False
                            for b in long_dist_sku_category:
                                long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=b, status = "Visible").first()
                                if long_dist_sales_check_cat:
                                    ProductCategory.objects.filter(
                                        uuid=j.product.item_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sku_category_flag = True
                                    break
                            if not long_dist_sku_category_flag:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=False)
                else:
                    SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                    # product exists looking for availablility
                    long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                               sku_status="Visible",
                                                               sku_expiry_duration__gt=30)
                    if not long_dist_sku_product:
                        Products.objects.filter(id=j.product.id).update(
                            long_distance_availability=False)
                    else:
                        long_dist_product_flag = False
                        for c in long_dist_sku_product:
                            long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=c, status = "Visible").first()
                            if long_dist_sales_check:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=True)
                                long_dist_product_flag = True
                                break
                        if not long_dist_product_flag:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)

                    # sub category exists looking for availablility
                    long_dist_sku_sub_category = SKU.objects.filter(
                        product__item_sub_category=j.product.item_sub_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_sub_category:
                        ProductSubCategory.objects.filter(
                            uuid=j.product.item_sub_category.uuid).update(
                            long_distance_availability=False)
                    else:

                        long_dist_sub_category_flag = False
                        for d in long_dist_sku_sub_category:
                            long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=d, status = "Visible").first()
                            if long_dist_sales_check_sub:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sub_category_flag = True
                                break
                        if not long_dist_sub_category_flag:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)

                    # category exists looking for availablility
                    long_dist_sku_category = SKU.objects.filter(
                        product__item_category=j.product.item_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_category:
                        ProductCategory.objects.filter(
                            uuid=j.product.item_category.uuid).update(
                            long_distance_availability=False)
                    else:
                        long_dist_sku_category_flag = False
                        for e in long_dist_sku_category:
                            long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=e, status = "Visible").first()
                            if long_dist_sales_check_cat:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sku_category_flag = True
                                break
                        if not long_dist_sku_category_flag:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)

                # assign the status information since it is not available on sku table
                # we list the shop status information on product list page
                # we can change the status also
                sku_data["status"] = sales_product.status
                # j.status = sales_product.status
                # j.shop_admin_status = sales_product.shop_admin_status
                sku_data["shop_admin_status"] = sales_product.shop_admin_status
                # sku_list.append(j)
                sku_list.append(sku_data)
            except:
                # j.status = "Disabled"
                sku_data["status"] = "Disabled"
                sku_data["shop_admin_status"] = "Disabled"
                sku_list.append(sku_data)
        # i.sku = sku_list
        product_data["sku"] = sku_list
        # list_data.append(i)
        list_data.append(product_data)
    paginator = Paginator(list_data, 10)

    products = paginator.get_page(1)  # try doing the above statuses after pagination ............
    # print(products)
    pagination = render_to_string('pagination.html', {'product_data': products})
    if request.user.user_type == 'Shop Admin':
        tbody = render_to_string('product_list_page.html', {"product_data": products})
    else:
        tbody = render_to_string('product_page.html', {"product_data": products})

    return JsonResponse({"tbody": tbody, "pagination": pagination}, safe=False)


# get the list of subcategories from a category
# on selecting the category from drop down a list of sub category loaded on drop down subcategory
# could be more efficient with processing limit to the page

# change that...........................................................
def load_subcategories(request):
    """
    get the list of subcategories from a category
    on selecting the category from drop down a list of sub category loaded on drop down subcategory
    could be more efficient with processing limit to the page

    :param request:
    :return:
    """
    category_id = request.GET.get('category_id')
    shop_id = request.GET.get('shop_id')
    product_type = request.GET.get('product_type')
    product_list = Products.objects.filter(product_type=product_type).order_by('-created_date')
    subcategories = []
    if category_id:
        product_list = product_list.filter(item_category=category_id)
        subcategories = ProductSubCategory.objects.filter(category_id=category_id).values('uuid', 'sub_category_name')

    list_data = []
    for i in product_list.filter():
        sku_list = []
        product_data = {"pk": i.pk, "item_name": i.item_name, "item_code": i.item_code,
                        "category_name": i.item_category.category_name,
                        "sub_category_name": i.item_sub_category.sub_category_name, "sku": []}
        for j in i.skus.all():
            sku_data = {"pk": j.pk, "sku_name": j.sku_name, "sku_code": j.sku_code, "sku_quantity": j.sku_quantity,
                        "sku_unit": j.sku_unit, "sku_mrp": j.sku_mrp, "sku_status": j.sku_status}
            try:
                sales_product, _ = SalesUnitProductSelection.objects.get_or_create(
                    sales_unit=shop_id, sku=j,
                    defaults={'shop_admin_status': 'Visible', 'status': 'Visible'}
                )

                if sales_product.shop_admin_status == "Visible":  # remove not included not needed in listing
                    j.sales_unit.add(sales_product.sales_unit)
                    j.product.sales_unit.add(sales_product.sales_unit)
                    j.product.item_category.sales_unit.add (sales_product.sales_unit)
                    j.product.item_sub_category.sales_unit.add(sales_product.sales_unit)
                else:

                    shop_admin_status_update_only_one_shop.apply_async(args=[j.id, sales_product.sales_unit.uuid], countdown=5)

                if sales_product.status == "Visible":
                    if j.sku_expiry_duration > 30:
                        SKU.objects.filter(id=j.id).update(long_distance_availability=True)
                        Products.objects.filter(id=j.product.id).update(long_distance_availability=True)
                        ProductCategory.objects.filter(uuid=j.product.item_category.uuid).update(
                            long_distance_availability=True)
                        ProductSubCategory.objects.filter(uuid=j.product.item_sub_category.uuid).update(
                            long_distance_availability=True)
                    else:
                        SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                        # product exists looking for availablility
                        long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                                   sku_status="Visible",
                                                                   sku_expiry_duration__gt=30)
                        if not long_dist_sku_product:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)
                        else:
                            long_dist_product_flag = False
                            for r in long_dist_sku_product:
                                long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=r, status = "Visible").first()
                                if long_dist_sales_check:
                                    Products.objects.filter(id=j.product.id).update(
                                        long_distance_availability=True)
                                    long_dist_product_flag = True
                                    break
                            if not long_dist_product_flag:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=False)

                        # sub category exists looking for availablility
                        long_dist_sku_sub_category = SKU.objects.filter(
                            product__item_sub_category=j.product.item_sub_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_sub_category:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)
                        else:

                            long_dist_sub_category_flag = False
                            for n in long_dist_sku_sub_category:
                                long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=n, status = "Visible").first()
                                if long_dist_sales_check_sub:
                                    ProductSubCategory.objects.filter(
                                        uuid=j.product.item_sub_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sub_category_flag = True
                                    break
                            if not long_dist_sub_category_flag:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=False)

                        # category exists looking for availablility
                        long_dist_sku_category = SKU.objects.filter(
                            product__item_category=j.product.item_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_category:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)
                        else:
                            long_dist_sku_category_flag = False
                            for l in long_dist_sku_category:
                                long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=l, status = "Visible").first()
                                if long_dist_sales_check_cat:
                                    ProductCategory.objects.filter(
                                        uuid=j.product.item_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sku_category_flag = True
                                    break
                            if not long_dist_sku_category_flag:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=False)
                else:
                    SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                    # product exists looking for availablility
                    long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                               sku_status="Visible",
                                                               sku_expiry_duration__gt=30)
                    if not long_dist_sku_product:
                        Products.objects.filter(id=j.product.id).update(
                            long_distance_availability=False)
                    else:
                        long_dist_product_flag = False
                        for g in long_dist_sku_product:
                            long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=g, status = "Visible").first()
                            if long_dist_sales_check:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=True)
                                long_dist_product_flag = True
                                break
                        if not long_dist_product_flag:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)

                    # sub category exists looking for availablility
                    long_dist_sku_sub_category = SKU.objects.filter(
                        product__item_sub_category=j.product.item_sub_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_sub_category:
                        ProductSubCategory.objects.filter(
                            uuid=j.product.item_sub_category.uuid).update(
                            long_distance_availability=False)
                    else:

                        long_dist_sub_category_flag = False
                        for o in long_dist_sku_sub_category:
                            long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=o, status = "Visible").first()
                            if long_dist_sales_check_sub:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sub_category_flag = True
                                break
                        if not long_dist_sub_category_flag:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)

                    # category exists looking for availablility
                    long_dist_sku_category = SKU.objects.filter(
                        product__item_category=j.product.item_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_category:
                        ProductCategory.objects.filter(
                            uuid=j.product.item_category.uuid).update(
                            long_distance_availability=False)
                    else:
                        long_dist_sku_category_flag = False
                        for w in long_dist_sku_category:
                            long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=w, status = "Visible").first()
                            if long_dist_sales_check_cat:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sku_category_flag = True
                                break
                        if not long_dist_sku_category_flag:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)

                # j.status = sales_product.status
                sku_data["status"] = sales_product.status
                # j.shop_admin_status = sales_product.shop_admin_status
                sku_data["shop_admin_status"] = sales_product.shop_admin_status
                sku_list.append(j)
            except:
                # j.status = "Disabled"
                sku_data["status"] = "Disabled"
                sku_data["shop_admin_status"] = "Disabled"
                sku_list.append(sku_data)
        # i.sku = sku_list
        product_data["sku"] = sku_list
        # list_data.append(i)
        list_data.append(product_data)
    paginator = Paginator(list_data, 10)
    products = paginator.get_page(1)
    pagination = render_to_string('pagination.html', {'product_data': products})
    if request.user.user_type == 'Shop Admin':
        tbody = render_to_string('product_list_page.html', {"product_data": products})
    else:
        tbody = render_to_string('product_page.html', {"product_data": products})

    if subcategories:
        return JsonResponse({"subcategories": list(subcategories), "tbody": tbody, "pagination": pagination},
                            safe=False)
    else:
        return JsonResponse({"subcategories": [], "tbody": tbody, "pagination": pagination}, safe=False)

    list_data = []
    # for i in list(subcategories):
    #     data = {}
    #     data["id"] = i.uuid
    #     data["name"] = i.sub_category_name
    #     list_data.append(data)


def load_subcategories_product(request):
    category_id = request.GET.get('category_id')
    if category_id:
        subcategories = ProductSubCategory.objects.filter(category_id=category_id).values('uuid', 'sub_category_name')
        return JsonResponse(list(subcategories), safe=False)
    else:
        return JsonResponse([], safe=False)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def master_product_list(request):

    products = Products.objects.order_by("-created_at")
    paginator = Paginator(products, 10)
    page = request.GET.get("page")
    try:
        products = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        products = paginator.get_page(1)
    except EmptyPage:
        products = paginator.get_page(paginator.num_pages)
    context = {"product_data": products, "category_active": "active"}
    return render(request, "product_list.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def master_product_list(request, product_split=False):
    """
    In the product section in side bar. if we click on the master product.
    We will get the list of master products.
    :param request:
    :return:
    """

    # get the filters
    search_query = request.GET.get('search', "")
    product_date = request.GET.get('product_date', "")
    product_status = request.GET.get('product_status', "")

    # fetch the product list
    product_list = Products.objects.filter(product_type="Master Product").order_by('-created_date')


    # apply the filters
    if search_query:
        product_list = Products.objects.filter(
            Q(item_name__icontains=search_query) |
            Q(item_code__icontains=search_query) |
            Q(item_category__category_name__icontains=search_query) |
            Q(item_sub_category__sub_category_name__icontains=search_query)
        ).order_by('-created_date')
    if product_date:
        formatted_date = datetime.strptime(product_date, "%Y-%m-%d")
        product_list = product_list.filter(created_date__date=formatted_date)
    if product_status:
        product_list = product_list.filter(product_status=product_status)

    paginator = Paginator(product_list, 10)

    # get the page number and retrieve the page
    page = request.GET.get('page')

    try:
        products = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        products = paginator.get_page(1)
    except EmptyPage:
        products = paginator.get_page(paginator.num_pages)

    base_url = reverse('list_master_product')
    query_params = request.GET.copy()
    if 'page' in query_params:
        del query_params['page']
    pagination_links = [
        {
            'page_number': page_number,
            'url': f"{base_url}?{query_params.urlencode(safe='/')}&page={page_number}",
        }
        for page_number in products.paginator.page_range
    ]
    context = {
        "product_data": products,
        'search_query': search_query,
        'date': product_date,
        'product_status': product_status,
        'pagination_links': pagination_links,
        "category_active": "active"

    }

    return render(request, "product_list.html", context)



@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin", "Shop Admin"]))
def product_list(request, shop_id=None, product_type="Master Product"):
    """
    get the product list on shop admin product listing.
    we can view the product menu on the side bar.
    :param request:
    :param shop_id:
    :param product_type:
    :return:
    """
    search_query = request.GET.get('search', "")

    # fetch products from the table
    product_list = Products.objects.filter(product_type=product_type).order_by('-created_date')

    # if shop admin we only need the products assigned to the shop
    # get shop id
    if request.user.user_type == 'Shop Admin':
        shop_id = Shop.objects.prefetch_related('unit_admin_user').filter(
            unit_admin_user__uuid=request.user.uuid).first().uuid

    category_list = ProductCategory.objects.all().order_by("-created_at")

    # fetch the shop details

    shop_details = get_object_or_404(Shop, uuid=shop_id)
    shop_name = shop_details.unit_name
    shop_location = shop_details.unit_location

    # filter using search query
    if search_query:
        product_list = Products.objects.filter(product_type=product_type).filter(
            Q(item_name__icontains=search_query) |
            Q(item_code__icontains=search_query) |
            Q(item_category__category_name__icontains=search_query) |
            Q(item_sub_category__sub_category_name__icontains=search_query)
        ).order_by('-created_date')

    category_page = request.GET.get('category_page')

    # filter the products based on category
    if category_page:
        product_list = product_list.filter(item_category=category_page)

    # filter the products using sub category
    sub_category_page = request.GET.get('subcategory_page')
    if sub_category_page:
        product_list = product_list.filter(item_sub_category=sub_category_page)

    # paginator = Paginator(product_list, 10)
    # # print(list_data)
    # page = request.GET.get('page')
    #
    #
    #
    # try:
    #     products = paginator.get_page(page)
    # except (PageNotAnInteger, TypeError):
    #     products = paginator.get_page(1)
    # except EmptyPage:
    #     products = paginator.get_page(paginator.num_pages)

    # shop admin status is not in product table we need to fetch from Sales unit product selection table
    # in the product listing page we have a provision to change the shop admin status for shop admin user
    list_data = []
    for i in product_list:
        product_data = {"pk": i.pk, "item_name": i.item_name, "item_code": i.item_code,
                        "category_name": i.item_category.category_name,
                        "sub_category_name": i.item_sub_category.sub_category_name, "sku": []}
        sku_list = []
        for j in i.skus.all():
            try:
                sku_data = {"pk": j.pk, "sku_name": j.sku_name, "sku_code": j.sku_code, "sku_quantity": j.sku_quantity,
                            "sku_unit": j.sku_unit, "sku_mrp": j.sku_mrp, "sku_status": j.sku_status}
                # sales_product = SalesUnitProductSelection.objects.get(sales_unit=shop_details.uuid, sku=j.id)
                sales_product, _ = SalesUnitProductSelection.objects.get_or_create(
                    sales_unit=shop_details, sku=j,
                    defaults={'shop_admin_status': 'Visible', 'status': 'Visible'}
                )

                if sales_product.shop_admin_status == "Visible":  # remove not included not needed in listing
                    j.sales_unit.add(shop_id)
                    j.product.sales_unit.add(shop_id)
                    j.product.item_category.sales_unit.add(shop_id)
                    j.product.item_sub_category.sales_unit.add(shop_id)
                else:
                    shop_admin_status_update_only_one_shop.apply_async(args=[j.id, sales_product.sales_unit.uuid], countdown=5)


                # print("condition failed")
                if sales_product.status == "Visible":
                    if j.sku_expiry_duration > 30:
                        sku_update = SKU.objects.filter(id=j.id).first()
                        sku_update.long_distance_availability = True
                        sku_update.save()
                        product_update = Products.objects.filter(id=j.product.id).first()
                        product_update.long_distance_availability = True
                        product_update.save()
                        # Products.objects.filter(id=j.product.id).update(long_distance_availability=True)
                        product_category_update = ProductCategory.objects.filter(
                            uuid=j.product.item_category.uuid).first()
                        product_category_update.long_distance_availability = True
                        product_category_update.save()
                        product_sub_category_update = ProductSubCategory.objects.filter(
                            uuid=j.product.item_sub_category.uuid).first()
                        product_sub_category_update.long_distance_availability = True
                        product_sub_category_update.save()
                        # ProductCategory.objects.filter(uuid=j.product.item_category.uuid).update(
                        # long_distance_availability=True)
                        # ProductSubCategory.objects.filter(uuid=j.product.item_sub_category.uuid).update(
                        # long_distance_availability=True)
                    else:
                        SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                        # product exists looking for availablility
                        long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                                   sku_status="Visible",
                                                                   sku_expiry_duration__gt=30)
                        if not long_dist_sku_product:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)
                        else:
                            long_dist_product_flag = False
                            for w in long_dist_sku_product:
                                long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=w, status = "Visible").first()
                                if long_dist_sales_check:
                                    Products.objects.filter(id=j.product.id).update(
                                        long_distance_availability=True)
                                    long_dist_product_flag = True
                                    break
                            if not long_dist_product_flag:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=False)

                        # sub category exists looking for availablility
                        long_dist_sku_sub_category = SKU.objects.filter(
                            product__item_sub_category=j.product.item_sub_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_sub_category:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)
                        else:

                            long_dist_sub_category_flag = False
                            for v in long_dist_sku_sub_category:
                                long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=v, status ="Visible").first()
                                if long_dist_sales_check_sub:
                                    ProductSubCategory.objects.filter(
                                        uuid=j.product.item_sub_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sub_category_flag = True
                                    break
                            if not long_dist_sub_category_flag:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=False)

                        # category exists looking for availablility
                        long_dist_sku_category = SKU.objects.filter(
                            product__item_category=j.product.item_category,
                            sku_status="Visible", sku_expiry_duration__gt=30)
                        if not long_dist_sku_category:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)
                        else:
                            long_dist_sku_category_flag = False
                            for r in long_dist_sku_category:
                                long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=r, status = "Visible").first()
                                if long_dist_sales_check_cat:
                                    ProductCategory.objects.filter(
                                        uuid=j.product.item_category.uuid).update(
                                        long_distance_availability=True)
                                    long_dist_sku_category_flag = True
                                    break
                            if not long_dist_sku_category_flag:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=False)
                else:

                    SKU.objects.filter(id=j.id).update(long_distance_availability=False)
                    # product exists looking for availablility
                    long_dist_sku_product = SKU.objects.filter(product=j.product,
                                                               sku_status="Visible",
                                                               sku_expiry_duration__gt=30)
                    if not long_dist_sku_product:
                        Products.objects.filter(id=j.product.id).update(
                            long_distance_availability=False)
                    else:
                        long_dist_product_flag = False
                        for s in long_dist_sku_product:
                            long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=s, status = "Visible").first()
                            if long_dist_sales_check:
                                Products.objects.filter(id=j.product.id).update(
                                    long_distance_availability=True)
                                long_dist_product_flag = True
                                break
                        if not long_dist_product_flag:
                            Products.objects.filter(id=j.product.id).update(
                                long_distance_availability=False)

                    # sub category exists looking for availablility
                    long_dist_sku_sub_category = SKU.objects.filter(
                        product__item_sub_category=j.product.item_sub_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_sub_category:
                        ProductSubCategory.objects.filter(
                            uuid=j.product.item_sub_category.uuid).update(
                            long_distance_availability=False)
                    else:

                        long_dist_sub_category_flag = False
                        for t in long_dist_sku_sub_category:
                            long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=t, status ="Visible").first()
                            if long_dist_sales_check_sub:
                                ProductSubCategory.objects.filter(
                                    uuid=j.product.item_sub_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sub_category_flag = True
                                break
                        if not long_dist_sub_category_flag:
                            ProductSubCategory.objects.filter(
                                uuid=j.product.item_sub_category.uuid).update(
                                long_distance_availability=False)

                    # category exists looking for availablility
                    long_dist_sku_category = SKU.objects.filter(
                        product__item_category=j.product.item_category,
                        sku_status="Visible", sku_expiry_duration__gt=30)
                    if not long_dist_sku_category:
                        ProductCategory.objects.filter(
                            uuid=j.product.item_category.uuid).update(
                            long_distance_availability=False)
                    else:
                        long_dist_sku_category_flag = False
                        for q in long_dist_sku_category:
                            long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=q, status = "Visible").first()
                            if long_dist_sales_check_cat:
                                ProductCategory.objects.filter(
                                    uuid=j.product.item_category.uuid).update(
                                    long_distance_availability=True)
                                long_dist_sku_category_flag = True
                                break
                        if not long_dist_sku_category_flag:
                            ProductCategory.objects.filter(
                                uuid=j.product.item_category.uuid).update(
                                long_distance_availability=False)

                # j.status = sales_product.status
                sku_data["status"] = sales_product.status
                # j.shop_admin_status = sales_product.shop_admin_status
                sku_data["shop_admin_status"] = sales_product.shop_admin_status
                sku_list.append(sku_data)

                # list_data.append(i)
            except Exception as e:
                logging.error(f"SKU Status Issue(display as disabled): {e}")
                # print(e)
                # print("Exception ")
                # j.status = "Disabled"
                sku_data["status"] = "Disabled"
                sku_data["shop_admin_status"] = "Disabled"
                # sku_list.append(j)
                sku_list.append(sku_data)
        # print(sku_list)
        # i.sku = sku_list
        product_data["sku"] = sku_list
        list_data.append(product_data)
        # print(list_data)

    paginator = Paginator(list_data, 10)
    # print(list_data)
    page = request.GET.get('page')

    try:
        products = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        products = paginator.get_page(1)
    except EmptyPage:
        products = paginator.get_page(paginator.num_pages)

    base_url = reverse('list_shop_product', kwargs={'shop_id': shop_id, 'product_type': 'Master Product'})
    query_params = request.GET.copy()
    if 'page' in query_params:
        del query_params['page']
    pagination_links = [
        {
            'page_number': page_number,
            'url': f"{base_url}?{query_params.urlencode(safe='/')}&page={page_number}",
        }
        for page_number in products.paginator.page_range
    ]

    context = {
        "product_data": products,
        'search_query': search_query,
        'pagination_links': pagination_links,
        'product_type': product_type,
        "sales_unit_active": "active",
        "shop_id": shop_id,
        "shop_name": shop_name,
        "shop_location": shop_location,
        "category_list": category_list

    }
    if request.user.user_type == 'Shop Admin':
        if request.headers.get('x-requested-with') == 'XMLHttpRequest':
            tbody = render_to_string('product_list_page.html',
                                     {"product_data": products, 'pagination_links': pagination_links})
            pagination = render_to_string('pagination.html', {'product_data': products})
            return JsonResponse({'tbody': tbody, "pagination": pagination})
        return render(request, "product_list_shop_admin.html", context)
    if request.headers.get('x-requested-with') == 'XMLHttpRequest':
        tbody = render_to_string('product_page.html', {"product_data": products, 'pagination_links': pagination_links})
        pagination = render_to_string('pagination.html', {'product_data': products})
        return JsonResponse({'tbody': tbody, "pagination": pagination})
    # print(products)
    # print("product_list_shop.html")
    return render(request, "product_list_shop.html", context)


# it is the status which can be changed with the admin dashboard not shop admin
# we have the status details in the salesproductselectiontable
# in that table we have a field status
# with this view we can change that status
# with this change of status shop admin status also change to the new status.

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin", "Shop Admin"]))
def update_product_sales_status(request):
    """
    it is the status which can be changed with the admin dashboard not shop admin
    we have the status details in the sales product selection table
    in that table we have a field status
    with this view we can change that status
    with this change of status shop admin status also change to the new status.

    :param request:
    :return:
    """
    # print("Logging : Updating Product status on Admin")
    if request.method == 'POST':
        sku_id = request.POST.get('sku_id')
        shop_id = request.POST.get('shop_id')
        new_status = request.POST.get('status')

        try:
            sales_unit_product = SalesUnitProductSelection.objects.get(sales_unit=shop_id, sku=sku_id)
        except SalesUnitProductSelection.DoesNotExist:
            # if not found record
            return JsonResponse({'success': False, 'error': 'Operation not allowed.'})
        try:
            # assigning the new status
            sales_unit_product.status = new_status
            sales_unit_product.shop_admin_status = new_status
            sales_unit_product.save()

            # if visible add the shop availablility
            if new_status == "Visible" and sales_unit_product.sku.sku_expiry_duration > 30:
                SKU.objects.filter(id=sku_id).update(long_distance_availability=True)
                Products.objects.filter(id=sales_unit_product.sku.product.id).update(long_distance_availability=True)
                ProductCategory.objects.filter(uuid=sales_unit_product.sku.product.item_category.uuid).update(
                    long_distance_availability=True)
                ProductSubCategory.objects.filter(uuid=sales_unit_product.sku.product.item_sub_category.uuid).update(
                    long_distance_availability=True)
            else:
                # print("Logging: Status updating to not visible statuses")
                SKU.objects.filter(id=sku_id).update(long_distance_availability=False)
                # product exists looking for availablility
                long_dist_sku_product = SKU.objects.filter(product=sales_unit_product.sku.product, sku_status="Visible",
                                                           sku_expiry_duration__gt=30)
                if not long_dist_sku_product:
                    Products.objects.filter(id=sales_unit_product.sku.product.id).update(
                        long_distance_availability=False)
                else:
                    long_dist_product_flag = False
                    for i in long_dist_sku_product:
                        long_dist_sales_check = SalesUnitProductSelection.objects.filter(sku=i, status = "Visible").first()
                        if long_dist_sales_check:
                            Products.objects.filter(id=sales_unit_product.sku.product.id).update(
                                long_distance_availability=True)
                            long_dist_product_flag = True
                            break
                    if not long_dist_product_flag:
                        Products.objects.filter(id=sales_unit_product.sku.product.id).update(
                            long_distance_availability=False)

                # sub category exists looking for availablility
                long_dist_sku_sub_category = SKU.objects.filter(
                    product__item_sub_category=sales_unit_product.sku.product.item_sub_category,
                    sku_status="Visible", sku_expiry_duration__gt=30)
                if not long_dist_sku_sub_category:
                    ProductSubCategory.objects.filter(
                        uuid=sales_unit_product.sku.product.item_sub_category.uuid).update(
                        long_distance_availability=False)
                else:

                    long_dist_sub_category_flag = False
                    for i in long_dist_sku_sub_category:
                        long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=i, status = "Visible").first()
                        if long_dist_sales_check_sub:
                            ProductSubCategory.objects.filter(
                                uuid=sales_unit_product.sku.product.item_sub_category.uuid).update(
                                long_distance_availability=True)
                            long_dist_sub_category_flag = True
                            break
                    if not long_dist_sub_category_flag:
                        ProductSubCategory.objects.filter(
                            uuid=sales_unit_product.sku.product.item_sub_category.uuid).update(
                            long_distance_availability=False)

                # category exists looking for availablility
                long_dist_sku_category = SKU.objects.filter(
                    product__item_category=sales_unit_product.sku.product.item_category,
                    sku_status="Visible", sku_expiry_duration__gt=30)
                if not long_dist_sku_category:
                    ProductCategory.objects.filter(uuid=sales_unit_product.sku.product.item_category.uuid).update(
                        long_distance_availability=False)
                else:
                    long_dist_sku_category_flag = False
                    for i in long_dist_sku_category:
                        long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=i, status = "Visible").first()
                        if long_dist_sales_check_cat:
                            ProductCategory.objects.filter(
                                uuid=sales_unit_product.sku.product.item_category.uuid).update(
                                long_distance_availability=True)
                            long_dist_sku_category_flag = True
                            break
                    if not long_dist_sku_category_flag:
                        ProductCategory.objects.filter(uuid=sales_unit_product.sku.product.item_category.uuid).update(
                            long_distance_availability=False)

            if new_status == "Visible":
                sales_unit_product.sku.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.item_category.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.item_sub_category.sales_unit.add(sales_unit_product.sales_unit)

                # else remove shop availablility
            else:

                sales_unit_product.sku.sales_unit.remove(sales_unit_product.sales_unit)

                # check sku exists for the product if any don't remove the shop from product
                shop_admin_status_update_only_one_shop.apply_async(args=[sales_unit_product.sku.id, sales_unit_product.sales_unit.uuid], countdown=5)


            try:
                log_user = request.user
                log_type = "product"
                message = f"Product Status Updated for Shop {sales_unit_product.sales_unit.unit_name}({sales_unit_product.sales_unit.unit_code})"
                description = f"SKU {sales_unit_product.sku.sku_name}  with code {sales_unit_product.sku.sku_code} updated its status to {new_status} for product {sales_unit_product.sku.product.item_name} ({sales_unit_product.sku.product.item_code} "
                LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                                description=description)
            except:
                pass
            return JsonResponse({'status': 'success'})
        except:
            return JsonResponse({'error': 'Invalid request.'})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def category_based_update_status(request, shop_id):
    """

    :param request:
    :param shop_id:
    :return:
    """
    category_id = request.POST.get('category')
    sub_category_id = request.POST.get('subcategory')
    new_status = request.POST.get('status_category_select')
    shop = Shop.objects.filter(uuid=shop_id).first()
    product_list = Products.objects.order_by('-created_date')
    if category_id:
        product_list = product_list.filter(item_category__uuid=category_id)
    if sub_category_id:
        product_list = product_list.filter(item_sub_category__uuid=sub_category_id)
    product_selection = SalesUnitProductSelection.objects.filter(sales_unit__uuid=shop_id)
    update_list = []
    show_list = []
    for i in product_list:
        product_data = {"pk": i.pk, "item_name": i.item_name, "item_code": i.item_code,
                        "category_name": i.item_category.category_name,
                        "sub_category_name": i.item_sub_category.sub_category_name, "sku": []}
        sku_list = []
        for j in i.skus.all():
            sku_data = {"pk": j.pk, "sku_name": j.sku_name, "sku_code": j.sku_code, "sku_quantity": j.sku_quantity,
                        "sku_unit": j.sku_unit, "sku_mrp": j.sku_mrp, "sku_status": j.sku_status}
            if j.sku_status == "Visible":
                check = product_selection.filter(sku=j).first()
                check.status = new_status
                check.shop_admin_status = new_status
                # check.save()

                sku_data["status"] = new_status
                sku_data["shop_admin_status"] = new_status
                # j.status = new_status
                # j.shop_admin_status = new_status
                update_list.append(check)
                # sku_list.append(j)
                sku_list.append(sku_data)
                if new_status == "Visible":
                    j.sales_unit.add(shop)
                    if j.sku_expiry_duration > 30:
                        j.long_distance_availability = True
                    else:
                        j.long_distance_availability = False
                else:
                    j.sales_unit.remove(shop)
                    j.long_distance_availability = False

            else:
                check = product_selection.filter(sku=j).first()
                # j.status = check.status
                sku_data["status"] = check.status
                if sku_data["status"] == "Visible" and j.sku_expiry_duration > 30:
                    j.long_distance_availability = True
                else:
                    j.long_distance_availability = False

                # j.shop_admin_status = check.shop_admin_status
                sku_data["shop_admin_status"] = check.shop_admin_status
                update_list.append(check)
                # sku_list.append(j)
                sku_list.append(sku_data)
            j.save()
        # i.sku = sku_list
        product_data["sku"] = sku_list
        # show_list.append(i)
        show_list.append(product_data)

    SalesUnitProductSelection.objects.bulk_update(update_list, ['status', 'shop_admin_status'])

    # check sku exists for the product if any don't remove the shop from product

    # print(f"SKU EXIST FOR PRODUCT AFTER DELETION: {sku_data}")

    # check sku exists for the sub category if any don't remove the shop from sub category

    # print(f"SKU EXIST FOR SUB CATEGORY AFTER DELETION: {sku_sub_category_data}")

    # check sku exists for the category if any don't remove the shop from sub category

    if category_id:

        sku_category_data = SKU.objects.filter(product__item_category__uuid=category_id,
                                               sku_status="Visible")

        long_dist_sku_category = sku_category_data.filter(sku_expiry_duration__gt=30)
        if not long_dist_sku_category:
            ProductCategory.objects.filter(uuid=category_id).update(
                long_distance_availability=False)
        else:
            long_dist_sku_category_flag = False
            for z in long_dist_sku_category:
                long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=z, status = "Visible").first()
                if long_dist_sales_check_cat:
                    ProductCategory.objects.filter(uuid=category_id).update(
                        long_distance_availability=True)
                    long_dist_sku_category_flag = True
                    break
            if not long_dist_sku_category_flag:
                ProductCategory.objects.filter(uuid=category_id).update(
                    long_distance_availability=False)

        if not sku_category_data:
            update_status = ProductCategory.objects.filter(uuid=category_id).first()
            if update_status:
                update_status.sales_unit.remove(shop)
        else:
            category_remove_flag = True
            # print(f"Iterating category skus : {sku_category_data}")
            for l in sku_category_data:
                sku_category_sales = SalesUnitProductSelection.objects.filter(sku=l, sales_unit=shop).first()
                # print(f"category  skus Shop Status for {sku_category_sales}")
                if sku_category_sales:
                    # print("category skus Shop status exists")
                    if sku_category_sales.shop_admin_status == "Visible":
                        # print(f"{sku_category_sales}: Visible")
                        category_remove_flag = False
                        break
            if category_remove_flag:
                # print("No visible status available removed")
                update_status = ProductCategory.objects.filter(uuid=category_id).first()
                if update_status:
                    update_status.sales_unit.remove(shop)
            else:
                update_status = ProductCategory.objects.filter(uuid=category_id).first()
                if update_status:
                    update_status.sales_unit.add(shop)
    else:

        for cat in ProductCategory.objects.all():
            sku_category_data = SKU.objects.filter(product__item_category=cat,
                                                   sku_status="Visible")

            long_dist_sku_category = sku_category_data.filter(sku_expiry_duration__gt=30)
            if not long_dist_sku_category:
                cat.long_distance_availability = False
                cat.save()
            else:
                long_dist_sku_category_flag = False
                for y in long_dist_sku_category:
                    long_dist_sales_check_cat = SalesUnitProductSelection.objects.filter(sku=y, status = "Visible").first()
                    if long_dist_sales_check_cat:
                        cat.long_distance_availability = True
                        cat.save()
                        long_dist_sku_category_flag = True
                        break
                if not long_dist_sku_category_flag:
                    cat.long_distance_availability = False
                    cat.save()

            if not sku_category_data:
                cat.sales_unit.remove(shop)
            else:
                category_remove_flag = True
                # print(f"Iterating category skus : {sku_category_data}")
                for t in sku_category_data:
                    sku_category_sales = SalesUnitProductSelection.objects.filter(sku=t, sales_unit=shop).first()
                    # print(f"category  skus Shop Status for {sku_category_sales}")
                    if sku_category_sales:
                        # print("category skus Shop status exists")
                        if sku_category_sales.shop_admin_status == "Visible":
                            # print(f"{sku_category_sales}: Visible")
                            category_remove_flag = False
                            break
                if category_remove_flag:
                    # print("No visible status available removed")
                    cat.sales_unit.remove(shop)
                else:
                    cat.sales_unit.add(shop)

    if sub_category_id:
        sku_sub_category_data = SKU.objects.filter(
            product__item_sub_category__uuid=sub_category_id,
            sku_status="Visible")

        long_dist_sku_sub_category = sku_sub_category_data.filter(sku_expiry_duration__gt=30)
        if not long_dist_sku_sub_category:
            ProductSubCategory.objects.filter(uuid=sub_category_id).update(
                long_distance_availability=False)
        else:

            long_dist_sub_category_flag = False
            for u in long_dist_sku_sub_category:
                long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=u, status = "Visible").first()
                if long_dist_sales_check_sub:
                    ProductSubCategory.objects.filter(
                        uuid=sub_category_id).update(
                        long_distance_availability=True)
                    long_dist_sub_category_flag = True
                    break
            if not long_dist_sub_category_flag:
                ProductSubCategory.objects.filter(uuid=sub_category_id).update(
                    long_distance_availability=False)

        # remove shop from the sub category list
        if not sku_sub_category_data:
            update_status = ProductSubCategory.objects.filter(uuid=sub_category_id).first()
            if update_status:
                update_status.sales_unit.remove(shop)
        else:
            sub_category_remove_flag = True
            # print(f"Iterating on Sub category skus : {sku_sub_category_data}")
            for v in sku_sub_category_data:
                sku_sub_category_sales = SalesUnitProductSelection.objects.filter(sku=v,
                                                                                  sales_unit=shop).first()
                # print(f"sub category  skus Shop Status for {sku_sub_category_sales}")
                if sku_sub_category_sales:
                    # print("sub category skus Shop status exists")
                    if sku_sub_category_sales.shop_admin_status == "Visible":
                        # print(f"{sku_sub_category_sales}: Visible")
                        sub_category_remove_flag = False
                        break
            if sub_category_remove_flag:
                # print("No visible status available removed")
                update_status = ProductSubCategory.objects.filter(uuid=sub_category_id).first()
                if update_status:
                    update_status.sales_unit.remove(shop)
            else:
                update_status = ProductSubCategory.objects.filter(uuid=sub_category_id).first()
                if update_status:
                    update_status.sales_unit.add(shop)

    else:
        if category_id:
            for sub_cat in ProductSubCategory.objects.filter(category__uuid=category_id):

                sku_sub_category_data = SKU.objects.filter(product__item_sub_category=sub_cat,
                                                           sku_status="Visible")

                long_dist_sku_sub_category = sku_sub_category_data.filter(sku_expiry_duration__gt=30)
                if not long_dist_sku_sub_category:
                    sub_cat.long_distance_availability = False
                    sub_cat.save()
                else:

                    long_dist_sub_category_flag = False
                    for g in long_dist_sku_sub_category:
                        long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=g).first()
                        if long_dist_sales_check_sub:
                            if long_dist_sales_check_sub.status == "Visible":
                                sub_cat.long_distance_availability = True
                                sub_cat.save()
                                long_dist_sub_category_flag = True
                                break
                    if not long_dist_sub_category_flag:
                        sub_cat.long_distance_availability = False
                        sub_cat.save()

                # remove shop from the sub category list
                if not sku_sub_category_data:
                    sub_cat.sales_unit.remove(shop)
                else:
                    sub_category_remove_flag = True
                    # print(f"Iterating on Sub category skus : {sku_sub_category_data}")
                    for h in sku_sub_category_data:
                        sku_sub_category_sales = SalesUnitProductSelection.objects.filter(sku=h,
                                                                                          sales_unit=shop).first()
                        # print(f"sub category  skus Shop Status for {sku_sub_category_sales}")
                        if sku_sub_category_sales:
                            # print("sub category skus Shop status exists")
                            if sku_sub_category_sales.shop_admin_status == "Visible":
                                # print(f"{sku_sub_category_sales}: Visible")
                                sub_category_remove_flag = False
                                break
                    if sub_category_remove_flag:
                        sub_cat.sales_unit.remove(shop)
                    else:
                        sub_cat.sales_unit.add(shop)
        else:
            for sub_cat in ProductSubCategory.objects.all():
                sku_sub_category_data = SKU.objects.filter(product__item_sub_category=sub_cat,
                                                           sku_status="Visible")
                long_dist_sku_sub_category = sku_sub_category_data.filter(sku_expiry_duration__gt=30)

                if not long_dist_sku_sub_category:
                    sub_cat.long_distance_availability = False
                    sub_cat.save()
                else:

                    long_dist_sub_category_flag = False
                    for o in long_dist_sku_sub_category:
                        long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=o, status = "Visible").first()
                        if long_dist_sales_check_sub:
                            sub_cat.long_distance_availability = True
                            sub_cat.save()
                            long_dist_sub_category_flag = True
                            break
                    if not long_dist_sub_category_flag:
                        sub_cat.long_distance_availability = False
                        sub_cat.save()

                # remove shop from the sub category list
                if not sku_sub_category_data:
                    sub_cat.sales_unit.remove(shop)
                else:
                    sub_category_remove_flag = True
                    # print(f"Iterating on Sub category skus : {sku_sub_category_data}")
                    for p in sku_sub_category_data:
                        sku_sub_category_sales = SalesUnitProductSelection.objects.filter(sku=p,
                                                                                          sales_unit=shop).first()
                        # print(f"sub category  skus Shop Status for {sku_sub_category_sales}")
                        if sku_sub_category_sales:
                            # print("sub category skus Shop status exists")
                            if sku_sub_category_sales.shop_admin_status == "Visible":
                                # print(f"{sku_sub_category_sales}: Visible")
                                sub_category_remove_flag = False
                                break
                    if sub_category_remove_flag:
                        # print("No visible status available removed")

                        sub_cat.sales_unit.remove(shop)
                    else:
                        sub_cat.sales_unit.add(shop)

    # product status checking
    # unique category and sub_category update the availability with the same flow
    sku_category_list = []
    sku_sub_category_list = []
    for product in product_list:

        sku_products = SKU.objects.filter(product=product, sku_status="Visible")
        long_dist_sku_product = sku_products.filter(sku_expiry_duration__gt=30)

        if not long_dist_sku_product:
            product.long_distance_availability = False
            product.save()
        else:

            long_dist_product_flag = False
            for e in long_dist_sku_product:
                long_dist_sales_check_sub = SalesUnitProductSelection.objects.filter(sku=e, status = "Visible").first()
                if long_dist_sales_check_sub:
                    product.long_distance_availability = True
                    product.save()
                    long_dist_product_flag = True
                    break
            if not long_dist_product_flag:
                product.long_distance_availability = False
                product.save()

        if not sku_products:
            product.sales_unit.remove(shop)
        else:
            product_remove_flag = True
            for sku_product in sku_products:
                sku_product_sales = SalesUnitProductSelection.objects.filter(sku=sku_product, sales_unit=shop).first()
                # print(f"category  skus Shop Status for {sku_category_sales}")
                if sku_product_sales:
                    # print("category skus Shop status exists")
                    if sku_product_sales.shop_admin_status == "Visible":
                        # print(f"{sku_category_sales}: Visible")
                        product_remove_flag = False
                        break
            if product_remove_flag:
                product.sales_unit.remove(shop)
            else:
                product.sales_unit.add(shop)

        # remove shop from the category list

    try:
        log_user = request.user
        log_type = "product"
        message = f"Product Status Updated for Category or Sub Category"
        description = f"Bulk Category update"
        LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                        description=description)
    except:
        pass

    paginator = Paginator(show_list, 10)

    products = paginator.get_page(1)
    # print(products)
    # print(show_list)

    if request.headers.get('x-requested-with') == 'XMLHttpRequest':
        tbody = render_to_string('product_page.html', {"product_data": products})
        pagination = render_to_string('pagination.html', {'product_data': products})
        return JsonResponse({'tbody': tbody, "pagination": pagination})
    base_url = reverse('list_shop_product',
                       kwargs={'shop_id': shop_id, 'product_type': 'Master Product', "sales_unit_active": "active"})
    return redirect(base_url)


def update_product_product_admin_status(request):
    if request.method == 'POST':
        sku_id = request.POST.get('sku_id')
        shop_id = request.POST.get('shop_id')
        new_status = request.POST.get('status')
        try:
            sales_unit_product = SalesUnitProductSelection.objects.get(sales_unit=shop_id, sku=sku_id)
        except SalesUnitProductSelection.DoesNotExist:
            return JsonResponse({'success': False, 'error': 'Operation not allowed.'})
        try:
            sales_unit_product.shop_admin_status = new_status
            sales_unit_product.save()
            # if visible add the shop availablility

            if new_status == "Visible":
                sales_unit_product.sku.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.item_category.sales_unit.add(sales_unit_product.sales_unit)
                sales_unit_product.sku.product.item_sub_category.sales_unit.add(sales_unit_product.sales_unit)

                # else remove shop availablility
            else:
                sales_unit_product.sku.sales_unit.remove(sales_unit_product.sales_unit)
                shop_admin_status_update_only_one_shop.apply_async(args=[sales_unit_product.sku.id, sales_unit_product.sales_unit.uuid], countdown=5)


            try:
                log_user = request.user
                log_type = "product"
                message = f"Product Shop Admin Status Updated for Shop {sales_unit_product.sales_unit.unit_name}({sales_unit_product.sales_unit.unit_code})"
                description = f"SKU {sales_unit_product.sku.sku_name}  with code {sales_unit_product.sku.sku_code} updated its status to {new_status} for product {sales_unit_product.sku.product.item_name} ({sales_unit_product.sku.product.item_code} "
                LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                                description=description)
            except:
                pass
            return JsonResponse({'status': 'success'})
        except:
            return JsonResponse({'error': 'Invalid request.'})


@login_required
@user_passes_test(check_user_role(["Shop Admin"]))
def category_based_shop_admin_update_status(request, shop_id):
    category_id = request.POST.get('category')
    sub_category_id = request.POST.get('subcategory')
    new_status = request.POST.get('status_category_select')
    shop = Shop.objects.filter(uuid=shop_id).first()
    product_list = Products.objects.order_by('-created_date')
    if category_id:
        product_list = product_list.filter(item_category__uuid=category_id)
    if sub_category_id:
        product_list = product_list.filter(item_sub_category=sub_category_id)
    update_list = []
    product_selection = SalesUnitProductSelection.objects.filter(sales_unit__uuid=shop_id)
    update_list = []
    show_list = []
    for i in product_list:
        product_data = {"pk": i.pk, "item_name": i.item_name, "item_code": i.item_code,
                        "category_name": i.item_category.category_name,
                        "sub_category_name": i.item_sub_category.sub_category_name, "sku": []}
        sku_list = []
        for j in i.skus.all():

            sku_data = {"pk": j.pk, "sku_name": j.sku_name, "sku_code": j.sku_code, "sku_quantity": j.sku_quantity,
                        "sku_unit": j.sku_unit, "sku_mrp": j.sku_mrp, "sku_status": j.sku_status}
            if j.sku_status == "Visible":
                check = product_selection.filter(sku=j).first()
                if check.status == "Visible":
                    check.shop_admin_status = new_status
                    # j.status = check.status
                    # j.shop_admin_status = new_status
                    sku_data["status"] = check.status

                    sku_data["shop_admin_status"] = new_status
                else:
                    # j.status = check.status
                    # j.shop_admin_status = check.shop_admin_status

                    sku_data["status"] = check.status

                    sku_data["shop_admin_status"] = check.shop_admin_status
                update_list.append(check)
                # sku_list.append(j)
                sku_list.append(sku_data)

            else:
                check = product_selection.filter(sku=j).first()
                # j.status = check.status
                # j.shop_admin_status = check.shop_admin_status
                sku_data["status"] = check.status

                sku_data["shop_admin_status"] = check.shop_admin_status
                update_list.append(check)
                # sku_list.append(j)
                sku_list.append(sku_data)
        # i.sku = sku_list
        product_data["sku"] = sku_list
        # show_list.append(i)
        show_list.append(product_data)

    SalesUnitProductSelection.objects.bulk_update(update_list, ['shop_admin_status'])
    category_based_shop_admin_status_celery.apply_async(args = [shop.uuid, category_id], countdown= 5)
    sub_category_based_shop_admin_status_celery.apply_async(args=[shop.uuid, sub_category_id, category_id], countdown= 5)
    category_product_list.apply_async(args = [shop.uuid, category_id, sub_category_id], countdown =5)

    paginator = Paginator(show_list, 10)

    products = paginator.get_page(1)

    try:
        log_user = request.user
        log_type = "product"
        message = f"Product Status Updated for Category or Sub Category For Shop"
        description = f"Bulk Category update for Shop"
        LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                        description=description)
    except:
        pass

    if request.headers.get('x-requested-with') == 'XMLHttpRequest':
        tbody = render_to_string('product_list_page.html', {"product_data": products})

        pagination = render_to_string('pagination.html', {'product_data': products})
        return JsonResponse({'tbody': tbody, "pagination": pagination})

    base_url = reverse('list_product_shop_admin')
    return redirect(base_url)


@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def sub_category_creation(request, category_id):
    link_1 = request.POST.get('image_link_1', '/media/icons/icon.jpg')
    link_2 = request.POST.get('image_link_2', '/media/standard_images/standard_image.jpg')
    link_3 = request.POST.get('image_link_3', '/media/banners/banner.jpg')

    image_field1 = render_to_string('image_field.html',
                                    {'class_name': 'upload', 'image_reset_button': 'image_reset_button1',
                                     'field_name': 'icon'})
    image_field2 = render_to_string('image_field.html',
                                    {'class_name': 'upload2', 'image_reset_button': 'image_reset_button2',
                                     'field_name': 'standard_image'})
    image_field3 = render_to_string('image_field.html',
                                    {'class_name': 'upload3', 'image_reset_button': 'image_reset_button3',
                                     'field_name': 'banner_image'})

    errors = []
    if request.headers.get('x-requested-with') == 'XMLHttpRequest':
        category = get_object_or_404(ProductCategory, uuid=category_id)
        sub_category_form = ProductSubCategoryForm(request.POST)

        sub_category_list = ProductSubCategory.objects.filter(category=category)
        if sub_category_form.is_valid():
            sub_category = sub_category_form.save(commit=False)
            sub_category.category = category
            sub_category.save()
            try:
                log_user = request.user
                log_type = "product"
                message = "Sub category created"
                description = f"Sub category {sub_category.sub_category_name}  with code {sub_category.sub_category_code} created for category {sub_category.category.category_name} with code {sub_category.category.category_code}"
                LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message,
                                                description=description)
            except:
                pass
            sub_categories = render_to_string('sub_category_delete_list.html', {'sub_category': sub_category_list})
            return JsonResponse({"data": sub_categories, "errors": [], "success": True, 'image_field1': image_field1,
                                 'image_field2': image_field2, 'image_field3': image_field3,
                                 "link_1": link_1, "link_2": link_2, "link_3": link_3})
        else:
            error = {}
            for i, data in sub_category_form.errors.items():
                error[i] = data[0]

            errors.append(sub_category_form.errors)

            if sub_category_list:
                sub_categories = render_to_string('sub_category_delete_list.html', {'sub_category': sub_category_list})
                return JsonResponse(
                    {
                        "data": sub_categories,
                        "errors": error,
                        "success": False,
                        'image_field1': image_field1, 'image_field2': image_field2, 'image_field3': image_field3,
                        "link_1": link_1, "link_2": link_2, "link_3": link_3

                    })
            else:
                return JsonResponse({
                    "data": [],
                    "errors": error,
                    "success": False,
                    'image_field1': image_field1, 'image_field2': image_field2, 'image_field3': image_field3,
                    "link_1": link_1, "link_2": link_2, "link_3": link_3

                })
    else:
        return JsonResponse({
            "data": [],
            "errors": {"sub_category_code": "Something went wrong try again"},
            "success": False,
            'image_field1': image_field1, 'image_field2': image_field2, 'image_field3': image_field3,
            "link_1": link_1, "link_2": link_2, "link_3": link_3
        })


def filter_products(request):
    category_id = request.GET.get('category_id', None)

    # Filter products based on the category selected
    if category_id:
        products = Products.objects.filter(item_category__uuid=category_id)
    else:
        products = Products.objects.all()  # Default all products

    # Prepare the data for JSON response
    product_list = []
    for product in products:
        product_list.append({
            'id': product.id,
            'name': product.name,
            'price': product.price,
            'category': product.category.name
        })

    return JsonResponse({'products': product_list})


# @login_required
# @user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
# def product_list_shop_admin(request, product_type = "Master Product"):
#     search_query = request.GET.get('search', "")
#
#     product_list = Products.objects.filter(product_type =product_type).order_by('-created_date')
#     category_list = ProductCategory.objects.all().order_by("-created_at")
#     shop_details = get_object_or_404(Shop,uuid = shop_id)
#     shop_name = shop_details.unit_name
#     shop_location = shop_details.unit_location
#
#     if search_query:
#         product_list = Products.objects.filter(product_type =product_type).filter(
#             Q(item_name__icontains=search_query) |
#             Q(item_code__icontains=search_query) |
#             Q(item_category__category_name__icontains=search_query)|
#             Q(item_sub_category__sub_category_name__icontains = search_query)
#         ).order_by('-created_date')
#
#     for i in product_list:
#         try:
#             sales_product = SalesUnitProductSelection.objects.get(sales_unit = shop_details.uuid, product= i.id)
#             i.status = sales_product.status
#         except:
#             i.status = "disable"
#     paginator = Paginator(product_list, 10)
#     page = request.GET.get('page')
#
#     try:
#         products = paginator.get_page(page)
#     except (PageNotAnInteger, TypeError):
#         products = paginator.get_page(1)
#     except EmptyPage:
#         products = paginator.get_page(paginator.num_pages)
#
#     base_url = reverse('list_shop_product',kwargs={'shop_id':shop_id, 'product_type':'Master Product'})
#     query_params = request.GET.copy()
#     if 'page' in query_params:
#         del query_params['page']
#     pagination_links = [
#         {
#             'page_number': page_number,
#             'url': f"{base_url}?{query_params.urlencode(safe='/')}&page={page_number}",
#         }
#         for page_number in products.paginator.page_range
#     ]
#     context = {
#         "product_data": products,
#         'search_query': search_query,
#         'pagination_links': pagination_links,
#         'product_type' : product_type,
#         "product_active": "active",
#         "shop_id": shop_id,
#         "shop_name": shop_name,
#         "shop_location":shop_location,
#         "category_list": category_list
#
#     }
#
#     return render(request, "product_list_shop.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def image_field_rendering(request):
    link_1 = request.GET.get('image_link_1', '/media/icons/icon.jpg')
    link_2 = request.GET.get('image_link_2', '/media/standard_images/standard_image.jpg')
    link_3 = request.GET.get('image_link_3', '/media/banners/banner.jpg')

    image_field1 = render_to_string('image_field.html',
                                    {'class_name': 'upload', 'image_reset_button': 'image_reset_button1',
                                     'field_name': 'icon'})
    image_field2 = render_to_string('image_field.html',
                                    {'class_name': 'upload2', 'image_reset_button': 'image_reset_button2',
                                     'field_name': 'standard_image', "link": link_2})
    image_field3 = render_to_string('image_field.html',
                                    {'class_name': 'upload3', 'image_reset_button': 'image_reset_button3',
                                     'field_name': 'banner_image', "link": link_3})
    return JsonResponse(
        {'image_field1': image_field1, 'image_field2': image_field2, 'image_field3': image_field3, "link_1": link_1,
         "link_2": link_2, "link_3": link_3})


@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def tag_list(request):
    """
    In the product section on the side bar. We have
    the product tags. We list the products based on the tags
    on app.
    :param request:
    :return:
    """
    tags = Tags.objects.order_by("-created_at")
    paginator = Paginator(tags, 6)
    page = request.GET.get("page")
    try:
        tags = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        tags = paginator.get_page(1)
    except EmptyPage:
        tags = paginator.get_page(paginator.num_pages)
    context = {"tag_data": tags, "category_active": "active"}
    return render(request, "tag_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def tag_delete(request, tag_id):
    """
    delete a tag listed in product section on side bar.
    :param request:
    :param tag_id:
    :return:
    """
    tags = Tags.objects.filter(pk=tag_id)
    if tags:
        tags.delete()
    return redirect("tag_list")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def tag_edit(request, tag_id):
    """
    If we click on the edit button on tag listing we are
    redirecting to edit form. We can change the tag details
    with the form.
    :param request:
    :param tag_id:
    :return:
    """
    errors = []
    tags = get_object_or_404(Tags, pk=tag_id)
    if request.method == "POST":
        form = TagForm(request.POST, request.FILES, instance=tags)
        if form.is_valid():
            # image validations are in the form

            # if no images we set the value of the field to None
            if request.POST.get('image1', ''):
                form.instance.icon = None
            form.save()
            return redirect("tag_list")

        else:
            errors.append(form.errors)
    else:
        form = TagForm(instance=tags)
    context = {"tag_form": form, "category_active": "active"}
    return render(request, "tag_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def tag_add(request):
    """
    In the tag list window we have a button add tag. on clicking
    the add tag button. we can add a new tag. fields are
    tag_name, icon, products.
    :param request:
    :return:
    """
    errors = []
    if request.method == "POST":
        tag_form = TagForm(request.POST, request.FILES)
        if tag_form.is_valid():
            tags = tag_form.save()
            return redirect("tag_list")
        else:
            errors.append(tag_form.errors)
            context = {
                "errors": errors,
                "tag_form": tag_form,
                "category_active": "active"
            }
            return render(request, "tag_add.html", context)
    else:
        tag_form = TagForm()

    context = {
        "errors": errors,
        "tag_form": tag_form,
        "category_active": "active"

    }
    return render(request, "tag_add.html", context)


@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def dynamic_filter_list(request):
    """In app just like tags we have easy filters also. One of them is dynamic
    filter. for eg: black forest. if we select the black forest we will return the
    products associated with the filter.
    admin listing of dynamic filter.
    """
    filter_list = DynamicFiltering.objects.order_by("-created_at")
    paginator = Paginator(filter_list, 6)
    page = request.GET.get("page")
    try:
        filter_list = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        filter_list = paginator.get_page(1)
    except EmptyPage:
        filter_list = paginator.get_page(paginator.num_pages)
    context = {"filter_data": filter_list, "category_active": "active"}
    return render(request, "dynamic_filter_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def dynamic_filter_delete(request, filter_id):
    """
    Dynamic filter also listed on the product section on side bar.
    for each dynamic filter, There is a delete button on the listing.
    on clicking the delete button. a confirmation dialog box will appear.
    click yes and we could delete the filter.
    :param request:
    :param filter_id:
    :return:
    """
    filter_list = DynamicFiltering.objects.filter(pk=filter_id)
    if filter_list:
        filter_list.delete()
    return redirect("filter_list")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def dynamic_filter_edit(request, filter_id):
    """
    Editing a dynamic filter. for each dynamic filter listed on
    dynamic filter list, we have a edit button. clicking on
    the edit button will open a form. fields are tag name and products.

    :param request:
    :param filter_id:
    :return:
    """
    errors = []
    filter_list = get_object_or_404(DynamicFiltering, pk=filter_id)
    if request.method == "POST":
        form = DynamicFilterForm(request.POST, instance=filter_list)
        if form.is_valid():
            form.save()
            return redirect("filter_list")

        else:
            errors.append(form.errors)
    else:
        form = DynamicFilterForm(instance=filter_list)
    context = {"filter_form": form, "category_active": "active"}
    return render(request, "dynamic_filter_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def dynamic_filter_add(request):
    """
    Adding a dynamic filter.
    On dynamic filter listing page which we can open by clicking
    on the product section on side bar have a add dynamic filter button.
    clicking will open the form enter the products and tag name on the form.
    on submission create a new dynamic filter.

    :param request:
    :return:
    """
    errors = []
    if request.method == "POST":
        filter_form = DynamicFilterForm(request.POST)
        if filter_form.is_valid():
            filter = filter_form.save()
            return redirect("filter_list")
        else:
            errors.append(filter_form.errors)
            context = {
                "errors": errors,
                "filter_form": filter_form,
                "category_active": "active"
            }
            return render(request, "dynamic_filter_add.html", context)
    else:
        filter_form = DynamicFilterForm()

    context = {
        "errors": errors,
        "filter_form": filter_form,
        "category_active": "active"

    }
    return render(request, "dynamic_filter_add.html", context)


@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def special_list(request):
    """
    special list showed on the app. for eg:  Rolls - Chicken Spring Roll,
    special list will list when we click on the special list on product section
    on side bar.
    :param request:
    :return:
    """
    special_product_list = SpecialList.objects.order_by("-created_at")
    paginator = Paginator(special_product_list, 6)
    page = request.GET.get("page")
    try:
        special_product_list = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        special_product_list = paginator.get_page(1)
    except EmptyPage:
        special_product_list = paginator.get_page(paginator.num_pages)
    context = {"special_data": special_product_list, "category_active": "active"}
    return render(request, "special_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def special_list_delete(request, special_list_id):
    """
    Deleting a special list. for each special list listed on
    special listing, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the special list.
    :param request:
    :param special_list_id:
    :return:
    """
    special_product_list = SpecialList.objects.filter(pk=special_list_id)
    if special_product_list:
        special_product_list.delete()
    return redirect("special_list")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def special_list_edit(request, special_list_id):
    """

    Editing a special list. for each special list listed on
    special listing, we have a edit button. clicking on
    the edit button will open a form. fields are special name and icon,
    standard image, banner image, and products.

    :param request:
    :param special_list_id:
    :return:
    """
    errors = []
    special_product_list = get_object_or_404(SpecialList, pk=special_list_id)
    if request.method == "POST":
        form = SpecialListForm(request.POST, request.FILES, instance=special_product_list)

        # validations and cleansing the images on the SpecialListForm
        if form.is_valid():
            if request.POST.get('image1', ''):
                form.instance.icon = None
            if request.POST.get('image2', ''):
                form.instance.standard_image = None
            if request.POST.get('image3', ''):
                form.instance.banner_image = None
            form.save()
            return redirect("special_list")

        else:
            errors.append(form.errors)
    else:
        form = SpecialListForm(instance=special_product_list)
    context = {"special_form": form, "category_active": "active"}
    return render(request, "special_list_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def special_list_add(request):
    """
    Adding a special list.
    On special listing page which we can open by clicking
    on the product section on side bar have a add special list button.
    clicking will open the form enter the special name and icon,
    standard image, banner image, and products.
    on submission create a new special list.
    :param request:
    :return:
    """
    errors = []
    if request.method == "POST":
        form = SpecialListForm(request.POST, request.FILES)
        if form.is_valid():
            special = form.save()
            return redirect("special_list")
        else:
            # error handling
            errors.append(form.errors)
            context = {
                "errors": errors,
                "special_form": form,
                "category_active": "active"
            }
            return render(request, "special_list_add.html", context)
    else:
        form = SpecialListForm()

    context = {
        "errors": errors,
        "special_form": form,
        "category_active": "active"

    }
    return render(request, "special_list_add.html", context)

#.......................................... end product ....................................................

# ............................................. start orders ...............................................#


class SendVerificationEmailView(APIView):
    """
    View to send delivery boy verification email.
    """

    # permission_classes = [IsAuthenticated]

    def post(self, request, delivery_boy):
        """
        On orders on side bar, we have a menu delivery details. if we click on
        delivery details we have the delivery boys listed. we can add new delivery
        boys. for assigning a order, adding alone not help. for that we have to
        verify the delivery boy. we will send the verification sms to the phone number using this view.

        :param request:
        :param delivery_boy:
        :return:
        """
        user = request.user
        delivery_boys = get_object_or_404(DeliveryBoys, pk=delivery_boy)
        if delivery_boys.status == 'verified':
            context = {"message": "Phone  alreay verified .", 'status': 0}
            return render(request, "phone_verification.html", context)

        # Create a token
        token = jwt.encode({'delivery_boy': str(delivery_boys.pk), 'exp': timezone.now() + timedelta(hours=24)},
                           settings.SECRET_KEY, algorithm='HS256')

        # Create verification link
        # verification_link = request.build_absolute_uri(
        #     reverse('verify-phone', args=[token])
        # )
        domain = getattr(settings, "DOMAIN", "http://localhost/")
        verification_link = f"{domain}{reverse('verify-phone', args=[token])}"
        sms_flag = True
        try:
            # sending sms with the twilio
            sms.apply_async(args=[f"click here to verify: {verification_link}", delivery_boys.phone_number],countdown=5)
            context = {"message": "Verification link sent to your phone number.", 'status': 1, "send": 1}
        except Exception as e:
            sms_flag = False


        # Send the email
        email_flag = True
        try:
            send_mail(
                'Verify your email',
                f'Click the link to verify your email: {verification_link}',
                settings.DEFAULT_FROM_EMAIL,
                [delivery_boys.email],
                fail_silently=False,
            )
        except Exception as e:
            email_flag = False

        # we are sending to email and the phone number.

        if sms_flag and email_flag :
            context = {"message": "Verification link sent to your phone number and email. Please check.", 'status': 1, "send": 1}
        elif sms_flag:
            context = {"message": "Verification link sent to your phone number. Please check.", 'status': 1, "send": 1}
        elif email_flag:
            context = {"message": "Verification link sent to your email. Please check.", 'status': 1, "send": 1}
        else:
            context = {"message": "Invalid Phone number or email!", 'status': 0, "send": 1}


        return render(request, "phone_verification.html", context)




class VerifyEmailView(APIView):
    """
    Delivery details menu on Orders section in the side bar will list the delivery boys.
    this view is to verify with the link.
    we send a link to verify the delivery boy on clicking that link, VerifyEmailView will
    verify.
    """
    def get(self, request, token):
        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
            delivery_boys = get_object_or_404(DeliveryBoys, pk=payload['delivery_boy'])

            # Update email verification status
            delivery_boys.status = "verified"
            delivery_boys.save()

            return render(request, 'phone_verification.html', {
                'status': 1,
                'message': "Phone number verified successfully.",
                "send": 0
            })
        except jwt.ExpiredSignatureError:
            return render(request, 'phone_verification.html', {
                'status': 0,
                'message': "Verification link has expired.",
                "send": 0
            })
        except jwt.InvalidTokenError:
            return render(request, 'phone_verification.html', {
                'status': 0,
                'message': "Invalid verification link.",
                "send": 0
            })


@user_passes_test(check_user_role(["Shop Admin", "PU Admin"]))
def delivery_boy_list(request):
    """
    Delivery details menu on Orders section in the side bar will list the delivery boys.
    It is only available for shop admin.
    each shop have their own delivery boys.
    :param request:
    :return:
    """

    # get the shop
    shop_id_verify = Shop.objects.prefetch_related('unit_admin_user').filter(
        unit_admin_user__uuid=request.user.uuid)

    # if shop exists get the shop id
    if shop_id_verify:
        shop_id = shop_id_verify[0]
    else:
        shop_id = ""

    # fetch delivery boy associated with the shop
    delivery_boys = DeliveryBoys.objects.filter(shop=shop_id).order_by("-created_date")
    paginator = Paginator(delivery_boys, 6)
    page = request.GET.get("page")

    try:
        delivery_boys = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        delivery_boys = paginator.get_page(1)
    except EmptyPage:
        delivery_boys = paginator.get_page(paginator.num_pages)
    context = {"delivery_boy_data": delivery_boys, "orders_active": "active"}
    return render(request, "delivery_boy_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Shop Admin"]))
def delivery_boy_delete(request, delivery_boy_id):
    """
    Delivery details menu on Orders section in the side bar will list the delivery boys.
    Deleting a delivery boy. for each delivery boy listed on
    delivery boy list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the boy.
    """
    delivery_boy = DeliveryBoys.objects.filter(pk=delivery_boy_id)
    if delivery_boy:
        delivery_boy.delete()
    return redirect("delivery_boy_list")


@login_required
@user_passes_test(check_user_role(["Shop Admin"]))
def delivery_boy_edit(request, delivery_boy_id):
    """
    Delivery details menu on Orders section in the side bar will list the delivery boys.
    Editing a delivery boy. for each delivery boy listed, we have a edit button. clicking on
    the edit button will open a form. fields are team member name and phone number,
    email.

    """
    errors = []
    delivery_boy = DeliveryBoys.objects.filter(pk=delivery_boy_id).first()
    if request.method == "POST":
        form = DeliveryBoyForm(request.POST, instance=delivery_boy)
        if form.is_valid():
            delivery_boy = form.save()
            delivery_boy.status = "not verified"
            delivery_boy.save()
            return redirect("delivery_boy_list")

        else:
            errors.append(form.errors)
    else:
        form = DeliveryBoyForm(instance=delivery_boy)
    context = {"delivery_boy_form": form, "orders_active": "active"}
    return render(request, "delivery_boy_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Shop Admin"]))
def delivery_boy_add(request):
    """
    Adding a delivery boy.
    On delivery boy listing page which we can open by clicking
    on the order section on side bar have a add delivery boy button.
    clicking will open the form enter the fields are team member name and phone number,
    email.
    on submission create a new delivery boy.
    """
    errors = []
    if request.method == "POST":
        shop_id_verify = Shop.objects.prefetch_related('unit_admin_user').filter(
            unit_admin_user__uuid=request.user.uuid)
        if shop_id_verify:
            shop_id = shop_id_verify[0]
        else:
            shop_id = ""
        form = DeliveryBoyForm(request.POST)
        if form.is_valid():
            delivery_boy = form.save()
            delivery_boy.shop = shop_id
            delivery_boy.save()
            return redirect("delivery_boy_list")
        else:
            errors.append(form.errors)
            context = {
                "errors": errors,
                "delivery_boy_form": form,
                "orders_active": "active"
            }
            return render(request, "delivery_boy_add.html", context)
    else:
        form = DeliveryBoyForm()

    context = {
        "errors": errors,
        "delivery_boy_form": form,
        "orders_active": "active"

    }
    return render(request, "delivery_boy_add.html", context)


# pip install twilio
# def sms(body, to_phone):
#     from twilio.rest import Client
#
#     account_sid = "ACc6df2dd6139dd07e9d84e78ac73f27f8"
#     auth_token = "d01958dbb0ae7723b4718873add279e7"
#     fromph = "+18106965991"
#     to_phone = str(to_phone)
#     # Here we'll build a new Twilio_client with different credentials
#     twilio_client = Client(account_sid, auth_token)
#
#     message = twilio_client.messages.create(
#         to=to_phone,
#         from_=fromph,
#         body=body)


@login_required
@user_passes_test(check_user_role(["Shop Admin", "PU Admin"]))
def order_list(request, order_id=None):
    """

    :param request:
    :param order_id:
    :return:
    """
    try:
        platform = request.GET.get('platform')
        order_type = request.GET.get('order_type', "")
        shop_id_verify = Shop.objects.filter(
            unit_admin_user__uuid=request.user.uuid)
        if shop_id_verify:
            shop_id = shop_id_verify[0]
        else:
            shop_id = ""
        errors = []
        field_name = request.COOKIES.get('field_name')
        sort_order = request.COOKIES.get('sort_order')

        if request.method == "POST":
            order_delivery = OrderDelivery.objects.filter(order=order_id).first()
            order = Orders.objects.filter(pk=order_id).first()
            form = CourierDetailsForm(request.POST, instance=order_delivery)
            if form.is_valid():
                delivery_data = form.save()
                delivery_data.order = order
                delivery_data.delivery_type = "Courier Delivery"
                delivery_data.save()
                if order.order_status == "Order Packed":
                    order.order_status = "Despatched"
                    order.save()

                    title = "Order Despatched"
                    description = "Your Order Despatched Successfully"
                    NotificationInit(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
                    EmailSend(title, description, [order.user_uuid.email], False)
                    WhatsappMessage(order.user_uuid.phone_number, "Navya Bakers", title, description)
                    # try:
                    #     initialize_fcm_app()
                    #     send_notification.s(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
                    # except:
                    #     pass

                # send notification

                # .........................
                return JsonResponse({
                    'status': 'success',
                    'order_id': order_id,
                    'OrderID': order.order_ID,
                })


            else:
                errors.append(form.errors)
                return JsonResponse({'status': 'fail', 'errors': form.errors})
        search_query = request.GET.get('search', "")
        order_date = request.GET.get('product_date', "")
        order_status = request.GET.get('product_status', "")
        sort_order_data = request.GET.get('sort_order', "")
        if not sort_order_data:
            sort_order_data = sort_order
        field_name_data = request.GET.get('field_name', "")
        if not field_name_data:
            field_name_data = field_name

        if shop_id:
            order_list = Orders.objects.filter(store_uuid=shop_id, order_type__in=["Local Orders", "Pick Up"]).exclude(
                order_status="New Order").order_by('-created_date')
        else:

            order_list = Orders.objects.filter(order_type="Long Distance Orders").exclude(
                order_status="New Order").order_by(
                '-created_date')

        if order_type == "new orders":
            order_list = order_list.filter(order_status = "Confirmed")

        if platform:

            if platform == 'web':
                order_list  = order_list.filter(Q(platform ="Web")|Q(platform= "Other"))
            else:
                order_list = order_list.filter(Q(platform ="IOS")|Q(platform= "Andriod"))


        if request.headers.get('x-requested-with') == 'XMLHttpRequest':
            if search_query:
                order_list = order_list.filter(
                    Q(order_ID__icontains=search_query) |
                    Q(order_type__icontains=search_query) |
                    Q(order_status__icontains=search_query) |
                    Q(user_uuid__first_name__icontains=search_query)
                ).order_by('-created_date')

            if order_date:
                formatted_date = datetime.strptime(order_date, "%Y-%m-%d")
                order_list = order_list.filter(created_date__date=formatted_date)
            if order_status:
                order_list = order_list.filter(product_status=order_status)

            if sort_order_data and field_name_data:
                if sort_order_data == 'asc':
                    if field_name_data == 'user_uuid':
                        order_list = order_list.order_by(field_name_data + '__first_name')
                    else:
                        order_list = order_list.order_by(field_name_data)
                else:
                    if field_name_data == 'user_uuid':
                        order_list = order_list.order_by('-' + field_name_data + '__first_name')
                    else:
                        order_list = order_list.order_by('-' + field_name_data)

        paginator = Paginator(order_list, 6)
        page = request.GET.get('page')

        try:
            orders = paginator.get_page(page)
        except (PageNotAnInteger, TypeError):
            orders = paginator.get_page(1)
        except EmptyPage:
            orders = paginator.get_page(paginator.num_pages)

        base_url = reverse('order_list')
        query_params = request.GET.copy()
        if 'page' in query_params:
            del query_params['page']
        pagination_links = [
            {
                'page_number': page_number,
                'url': f"{base_url}?{query_params.urlencode(safe='/')}&page={page_number}",
            }
            for page_number in orders.paginator.page_range
        ]
        form = CourierDetailsForm()
        try:
            ongoing_orders = DeliveryBoys.objects.filter(status='verified', shop=shop_id)
        except:
            ongoing_orders = []

        new_order_data = [{"order_ID":order.order_ID} for order in orders if (order.order_status == "Confirmed") ]
        context = {
            "courier_form": form,
            "order_data": orders,
            'search_query': search_query,
            'date': order_date,
            'product_status': order_status,
            'pagination_links': pagination_links,
            "ongoing_orders": ongoing_orders,
            "orders_active": "active",
            "platform": platform,
            "order_type": order_type if order_type else "",
            "new_order_data": new_order_data


        }

        if request.headers.get('x-requested-with') == 'XMLHttpRequest':
            if order_type == "new orders":
                new_order_data = [{"order_ID":order.order_ID} for order in orders]
                tbody = render_to_string('new_order_list_page.html',
                                     {"order_data": orders, 'pagination_links': pagination_links,
                                      "ongoing_orders": ongoing_orders, "platform": platform, "order_type": order_type, "new_order_data": new_order_data})
            else:
                tbody = render_to_string('order_list_page.html',
                                     {"order_data": orders, 'pagination_links': pagination_links,
                                      "ongoing_orders": ongoing_orders, "platform": platform, "order_type": order_type, "new_order_data": new_order_data})
            pagination = render_to_string('order_list_pagination.html', {"order_data": orders})
            return JsonResponse({'tbody': tbody, "pagination": pagination})

        errors = []

        # context = {
        #     "order_data": orders,
        #     'search_query': search_query,
        #     'date': order_date,
        #     'product_status': order_status,
        #     'pagination_links': pagination_links,
        #     "ongoing_orders": ongoing_orders,
        #     "errors": errors,
        #     "courier_form": form,
        #     "orders_active": "active",
        #     "modal_active": "active"
        # }
        # return render(request, "order_list.html", context)
        if order_type == "new orders":

            return render(request, "new_orders.html", context)
        response = render(request, "order_list.html", context)
        # response.delete_cookie('field_name', path = r'/adminportal/orders', domain = '127.0.0.1')
        # response.delete_cookie('sort_order', path = r'/adminportal/orders', domain = '127.0.0.1')
        # response.set_cookie('field_name', 'created_date')
        # response.set_cookie('sort_order', 'desc')
        # if field_name_data:
        #
        #     response.set_cookie('field_name', field_name_data)
        #     response.set_cookie('sort_order', sort_order_data)
        # else:

        return response
    #
    except IndexError:
        context = {
            "order_data": [],
            "orders_active": "active"

        }
        return render(request, "order_list.html", context)


#
#
#
#

@login_required
@user_passes_test(check_user_role(["PU Admin"]))
def courier_details_edit(request, order_id):
    csrf = request.POST.get('csrfmiddlewaretoken')
    errors = []
    button_status = "false"
    order_delivery = OrderDelivery.objects.filter(order=order_id).first()

    if order_delivery:
        form = CourierDetailsForm(instance=order_delivery)
        if order_delivery.order.order_status == "Order Packed":
            button_status = "true"

    else:
        order = Orders.objects.filter(uuid=order_id).first()
        order_delivery = OrderDelivery.objects.create(delivery_type="Courier Delivery", order=order)
        form = CourierDetailsForm(instance=order_delivery)
        if order.order_status == "Order Packed":
            button_status = "true"

    courier_form = render_to_string('courier_edit_form.html',
                                    {'courier_form': form, "order_id": order_id, "button_status": button_status})

    return JsonResponse({'success': True, 'form': courier_form})


class ShipperAutocomplete(autocomplete.Select2QuerySetView):
    def get_result_label(self, item):
        # Customize the format of the autocomplete options
        return f"{item.team_member_name}"

    def get_queryset(self):
        ongoing_orders = DeliveryBoys.objects.filter(status='verified', delivery_boy_status='not assigned')
        if self.q:
            ongoing_orders = ongoing_orders.filter(team_member_name__istartswith=self.q)

        return ongoing_orders


# function to send notification



def WhatsappMessageSend(order, pick_up_address, drop_address, delivery_boy):

    try:
        if delivery_boy:
            START_LAT = order.drop_address.latitude
            START_LNG = order.drop_address.longitude
            DEST_LAT = order.store_uuid.latitude
            DEST_LNG = order.store_uuid.longitude
            google_place_link = f"https://www.google.com/maps/dir/{START_LAT},{START_LNG}/{DEST_LAT},{DEST_LNG}"
            data = {"pick_up_address": pick_up_address, "drop_address": drop_address,
                    "google_place_link": google_place_link, "orderID": order.order_ID}
            data = send_whatsapp_message_delivery_boy.apply_async(args=[delivery_boy.phone_number, delivery_boy.team_member_name, data], countdown=5)

            return data
        else:
            return None

    except Exception as e:
        logging.error(f"Send Whatsapp message to Shipper Failed: {e}")
        return None

def ShipperEmailSend(shipper_uuid, order, pick_up_address, drop_address, delivery_boy):
    try:
        # fetch location user location details
        location_list = []
        orders = Orders.objects.filter(delivery_boy=shipper_uuid, order_status="Delivery Assigned")
        for i in orders:
            try:
                latitude = i.drop_address.latitude
                longitude = i.drop_address.longitude
                location_list.append((latitude, longitude))
            except:
                pass

        map_link = [f"{i[0]},{i[1]}" for i in location_list]
        map_link_join = "|".join(map_link)

        # send email with the map
        subject = f"Order {order.order_ID} Assinged to you"
        latitude = str(order.store_uuid.latitude)
        longitude = str(order.store_uuid.longitude)
        base = 'https://www.google.com/maps/dir/?api=1'
        map_link_to_send = f"{base}&origin={latitude},{longitude}&destination={latitude},{longitude}&waypoints={map_link_join}&travelmode=driving"

        message = f""" The Order no: {order.order_ID} assigned to you by NavyaBakeShop.\nPickup Location : {pick_up_address} \nDelivery Location : {drop_address}\n\nPlease click link to track order : {map_link_to_send}\n\nNavya Bakers
                  """
        # change to celery
        # email_data = email_sending.s(subject=subject,
        #                              message=message,
        #                              from_email=settings.EMAIL_HOST_USER,
        #                              recipient_list=[delivery_boy.email],
        #                              fail_silently=False, )
        email_data = email_sending.apply_async(args=[subject, message, settings.EMAIL_HOST_USER, [delivery_boy.email], False], countdown=5)
        return email_data



    except Exception as e:
        logging.error(f"Failed to Send Email While Assigning Delivery Boy:{e}")
        return None


def save_shipper(request):
    if request.method == 'POST':
        shipper_uuid = request.POST.get('shipper_uuid')
        page = request.POST.get('page')
        search = request.POST.get('search')
        product_date = request.POST.get('product_date')
        try:
            shipper = DeliveryBoys.objects.filter(pk=shipper_uuid).first()
            # if shipper.delivery_boy_status == "assigned":
            #     return JsonResponse({"sucess": False, 'message': 'Shipper Already Assigned'})
            order_uuid = request.POST.get('order_uuid')

            order = Orders.objects.filter(uuid=order_uuid).first()
            if order.order_status in ["Viewed", "New Order"]:
                return JsonResponse({'success': False, 'message': 'Order not confirmed'})
            if order.order_status == 'Delivered':
                return JsonResponse({'success': False, 'message': 'Order already delivered'})
            if order.order_status == 'Failed':
                return JsonResponse({'success': False, 'message': 'Order Failed'})
            if order.order_status == "Delivery Assigned":
                return JsonResponse({'success': False, 'message': 'Order already Assigned'})
            order.order_status = "Delivery Assigned"
            order.order_assigntime = timezone.now()
            order.delivery_boy = shipper_uuid
            try:
                delivery_boy = OrderDelivery.objects.get_or_create(delivery_boy=shipper, order=order,
                                                               defaults={'delivery_type': order.order_type})
            except:
                OrderDelivery.objects.filter(delivery_boy= shipper, order = order).delete()
                OrderDelivery.objects.create(delivery_boy = shipper, order = order, defaults = {"delivery_type": order.order_type})
                pass
            shipper.delivery_boy_status = 'assigned'
            shipper.save()
            order.save()
            delivery_boy = DeliveryBoys.objects.filter(id=order.delivery_boy).first()
            if delivery_boy:
                drop_address = str(order.drop_address.name) + ", " + str(
                    order.drop_address.house_number_or_name) + ", " + str(order.drop_address.street) + " " + str(
                    order.drop_address.land_mark) + ", " + str(order.drop_address.city) + " " + str(
                    order.drop_address.district) + ", " + str(order.drop_address.state_or_province) + " " + str(
                    order.drop_address.pin_code) if order.drop_address else ""
                pick_up_address = str(order.store_uuid.unit_name) + "(" + str(
                    order.store_uuid.unit_code) + "), " + str(order.store_uuid.unit_location) + "," + str(
                    order.store_uuid.street) + ", " + str(order.store_uuid.city) + " " + str(
                    order.store_uuid.district) + ", " + str(order.store_uuid.state_or_province) if order.store_uuid else ""
            # send notification
                email_msg = ShipperEmailSend(shipper_uuid, order, pick_up_address, drop_address, delivery_boy)


            # to function
                whatsapp_msg = WhatsappMessageSend(order, pick_up_address, drop_address, delivery_boy)


            # to function
                title = "Order Assigned"
                description = "Your Order Assigned Successfully, Delivery Boy will reach you shortly!"
                NotificationInit(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
                EmailSend(title, description, [order.user_uuid.email], False)

            # grouping asynchronous tasks
            #     task_list = []
            #     if email_msg:
            #         task_list.append(email_msg)
            #     if whatsapp_msg:
            #         task_list.append(whatsapp_msg)
            #     if notification:
            #         task_list.append(notification)
            #     print(task_list)
            #     flat_tasks = list(filter(None, chain.from_iterable(
            #         [t] if not isinstance(t, list) else t for t in task_list
            #     )))
            #     print(flat_tasks)
            #     my_grouped_task = group(flat_tasks)
            #     my_grouped_task.apply_async()
            #     print(my_grouped_task)
            # print("My grouped task")

            # .........................
            return JsonResponse({'success': "True", 'page': page, 'search': search, 'product_date': product_date})
        except Exception as e:
            logging.error(f"Failed While Assigning Delivery Boy:{e}")
            return JsonResponse({'success': "False", 'page': page, 'search': search, 'product_date': product_date})

    else:
        return JsonResponse({'success': "False"})


def save_order_status(request):
    if request.method == 'POST':
        page = request.POST.get('page')
        search = request.POST.get('search')
        product_date = request.POST.get('product_date')
        try:
            order_uuid = request.POST.get('order_uuid')
            status = request.POST.get('selected_value').strip()
            order = Orders.objects.filter(uuid=order_uuid).first()
            order.order_status = status
            order.save()
            if status == "Order Packed":
                try:
                    combined_datetime = datetime.combine(order.delivery_slot_date, order.delivery_slot_time)
                except:
                    combined_datetime = datetime.now()
                time_dif_with_del_slot = combined_datetime.astimezone() - datetime.now().astimezone()
                duration_in_s_data = time_dif_with_del_slot.total_seconds()
                hours_del = divmod(duration_in_s_data, 3600)[0]

                if hours_del < 0:
                    order.color_status = "Red"
                    order.color_status_updation_time = datetime.now()
                    order.save()

                elif hours_del < 1 and hours_del > 0:
                    order.color_status = "Orange"
                    order.color_status_updation_time = datetime.now()
                    order.save()
                else:
                    order.color_status = "White"
                    order.color_status_updation_time = datetime.now()
                    order.save()
            if status == "Failed":
                order.color_status = "Dark Red"
                order.color_status_updation_time = datetime.now()
                order.save()
                try:
                    shipper_uuid = order.delivery_boy
                    shipper = DeliveryBoys.objects.filter(pk=shipper_uuid).first()
                    if shipper.delivery_boy_status == "assigned":
                        shipper.delivery_boy_status = "not assigned"
                        shipper.save()
                except:
                    pass

            if status in ["Delivery Assinged", "Despatched", "Delivered"]:
                order.color_status = "White"
                order.color_status_updation_time = datetime.now()
                order.save()

                # if order status delivered
                if status == "Delivered":
                    try:
                        shipper_uuid = order.delivery_boy
                        shipper = DeliveryBoys.objects.filter(pk=shipper_uuid).first()
                        if shipper.delivery_boy_status == "assigned":
                            shipper.delivery_boy_status = "not assigned"
                            shipper.save()
                        custom_coupon_check = CustomCouponSettings.objects.first()
                        if (order.grand_total >= custom_coupon_check.lower_limit_price) and (order.grand_total <= custom_coupon_check.higher_limit_price):
                            # creating coupon instance with the code
                            custom_coupon_settings = CustomCouponSettings.objects.first()
                            coupon_code_generation = f"NavyaCoup{str(random.randint(1000,100000000))}"
                            custom_coupon = CustomCoupon.objects.create(coupon_code=coupon_code_generation, coupon=custom_coupon_settings, user=order.user_uuid)
                            # notification sending
                            # need message configuration tables
                            notification_title = "Eligible for Coupon!"
                            notification_description = f"You are eligible for Coupon ({coupon_code_generation})."
                            NotificationInit(order.user_uuid.fcm_token, order, notification_title, notification_description, order.user_uuid)
                            EmailSend(notification_title, notification_description, [order.user_uuid.email], False)
                            WhatsappMessage(order.user_uuid.phone_number, "Navya Bakers", notification_title, notification_description)

                    except:
                        pass

            order.save()
            # send notification
            try:
                initialize_fcm_app()
            except:
                pass
            if status == "Order Packed":
                description = "Your Order Packed Successfully, Delivery Boy will pick your order shortly!"
            elif status == "Delivery Assigned":
                description = "Your Order Assigned Successfully, your order will reach your foot steps shortly!"
            elif status == "Delivered":
                description = "Your Order Delivered Successfully, Please give your valuable feed for better future endevours!"
            elif status == 'Failed':
                description = "Sorry! Your Order Failed!"
            else:
                description = "Your Order Status will be updated soon!"
            title = status
            NotificationInit(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
            EmailSend(title, description, [order.user_uuid.email], False)
            WhatsappMessage(order.user_uuid.phone_number, "Navya Bakers", title, description)
            # try:
            #
            #     send_notification.s(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
            #
            #
            #
            #
            # except Exception as e:
            #     logging.error(f"Notification Send Failed on {description}: {e}")
            #     # print(e)
            #
            #     pass

            return JsonResponse({'status': 'success', 'page': page, 'search': search, 'product_date': product_date})
        except Exception as e:
            return JsonResponse(
                {'status': 'fail', 'msg': str(e), 'page': page, 'search': search, 'product_date': product_date})


@csrf_exempt
def save_customorder_status(request):
    if request.method == 'POST':
        try:
            order_uuid = request.POST.get('order_uuid')
            status = request.POST.get('selected_value')

            order = Orders.objects.filter(uuid=order_uuid).first()

            if not order:
                return JsonResponse({'status': 'fail', 'msg': 'Order not found'})

            # Ensure the status cannot be changed once set to "Contacted"
            if order.order_status == "Contacted":
                return JsonResponse(
                    {'status': 'fail', 'msg': 'Order status cannot be changed after being set to Contacted'})

            order.order_status = status
            order.save()

            return JsonResponse({'status': 'success'})
        except Exception as e:
            return JsonResponse({'status': 'fail', 'msg': str(e)})


@csrf_exempt  # Remove this if CSRF token is handled properly in the frontend
def save_customorder_data(request):
    if request.method == 'POST':

        uuid = request.POST.get('order_ID')  # Ensure the key name matches the form field
        order = Orders.objects.filter(order_ID=uuid).first()  # Fetch existing order

        if not order:
            return JsonResponse({'status': 'fail', 'msg': 'Order not found'})

        # Bind the form to update the existing order instance
        form = CustomOrderForm(request.POST, instance=order)

        if form.is_valid():
            form.save()  # This will update the existing order
            return JsonResponse({'status': 'success', 'message': 'Order updated successfully'})
        else:
            return JsonResponse({'status': 'error', 'errors': form.errors})

    return JsonResponse({'status': 'error', 'message': 'Invalid request method'})


def order_status_update_to_viewed(request, orderID):
    if request.method == 'POST':
        page = request.POST.get('page')
        search = request.POST.get('search')
        product_date = request.POST.get('product_date')
        try:
            order = Orders.objects.filter(order_ID=orderID).first()
            if order.order_status == 'Confirmed':
                order.order_status = 'Viewed'
                order.save()
                # send notification

                title = "Order Viewed"
                description = "Your Order Viewed by the Merchant and will be packed soon!"

                NotificationInit(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
                EmailSend(title, description, [order.user_uuid.email], False)
                WhatsappMessage(order.user_uuid.phone_number, "Navya Bakers", title, description)
                # try:
                #     initialize_fcm_app()
                #     send_notification.s(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
                # except Exception as e:
                #     logging.error(f"Notification Send Failed on Order Viewed: {e}")
                #     # print(e)
                #
                #     pass
                return JsonResponse({'status': 'success', 'page': page, 'search': search, 'product_date': product_date})
            return JsonResponse({'status': 'fail', 'page': page, 'search': search, 'product_date': product_date})


        except Exception as e:
            return JsonResponse(
                {'status': 'fail', 'msg': str(e), 'page': page, 'search': search, 'product_date': product_date})

#........................................ promotioins start ...........................................
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def coupon_list(request):
    """
    Coupons  menu on Promotions section in the side bar will list the coupons.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete coupons.
    this defined coupons will list on apps.
    if they are eligible they can use the coupons.
    :param request:
    :return:
    """
    coupon = Coupons.objects.order_by("-created_at")
    paginator = Paginator(coupon, 6)
    page = request.GET.get("page")
    try:
        coupon_list_data = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        coupon_list_data = paginator.get_page(1)
    except EmptyPage:
        coupon_list_data = paginator.get_page(paginator.num_pages)
    context = {"coupon_data": coupon_list_data, "promotion_active": "active"}
    return render(request, "coupon_list.html", context)

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def coupon_delete(request, coupon_id):

    """
     Coupons  menu on Promotions section in the side bar will list the coupons.
    Deleting a coupon. for each coupon listed on
    coupon list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the coupon.
    :param request:
    :param coupon_id:
    :return:
    """
    coupon = Coupons.objects.filter(pk=coupon_id)

    # there are ads exists for coupons so we couldn't delete the coupon
    # since we add a warning message
    if coupon:
        ads = Ads.objects.filter(Coupon=coupon_id)

        if ads:
            messages.add_message(request, messages.WARNING,
                                 "Your coupon used in ads! couldn't delete coupon")
            # messages.error(request, "Your coupon used in ads! couldn't delete coupon")
        else:
            coupon.delete()
    return redirect("list_coupons")

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def coupon_edit(request, coupon_id):
    """
    Coupons  menu on Promotions section in the side bar will list the coupons.
    Editing a coupon. for each coupon listed, we have a edit button. clicking on
    the edit button will open a form. fields are coupon_name and coupon_code,
    coupon_description, icon, coupon_on, applicable_category, applicable_subcategory,
    applicable_product, applicable_sku, minimum_total_bill_amount, coupon_type,
    discount_amount, no_of_users, validity_start_date, validity_end_date, terms_and_conditions.
    validations listed on the forms. applicable_category, applicable_subcategory,
    applicable_product, applicable_sku are dynamic fields based on coupon_on field.
    coupon maybe amount based or percentage based. the field used to indicate is
    coupon_type.

    Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.

    :param request:
    :param coupon_id:
    :return:
    """
    errors = []

    coupon = Coupons.objects.filter(pk=coupon_id).first()
    if request.method == "POST":
        # since request not editable make a copy and update the value
        post_data = request.POST.copy()

        # null value raises validation error so remove the null value.
        # null value appears since the drop down on select have a field select all.
        # if we select the value using the select all option we need to remove the "".
        if '' in post_data.getlist('ApplicableCategory', None):
            category_list = post_data.getlist('ApplicableCategory', None)
            if category_list:
                category_list.remove('')
            post_data.setlist('ApplicableCategory', category_list)

        if '' in post_data.getlist('ApplicableSubCategory', None):
            sub_category_list = post_data.getlist('ApplicableSubCategory', None)
            if sub_category_list:
                sub_category_list.remove('')
            post_data.setlist('ApplicableSubCategory', sub_category_list)
        if '' in post_data.getlist('ApplicableProduct', None):
            product_list = post_data.getlist('ApplicableProduct', None)
            if product_list:
                product_list.remove('')
            post_data.setlist('ApplicableProduct', product_list)

        if '' in post_data.getlist('ApplicableSku', None):
            sku_list = post_data.getlist('ApplicableSku', None)
            if sku_list:
                sku_list.remove('')
            post_data.setlist('ApplicableSku', sku_list)

        coupon_on = post_data.get("CouponOn", None)

        # since coupon_on based dynamic field and coupon_type dynamic fields exists
        # other related fields should populate null except the selected required values

        try:
            if coupon_on == "Category":
                del (post_data['ApplicableSubCategory'])
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif coupon_on == "SubCategory":
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif coupon_on == "Product":
                del (post_data['ApplicableSku'])
        except:
            pass

        form = CouponForm(post_data, request.FILES, instance=coupon)

        if form.is_valid():
            if request.POST.get('image', ''):
                form.instance.Icon = None
            if request.POST.get("CouponType") == "amount":
                form.instance.DiscountPercentage = None
                form.instance.MaxDiscountAmountForPercentage = None
            else:
                form.instance.DiscountAmount = None
            try:
                form.save()
            # one type of validation error couldn't managed itself in the form.
            # please refer coupon form
            except  ValidationError as e:
                error_data = e.message_dict
                form.add_error(list(error_data.keys())[0], e.messages[0])
                return JsonResponse({"success": False, 'errors': form.errors})

            return JsonResponse({'success': True})

        else:
            errors.append(form.errors)
            return JsonResponse({'success': False, 'errors': form.errors})
    else:
        form = CouponForm(instance=coupon)
    context = {
        "errors": errors,
        "coupon_form": form,
        "coupon_id": coupon.pk,
        "promotion_active": "active",
        "coupon_data": coupon.CouponOn,
        "coupon_type": coupon.CouponType
    }
    return render(request, "coupon_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def coupon_add(request):
    """
    Coupons  menu on Promotions section in the side bar will list the coupons.
    adding a coupon.We have a add coupon button. clicking on
    the add button will open a form. fields are coupon_name and coupon_code,
    coupon_description, icon, coupon_on, applicable_category, applicable_subcategory,
    applicable_product, applicable_sku, minimum_total_bill_amount, coupon_type,
    discount_amount, no_of_users, validity_start_date, validity_end_date, terms_and_conditions.
    validations listed on the forms. applicable_category, applicable_subcategory,
    applicable_product, applicable_sku are dynamic fields based on coupon_on field.
    coupon maybe amount based or percentage based. the field used to indicate is
    coupon_type.

    Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.
    :param request:
    :return:
    """
    errors = []
    if request.method == "POST":
        post_data = request.POST.copy()

        # null value raises validation error so remove the null value.
        # null value appears since the drop down on select have a field select all.
        # if we select the value using the select all option we need to remove the "".

        if '' in post_data.getlist('ApplicableCategory', None):
            category_list = post_data.getlist('ApplicableCategory', None)
            if category_list:
                category_list.remove('')
            post_data.setlist('ApplicableCategory', category_list)

        if '' in post_data.getlist('ApplicableSubCategory', None):
            sub_category_list = post_data.getlist('ApplicableSubCategory', None)
            if sub_category_list:
                sub_category_list.remove('')
            post_data.setlist('ApplicableSubCategory', sub_category_list)
        if '' in post_data.getlist('ApplicableProduct', None):
            product_list = post_data.getlist('ApplicableProduct', None)
            if product_list:
                product_list.remove('')
            post_data.setlist('ApplicableProduct', product_list)

        if '' in post_data.getlist('ApplicableSku', None):
            sku_list = post_data.getlist('ApplicableSku', None)
            if sku_list:
                sku_list.remove('')
            post_data.setlist('ApplicableSku', sku_list)

        coupon_on = post_data.get("CouponOn", None)


        # since coupon_on based dynamic field and coupon_type dynamic fields exists
        # other related fields should populate null except the selected required values

        try:
            if coupon_on == "Category":
                del (post_data['ApplicableSubCategory'])
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif coupon_on == "SubCategory":
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif coupon_on == "Product":
                del (post_data['ApplicableSku'])
        except:
            pass

        coupon_form = CouponForm(post_data, request.FILES)

        if coupon_form.is_valid():
            if request.POST.get('image', ''):
                coupon_form.instance.Icon = None
            if request.POST.get("CouponType") == "amount":
                coupon_form.instance.DiscountPercentage = None
                coupon_form.instance.MaxDiscountAmountForPercentage = None
            else:
                coupon_form.instance.DiscountAmount = None

            try:
                coupon_form.save()
            except  ValidationError as e:

                # one type of validation error couldn't managed itself in the form.
                # please refer coupon form

                error_data = e.message_dict
                coupon_form.add_error(list(error_data.keys())[0], e.messages[0])
                return JsonResponse({"success": False, 'errors': coupon_form.errors})
            return JsonResponse({'success': True})
        else:
            errors.append(coupon_form.errors)
            return JsonResponse({'success': False, 'errors': coupon_form.errors})

    else:
        coupon_form = CouponForm()
        errors = []
    context = {
        "errors": errors,
        "coupon_form": coupon_form,
        "promotion_active": "active",
        "coupon_data": "Category",
        "coupon_type": "amount"
    }
    return render(request, "coupon_add.html", context)


@user_passes_test(check_user_role(["Super Admin", "NBC Admin", "PU Admin"]))
def delivery_slot_list(request):
    """
    In NBC admin dashboard, On the orders section we have order slot menu. clicking on the
    menu will open the slot list. we can add, edit and view the slots from here.
    :param request:
    :return:
    """

    delivery_slots = DeliverySlot.objects.order_by('-created_at')  # .filter(shop = shop_id)
    paginator = Paginator(delivery_slots, 6)
    page = request.GET.get("page")
    try:
        delivery_slot = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        delivery_slot = paginator.get_page(1)
    except EmptyPage:
        delivery_slot = paginator.get_page(paginator.num_pages)
    context = {"delivery_slot_data": delivery_slot, "orders_active": "active"}
    return render(request, "delivery_slot_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delivery_slot_delete(request, delivery_slot_id):
    """
    In NBC admin dashboard, On the orders section we have order slot menu.
    on the slots listed, for each slot we have a delete button. Clicking on
    the delete button will open a dialog box. clicking on the confirm will
    delete the delivery slot.

    :param request:
    :param delivery_slot_id:
    :return:
    """
    delivery_slot = DeliverySlot.objects.filter(pk=delivery_slot_id)
    if delivery_slot:
        delivery_slot.delete()
    return redirect("delivery_slot_list")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delivery_slot_edit(request, delivery_slot_id):
    """
    In NBC admin dashboard, On the orders section we have order slot menu.
    on the slots listed, for each slot we have a edit button. Clicking on
    the edit button will open a edit form. fields of the form are  start_time,
    end_time, is_available, status. based on the availability status, it is
    showing on the app(select Delivery slots).

    :param request:
    :param delivery_slot_id:
    :return:
    """
    errors = []

    delivery_slot = DeliverySlot.objects.filter(pk=delivery_slot_id).first()
    if request.method == "POST":
        form = DeliverySlotForm(request.POST, instance=delivery_slot)  # request = request
        if form.is_valid():
            del_slot = form.save()

            return redirect("delivery_slot_list")

        else:
            errors.append(form.errors)
    else:
        form = DeliverySlotForm(instance=delivery_slot)  # , request = request
    context = {"delivery_slot_form": form, "orders_active": "active"}
    return render(request, "delivery_slot_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delivery_slot_add(request):
    """
        In NBC admin dashboard, On the orders section we have order slot menu.
    Clicking on the Add Delivery Slot button will open a add delivery slot form.
    fields of the form are  start_time, end_time, is_available, status.

    :param request:
    :return:
    """
    errors = []

    if request.method == "POST":
        form = DeliverySlotForm(request.POST)  # , request = request
        if form.is_valid():
            del_slot = form.save()
            return redirect("delivery_slot_list")
        else:
            errors.append(form.errors)
            context = {
                "errors": errors,
                "delivery_slot_form": form,
                "orders_active": "active"
            }
            return render(request, "delivery_slot_add.html", context)
    else:
        form = DeliverySlotForm()  # request = request

    context = {
        "errors": errors,
        "delivery_slot_form": form,
        "orders_active": "active"

    }
    return render(request, "delivery_slot_add.html", context)

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def discount_list(request):
    """
    Discount  menu on Promotions section in the side bar will list the discounts.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete discounts.
    this defined discount will shown on product listing with a strike.
    """
    discount = Discount.objects.order_by("-created_at")
    paginator = Paginator(discount, 6)
    page = request.GET.get("page")
    try:
        discount_list_data = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        discount_list_data = paginator.get_page(1)
    except EmptyPage:
        discount_list_data = paginator.get_page(paginator.num_pages)
    context = {"discount_data": discount_list_data, "promotion_active": "active"}
    return render(request, "discount_list.html", context)

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def discount_delete(request, discount_id):
    """
    Discount  menu on Promotions section in the side bar will list the discounts.
    Deleting a discount. for each discount listed on
    discount list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the discount.
    :param request:
    :param discount_id:
    :return:
    """
    discount = Discount.objects.filter(pk=discount_id)
    # there are ads exists for discounts so we couldn't delete the discount.
    # since we add a warning message
    if discount:
        ads = Ads.objects.filter(Discount=discount_id)
        messages.add_message(request, messages.WARNING,
                             "Your Discount used in ads! couldn't delete discount")
        # messages.error(request, "Your coupon used in ads! couldn't delete coupon")
    else:
        discount.delete()
    return redirect("list_discounts")

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def discount_edit(request, discount_id):
    """
    Discount  menu on Promotions section in the side bar will list the discounts.
    Editing a discount. for each discount listed, we have a edit button. clicking on
    the edit button will open a form. fields are discount_name and discount_code,
    discount_description, discount_on, applicable_category, applicable_subcategory,
    applicable_product, applicable_sku,
    discount_percentage.
    validations listed on the forms. applicable_category, applicable_subcategory,
    applicable_product, applicable_sku are dynamic fields based on discount_on field.
    discount is percentage based.
     Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.
    :param request:
    :param discount_id:
    :return:
    """
    errors = []

    discount = Discount.objects.filter(pk=discount_id).first()
    if request.method == "POST":
        post_data = request.POST.copy()
        # null value raises validation error so remove the null value.
        # null value appears since the drop down on select have a field select all.
        # if we select the value using the select all option we need to remove the "".

        if '' in post_data.getlist('ApplicableCategory', None):
            category_list = post_data.getlist('ApplicableCategory', None)
            if category_list:
                category_list.remove('')
            post_data.setlist('ApplicableCategory', category_list)

        if '' in post_data.getlist('ApplicableSubCategory', None):
            sub_category_list = post_data.getlist('ApplicableSubCategory', None)
            if sub_category_list:
                sub_category_list.remove('')
            post_data.setlist('ApplicableSubCategory', sub_category_list)
        if '' in post_data.getlist('ApplicableProduct', None):
            product_list = post_data.getlist('ApplicableProduct', None)
            if product_list:
                product_list.remove('')
            post_data.setlist('ApplicableProduct', product_list)

        if '' in post_data.getlist('ApplicableSku', None):
            sku_list = post_data.getlist('ApplicableSku', None)
            if sku_list:
                sku_list.remove('')
            post_data.setlist('ApplicableSku', sku_list)

        discount_on = post_data.get("DiscountOn", None)
        # since discount_on based dynamic field and coupon_type dynamic fields exists
        # other related fields should populate null except the selected required values

        try:
            if discount_on == "Category":
                del (post_data['ApplicableSubCategory'])
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif discount_on == "SubCategory":
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif discount_on == "Product":
                del (post_data['ApplicableSku'])
        except:
            pass

        form = DiscountForm(post_data, request.FILES, instance=discount)
        if form.is_valid():

            if request.POST.get('image2', ''):
                form.instance.StandardImage = None
            if request.POST.get('image3', ''):
                form.instance.BannerImage = None
            form.save()
            return JsonResponse({'success': True})

        else:
            errors.append(form.errors)
            return JsonResponse({'success': False, 'errors': form.errors})
    else:
        form = DiscountForm(instance=discount)
    context = {
        "errors": errors,
        "discount_form": form,
        "discount_id": discount.pk,
        "promotion_active": "active",
        "discount_data": discount.DiscountOn
    }
    return render(request, "discount_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def discount_add(request):
    """
    Discount  menu on Promotions section in the side bar will list the discounts.
    adding a discount.We have a add coupon discount. clicking on
    the add button will open a form. fields are discount_name and discount_code,
    discount_description, discount_on, applicable_category, applicable_subcategory,
    applicable_product, applicable_sku,
    discount_percentage.
    validations listed on the forms. applicable_category, applicable_subcategory,
    applicable_product, applicable_sku are dynamic fields based on discount_on field.
    discount is percentage based.
     Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.
    """
    errors = []
    if request.method == "POST":
        post_data = request.POST.copy()
        # null value raises validation error so remove the null value.
        # null value appears since the drop down on select have a field select all.
        # if we select the value using the select all option we need to remove the "".
        if '' in post_data.getlist('ApplicableCategory', None):
            category_list = post_data.getlist('ApplicableCategory', None)
            if category_list:
                category_list.remove('')
            post_data.setlist('ApplicableCategory', category_list)

        if '' in post_data.getlist('ApplicableSubCategory', None):
            sub_category_list = post_data.getlist('ApplicableSubCategory', None)
            if sub_category_list:
                sub_category_list.remove('')
            post_data.setlist('ApplicableSubCategory', sub_category_list)
        if '' in post_data.getlist('ApplicableProduct', None):
            product_list = post_data.getlist('ApplicableProduct', None)
            if product_list:
                product_list.remove('')
            post_data.setlist('ApplicableProduct', product_list)

        if '' in post_data.getlist('ApplicableSku', None):
            sku_list = post_data.getlist('ApplicableSku', None)
            if sku_list:
                sku_list.remove('')
            post_data.setlist('ApplicableSku', sku_list)

        discount_on = post_data.get("DiscountOn", None)

        # since discount_on based dynamic field and coupon_type dynamic fields exists
        # other related fields should populate null except the selected required values

        try:
            if discount_on == "Category":
                del (post_data['ApplicableSubCategory'])
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif discount_on == "SubCategory":
                del (post_data['ApplicableProduct'])
                del (post_data['ApplicableSku'])

            elif discount_on == "Product":
                del (post_data['ApplicableSku'])
        except:
            pass

        discount_form = DiscountForm(post_data, request.FILES)
        if discount_form.is_valid():
            # image submission
            if request.POST.get('image2', ''):
                discount_form.instance.StandardImage = None
            if request.POST.get('image3', ''):
                discount_form.instance.BannerImage = None
            discount_form.save()
            return JsonResponse({'success': True})
        else:
            errors.append(discount_form.errors)
            return JsonResponse({'success': False, 'errors': discount_form.errors})

    else:
        discount_form = DiscountForm()
        errors = []
    context = {
        "errors": errors,
        "discount_form": discount_form,
        "promotion_active": "active",
        "discount_data": "Category"
    }
    return render(request, "discount_add.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def image_field_style(request):
    link_1 = request.GET.get('image_link_1', '/media/icons/icon.jpg')
    link_2 = request.GET.get('image_link_2', '/media/standard_images/standard_image.jpg')
    link_3 = request.GET.get('image_link_3', '/media/banners/banner.jpg')

    image_field1 = render_to_string('image_field.html',
                                    {'class_name': 'upload', 'image_reset_button': 'image_reset_button1',
                                     'field_name': 'Icon'})
    image_field2 = render_to_string('image_field.html',
                                    {'class_name': 'upload2', 'image_reset_button': 'image_reset_button2',
                                     'field_name': 'StandardImage', "link": link_2})
    image_field3 = render_to_string('image_field.html',
                                    {'class_name': 'upload3', 'image_reset_button': 'image_reset_button3',
                                     'field_name': 'BannerImage', "link": link_3})
    return JsonResponse(
        {'image_field1': image_field1, 'image_field2': image_field2, 'image_field3': image_field3, "link_1": link_1,
         "link_2": link_2, "link_3": link_3})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def ad_list(request):
    """
    Ads menu on Promotions section in the side bar will list the ads.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete ads.
    These ads are used in carousel in the app and also the bottom banner.
    We are defining the placement of ads while creating.

    :param request:
    :return:
    """
    ad = Ads.objects.order_by("-created_at")
    paginator = Paginator(ad, 6)
    page = request.GET.get("page")
    try:
        ad_list_data = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        ad_list_data = paginator.get_page(1)
    except EmptyPage:
        ad_list_data = paginator.get_page(paginator.num_pages)
    context = {"ad_data": ad_list_data, "promotion_active": "active"}
    return render(request, "ad_list.html", context)

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def ad_delete(request, ad_id):
    """
    Ads  menu on Promotions section in the side bar will list the ads.
    Deleting a ad. for each ads listed on
    ads list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the ad.
    :param request:
    :param ad_id:
    :return:
    """
    ad = Ads.objects.filter(pk=ad_id)
    if ad:
        ad.delete()
    return redirect("list_ad")

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def ad_edit(request, ad_id):
    """
    Ads menu on Promotions section in the side bar will list the ads.
    Editing a ad. for each ad listed, we have a edit button. clicking on
    the edit button will open a form. fields are placement_choices and AdTitle,
    AdDescription, AdPlacement, AdType, StandardImage,
    BannerImage, Discount,
    Coupon, Product.
    validations listed on the forms. Discount, Coupon, Product are dynamic fields based on AdType field.
    On submitting we can edit a ad already existing.
    Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.
    :param request:
    :param ad_id:
    :return:
    """
    errors = []

    ad = Ads.objects.filter(pk=ad_id).first()
    if request.method == "POST":

        form = AdForm(request.POST, request.FILES, instance=ad)

        coupon = request.POST.get("Coupon", "")
        discount = request.POST.get("Discount", "")

        product = request.POST.get("Product", "")

        if form.is_valid():
            ad = form.save(commit=False)

            # dyanamic entering of  adtype based on the form value
            if ad.AdType == "Coupon":
                try:
                    coupon_instance = Coupons.objects.filter(pk=coupon).first()
                    ad.Coupon = coupon_instance
                except:
                    pass

            elif ad.AdType == "Discount":
                try:
                    discount_instance = Discount.objects.filter(pk=discount).first()
                    ad.Discount = discount_instance
                except:
                    pass

            elif ad.AdType == "Product":
                try:
                    product_instance = Products.objects.filter(pk=product).first()
                    ad.Product = product_instance
                except:
                    pass

            # if no image mark it as none
            if request.POST.get('image2', ''):
                form.instance.StandardImage = None
            if request.POST.get('image3', ''):
                form.instance.BannerImage = None
            ad.save()
            return redirect('list_ad')

        else:
            errors.append(form.errors)
    else:
        form = AdForm(instance=ad)
    context = {
        "errors": errors,
        "ad_form": form,
        "promotion_active": "active",
        "ad_data": ad.AdType
    }
    return render(request, "ad_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def ad_add(request):
    """
    Ad  menu on Promotions section in the side bar will list the ads.
    adding a ad.We have a add ad button. clicking on
    the add button will open a form. fields are placement_choices and AdTitle,
    AdDescription, AdPlacement, AdType, StandardImage,
    BannerImage, Discount,
    Coupon, Product.
    validations listed on the forms. Discount, Coupon, Product are dynamic fields based on AdType field.
    Imp: image refresh and showing logic is handled via javascript.It is field wise and based on id.

    """
    errors = []
    if request.method == "POST":

        form = AdForm(request.POST, request.FILES)

        coupon = request.POST.get("Coupon", "")
        discount = request.POST.get("Discount", "")

        product = request.POST.get("Product", "")

        if form.is_valid():
            ad = form.save(commit=False)

            # dyanamic entering of  adtype based on the form

            if ad.AdType == "Coupon":
                try:
                    coupon_instance = Coupons.objects.filter(pk=coupon).first()
                    ad.Coupon = coupon_instance
                except:
                    pass


            elif ad.AdType == "Discount":
                try:
                    discount_instance = Discount.objects.filter(pk=discount).first()
                    ad.Discount = discount_instance
                except:
                    pass

            elif ad.AdType == "Product":
                try:
                    product_instance = Products.objects.filter(pk=product).first()
                    ad.Product = product_instance
                except:
                    pass
            # if no image mark it as none
            if request.POST.get('image2', ''):
                form.instance.StandardImage = None
            if request.POST.get('image3', ''):
                form.instance.BannerImage = None
            form.save()

            return redirect('list_ad')
        else:
            errors.append(form.errors)

            context = {
                "errors": errors,
                "ad_form": form,
                "promotion_active": "active",
                "ad_data": form.instance.AdType
            }
    else:
        form = AdForm()
        errors = []
        context = {
            "errors": errors,
            "ad_form": form,
            "promotion_active": "active",
            "ad_data": "Discount"
        }
    return render(request, "ad_add.html", context)

# ------------------------------------------- promotions end ...........................................

# ................................................ products start ............................................

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def custom_product_add(request):
    """
    Custom Products  menu on Products section in the side bar will list the custom products.
    adding a Custom Product.We have a Add Custom Product button. clicking on
    the add button will open a form. fields are item_name and item_code,
    item_description, veg_or_non_veg_status, min_size, max_size,
    size_unit, availability.
    Imp: Images are stored separately in CustomProductVideo and CustomProductImage.
    multiple image upload supported
    for validations please refer CustomProductForm


    """
    errors = []

    if request.method == "POST":
        custom_product_form = CustomProductForm(request.POST, request.FILES)
        if custom_product_form.is_valid():
            custom_product = custom_product_form.save()

            # Handle image uploads
            images = request.FILES.getlist('images')
            for image in images:
                CustomProductImage.objects.create(custom_product=custom_product, image=image)

            # Handle video uploads
            videos = request.FILES.getlist('videos')
            for video in videos:
                CustomProductVideo.objects.create(custom_product=custom_product, video=video)

            try:
                # logging operation
                log_user = request.user
                log_type = "product"
                message = "Custom Product Added"
                description = f"Custom Product {custom_product.item_name}  with code {custom_product.item_code} added "
                log(log_user, log_type, message, description)
            except:
                pass

            # After saving, redirect to the listing page
            return redirect('custom_product_listing')  # This line redirects to the product listing page

        else:
            errors.append(custom_product_form.errors)
    else:
        custom_product_form = CustomProductForm()

    context = {
        "errors": errors,
        "custom_product_form": custom_product_form,
        "category_active": "active"
    }

    return render(request, "custom_product_add.html", context)


# Custom Product Listing View
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def custom_product_listing(request):
    """
    Custom Products  menu on Products section in the side bar will list the custom products.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete custom products.
    custom product means, customer can define a product by directly contact
    with navya bakers.
    There is a section in the app to create a custom order. The admin can define the customization
    available products in the custom products section.
    :param request:
    :return:
    """
    # Fetch all the custom products (with pagination if needed)
    product_data = CustomProduct.objects.order_by('-created_date')
    paginator = Paginator(product_data, 6)
    page = request.GET.get("page")
    try:
        product_list_data = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        product_list_data = paginator.get_page(1)
    except EmptyPage:
        product_list_data = paginator.get_page(paginator.num_pages)
    context = {"product_data": product_list_data, "category_active": "active"}
    return render(request, 'custom_product_list.html', context)



# Delete Custom Product View
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delete_custom_product(request, product_id):
    """
    Custom Products  menu on Products section in the side bar will list the custom products.
    Deleting a custom product. for each custom product listed on
    custom product list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the custom product.
    :param request:
    :param product_id:
    :return:
    """
    product = get_object_or_404(CustomProduct, pk=product_id)
    product_data = product

    if request.method == 'POST':
        # Delete the product
        product.delete()
        try:
            # logging operation
            log_user = request.user
            log_type = "product"
            message = "Custom Product deleted"
            description = f"Custom product {product_data.item_name}  with code {product_data.item_code} deleted "
            log(log_user, log_type, message, description)
        except Exception as e:
            logging.error(f"Error on Custom Product Delete(logging): {e}")
            # print(e)
            pass
        return redirect('custom_product_listing')  # Redirect to the listing page after deletion

    return render(request, 'delete_confirmation.html', {'product': product})




@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def custom_product_edit(request, product_id):
    """
    Custom Products  menu on Products section in the side bar will list the custom products.
    Editing a Custom Product. for each custom product listed, we have a edit button. clicking on
    the edit button will open a form. fields are item_name and item_code,
    item_description, veg_or_non_veg_status, min_size, max_size,
    size_unit, availability.
        Imp: Images are stored separately in CustomProductVideo and CustomProductImage.
    multiple image upload supported
    for validations please refer CustomProductForm
    :param request:
    :param product_id:
    :return:
    """
    errors = []
    custom_product = get_object_or_404(CustomProduct, pk=product_id)
    custom_product_form = CustomProductForm(request.POST or None, request.FILES or None, instance=custom_product)

    if request.method == "POST":
        if custom_product_form.is_valid():
            custom_product = custom_product_form.save()

            # Handle image uploads
            images = request.FILES.getlist('images')
            for image in images:
                CustomProductImage.objects.create(custom_product=custom_product, image=image)

            # Handle video uploads
            videos = request.FILES.getlist('videos')
            for video in videos:
                CustomProductVideo.objects.create(custom_product=custom_product, video=video)

            try:
                log_user = request.user
                log_type = "product"
                message = "Custom Product Updated"
                description = f"Product {custom_product.item_name}  with code {custom_product.item_code} updated "
                log(log_user, log_type, message, description)
            except:
                pass

            return redirect('custom_product_listing')  # Redirect to the listing page after saving

        else:
            errors.append(custom_product_form.errors)

    context = {
        "errors": errors,
        "custom_product_form": custom_product_form,
        "custom_product": custom_product
    }

    return render(request, "custom_product_edit.html", context)


@login_required
def delete_custom_image(request, product_id, image_id):
    """
        Custom Products  menu on Products section in the side bar will list the custom products.
        custom product have its associated images and videos which is not listed. when we try to
        add or edit a custom product it will be on form.
        These images and videos are stored in separate table and managed separately.
        access via id.
        Table: CustomProductImage

        We can remove the image with this api.
    :param request:
    :param product_id:
    :param image_id:
    :return:
    """
    image = get_object_or_404(CustomProductImage, id=image_id)
    custom_product_id = image.custom_product.id  # type: ignore
    image.delete()
    try:
        log_user = request.user
        log_type = "product"
        message = "Custom Product Image deleted"
        description = f"Custom product {image.custom_product.item_name}  with code {image.custom_product.item_code} deleted its image"
        LoggingOperation.objects.create(user=log_user, log_type=log_type, message=message, description=description)
    except:
        pass
    return redirect('custom_product_edit', product_id=custom_product_id)


@login_required
def delete_custom_video(request, product_id, video_id):
    """
    Custom Products  menu on Products section in the side bar will list the custom products.
    custom product have its associated images and videos which is not listed. when we try to
    add or edit a custom product it will be on form.
    These images and videos are stored in separate table and managed separately.
    access via id.
    Table: CustomProductVideo
    We can remove the video with this api.

    :param request:
    :param product_id:
    :param video_id:
    :return:
    """
    video = get_object_or_404(CustomProductVideo, id=video_id)
    custom_product_id = video.custom_product.id  # type: ignore

    # Deleting video
    video.delete()

    try:
        # logging operation
        log_user = request.user
        log_type = "product"
        message = "Custom Product Video deleted"
        description = f"Custom product {video.custom_product.item_name}  with code {video.custom_product.item_code} deleted its video"
        log(log_user, log_type, message, description)
    except Exception as e:
        logging.error(f"Custom Product Video Delete(logging): {e}")
        # print(e)
        pass
    return redirect('custom_product_edit', product_id=custom_product_id)


@login_required
def production_unit(request):
    """
    Production unit means where products are manufacturing. Currently only one
    production unit available.
    production units  menu on Sales Unit section in the side bar will list the production unit.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete production unit. Only one production unit could be
    created now. creating more than one production unit is restricted. If no production unit created,
    a add button is available to create one else we hide that button.
    edit and delete button available for the production unit.

    :param request:
    :return:
    """
    production_unit = ProductionUnit.objects.order_by("-created_at")
    paginator = Paginator(production_unit, 10)
    page = request.GET.get("page")
    try:
        production_unit = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        production_unit = paginator.get_page(1)
    except EmptyPage:
        production_unit = paginator.get_page(paginator.num_pages)
    context = {"production_unit_data": production_unit, "sales_unit_active": "active"}
    return render(request, "production_unit_list.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def production_unit_add(request):
    """
    production units  menu on Sales Unit section in the side bar will list the production unit.
    adding a Production unit.We have a Add Production Unit button. clicking on
    the add button will open a form. fields are pu_name, pu_code, pu_location, street, city
    district, state_or_province, pin_code, latitude, longitude, gst, contact_no, email, status

    *NOt implemented yet (Only one production unit, not checking the status)
    We can change the status of pu by open, close or currently not accepting any order. it is by
    changing the status field.


    :param request:
    :return:
    """
    errors = []
    if request.method == "POST":
        production_unit_form = ProductionUnitForm(request.POST)
        if production_unit_form.is_valid():
            production_unit_form.save()
            return redirect("production_unit_list")
        else:
            errors.append(production_unit_form.errors)
            context = {
                "errors": errors,
                "production_unit_form": production_unit_form,
                "show_modal": "modal_hide",
                "sales_unit_active": "active"
            }
            return render(request, "production_unit_add.html", context)
    else:
        production_unit_form = ProductionUnitForm()
        errors = []
    context = {
        "errors": errors,
        "production_unit_form": production_unit_form,
        "sales_unit_active": "active"
    }
    return render(request, "production_unit_add.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def production_unit_delete(request, pu_id):
    """
    production units  menu on Sales Unit section in the side bar will list the production unit.
    Deleting a production unit. for each production unit listed on
    production unit list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the production unit.
    :param request:
    :param pu_id:
    :return:
    """
    pu = ProductionUnit.objects.filter(pk=pu_id)
    if pu:
        pu.delete()
    return redirect("production_unit_list")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def production_unit_edit(request, pu_id=None):
    """
    production units  menu on Sales Unit section in the side bar will list the production unit.
    Editing a production unit. for each production unit listed, we have a edit button. clicking on
    the edit button will open a form. fields are pu_name, pu_code, pu_location, street, city
    district, state_or_province, pin_code, latitude, longitude, gst, contact_no, email, status
    :param request:
    :param pu_id:
    :return:
    """
    pu = get_object_or_404(ProductionUnit, pk=pu_id)
    errors = []
    if request.method == "POST":
        pu_form = ProductionUnitForm(request.POST, instance=pu)
        if pu_form.is_valid():
            pu_form.save()
            return redirect("production_unit_list")
        else:
            errors.append(pu_form.errors)
    else:
        pu_form = ProductionUnitForm(instance=pu)
    context = {"pu_form": pu_form, "sales_unit_active": "active"}
    return render(request, "production_unit_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def faq(request):
    """
    Faq's are the possible questions raised by the user.
    Faq  menu on Settings in the side bar will list the Faq.
    It is only available for super admin and nbc admin.
    nbc admin can create, edit and delete faq.
    add button is available to create.
    edit and delete button available for each faq listed.
    This created faq will render on the faq section on app.

    :param request:
    :return:
    """
    faqs = FAQ.objects.all()
    return render(request, "faq.html", {'faqs': faqs})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def add_faq(request):
    """
    Faq  menu on Settings in the side bar will list the Faq.
    adding a Faq.We have a Add Faq button. clicking on
    the add button will open a form. fields are category, question,
    answer
    :param request:
    :return:
    """
    if request.method == 'POST':
        form = FAQForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('faq')
        else:
            messages.error(request, 'There was an error with your form submission.')
    else:
        form = FAQForm()

    return render(request, 'add_faq.html', {'form': form})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def edit_faq(request, faq_id):
    """
    Faq  menu on Settings in the side bar will list the Faq.
    editing a Faq.We have a edit Faq button for each faq listed. clicking on
    the edit button will open a form. fields are category, question,
    answer
    :param request:
    :param faq_id:
    :return:
    """
    faq = get_object_or_404(FAQ, id=faq_id)
    if request.method == 'POST':
        form = FAQForm(request.POST, instance=faq)
        if form.is_valid():
            form.save()
            return redirect('faq')
        else:
            messages.error(request, 'There was an error with your form submission.')
    else:
        form = FAQForm(instance=faq)

    return render(request, 'edit_faq.html', {'form': form, 'faq': faq})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delete_faq(request, faq_id):
    """
    Faq  menu on Settings in the side bar will list the Faq.
    deleting a Faq.We have a delete Faq button for each faq listed. clicking on
    the delete button will open a form. fields are category, question,
    answer
    :param request:
    :param faq_id:
    :return:
    """
    faq = get_object_or_404(FAQ, id=faq_id)
    faq.delete()
    return redirect('faq')

    # return render(request, 'delete_faq.html', {'faq': faq})


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def about_us(request):
    """
    About us describe about the Navya backers,
    Where there shops, contact details, app url,
    privacy polices, terms and conditions and what is their motto etc......

    each of this details are handled separately.
    we can edit each of this details.

    :param request:
    :return:
    """
    about_us = AboutUs.objects.first()
    contact_us = ContactUs.objects.first()
    app_url = AppUrl.objects.first()
    privacy_policy = PrivacyPolicy.objects.first()
    terms_and_conditions = TermsAndConditions.objects.first()
    if request.method == 'POST':
        if 'edit_about_us' in request.POST:
            about_us_form = AboutUsForm(request.POST, request.FILES,
                                        instance=about_us)  # Include request.FILES for image upload
            if about_us_form.is_valid():
                about_us_form.save()
        elif 'edit_contact_us' in request.POST:
            contact_us_form = ContactUsForm(request.POST, instance=contact_us)
            if contact_us_form.is_valid():
                contact_us_form.save()
        elif 'edit_app_url' in request.POST:
            app_url_form = AppUrlForm(request.POST, instance=app_url)
            if app_url_form.is_valid():
                app_url_form.save()
        elif 'edit_privacy_policy' in request.POST:
            privacy_policy_form = PrivacyPolicyForm(request.POST, instance=privacy_policy)
            if privacy_policy_form.is_valid():
                privacy_policy_form.save()
        elif 'edit_terms_conditions' in request.POST:
            terms_conditions_form = TermsAndConditionsForm(request.POST, instance=terms_and_conditions)
            if terms_conditions_form.is_valid():
                terms_conditions_form.save()

        return redirect('about_us')  # After saving, redirect to the page again

    # Create forms with current data
    about_us_form = AboutUsForm(instance=about_us)
    contact_us_form = ContactUsForm(instance=contact_us)
    app_url_form = AppUrlForm(instance=app_url)
    privacy_policy_form = PrivacyPolicyForm(instance=privacy_policy)
    terms_conditions_form = TermsAndConditionsForm(instance=terms_and_conditions)

    return render(request, 'about_us.html', {
        'about_us': about_us,
        'contact_us': contact_us,
        'app_url': app_url,
        'privacy_policy': privacy_policy,
        'terms_and_conditions': terms_and_conditions,
        'about_us_form': about_us_form,
        'contact_us_form': contact_us_form,
        'app_url_form': app_url_form,
        'privacy_policy_form': privacy_policy_form,
        'terms_conditions_form': terms_conditions_form,
    })


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def privacy_policy_view(request):
    """
    In about us section we have the privacy policy view, in addition to that
    we have a separate menu on the settings, Privacy Policy. on clicking
    it will list current privacy policy, we have edit button on the list.
    by clicking edit we can edit the current privacy policy.

    :param request:
    :return:
    """
    policy = PrivacyPolicy.objects.first()
    form = PrivacyPolicyForm(request.POST or None, instance=policy)

    if request.method == 'POST' and form.is_valid():
        form.save()
        return redirect('privacy_policy')

    return render(request, 'privacy_policy.html', {
        'form': form,
        'policy': policy,
    })

@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def terms_conditions_view(request):
    """
        In about us section we have the Terms and condition view, in addition to that
    we have a separate menu on the settings, Terms and condition. on clicking
    it will list current terms and condition, we have edit button on the list.
    by clicking edit we can edit the current terms and condition.

    :param request:
    :return:
    """
    terms = TermsAndConditions.objects.first()
    form = TermsAndConditionsForm(request.POST or None, instance=terms)

    if request.method == 'POST' and form.is_valid():
        form.save()
        return redirect('terms_and_conditions')

    return render(request, 'terms_conditions.html', {
        'form': form,
        'terms': terms,
    })


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def delivery_settings(request):
    """
    * Not implemented yet........
    :param request:
    :return:
    """
    return render(request, "delivery_settings.html")


#


@login_required
@user_passes_test(check_user_role(["Shop Admin", "PU Admin"]))
def custom_orders(request, order_id=None):
    try:
        platform = request.GET.get('platform')
        shop_id_verify = Shop.objects.prefetch_related('unit_admin_user').filter(
            unit_admin_user__uuid=request.user.uuid)
        shop_id = shop_id_verify[0]
        errors = []
        field_name = request.COOKIES.get('field_name')
        sort_order = request.COOKIES.get('sort_order')
        search_query = request.GET.get('search', "")
        order_date = request.GET.get('product_date', "")
        order_status = request.GET.get('product_status', "")
        sort_order_data = request.GET.get('sort_order', "")
        if not sort_order_data:
            sort_order_data = sort_order
        field_name_data = request.GET.get('field_name', "")
        if not field_name_data:
            field_name_data = field_name
        order_list = Orders.objects.filter(store_uuid=shop_id, order_type="Custom Orders").exclude(
            order_status="New Order").order_by('-created_date')
        if platform:
            if platform == 'web':
                order_list  = order_list.filter(Q(platform ="Web")|Q(platform= "Other"))
            else:
                order_list = order_list.filter(Q(platform ="IOS")|Q(platform= "Andriod"))

        if request.headers.get('x-requested-with') == 'XMLHttpRequest':
            if search_query:
                order_list = order_list.filter(
                    Q(order_ID__icontains=search_query) |
                    Q(order_type__icontains=search_query) |
                    Q(order_status__icontains=search_query) |
                    Q(user_uuid__first_name__icontains=search_query)
                ).order_by('-created_date')
            if order_date:
                formatted_date = datetime.strptime(order_date, "%Y-%m-%d")
                order_list = order_list.filter(created_date__date=formatted_date)
            if order_status:
                order_list = order_list.filter(product_status=order_status)

            if sort_order_data and field_name_data:
                if sort_order_data == 'asc':
                    if field_name_data == 'user_uuid':
                        order_list = order_list.order_by(field_name_data + '__first_name')
                    else:
                        order_list = order_list.order_by(field_name_data)
                else:
                    if field_name_data == 'user_uuid':
                        order_list = order_list.order_by('-' + field_name_data + '__first_name')
                    else:
                        order_list = order_list.order_by('-' + field_name_data)

        paginator = Paginator(order_list, 6)

        page = request.GET.get('page')

        try:
            orders = paginator.get_page(page)
        except (PageNotAnInteger, TypeError):
            orders = paginator.get_page(1)
        except EmptyPage:
            orders = paginator.get_page(paginator.num_pages)

        base_url = reverse('custom_orders')
        query_params = request.GET.copy()
        if 'page' in query_params:
            del query_params['page']
        pagination_links = [
            {
                'page_number': page_number,
                'url': f"{base_url}?{query_params.urlencode(safe='/')}&page={page_number}",
            }
            for page_number in orders.paginator.page_range
        ]

        form = CustomOrderForm()
        ongoing_orders = DeliveryBoys.objects.filter(status='verified', shop=shop_id)
        context = {
            "order_form": form,
            "order_data": orders,
            'search_query': search_query,
            'date': order_date,
            'product_status': order_status,
            'pagination_links': pagination_links,
            "orders_active": "active",
            "ongoing_orders": ongoing_orders,
            "platform":platform

        }
        if request.headers.get('x-requested-with') == 'XMLHttpRequest':
            tbody = render_to_string('custom_order_list_page.html',
                                     {"order_data": orders, 'pagination_links': pagination_links,
                                      "ongoing_orders": ongoing_orders, "platform":platform})
            pagination = render_to_string('order_list_pagination.html', {"order_data": orders})
            return JsonResponse({'tbody': tbody, "pagination": pagination})

        response = render(request, "custom_orders.html", context)
        return response
    #
    except IndexError:
        context = {
            "order_data": [],
            "orders_active": "active"

        }
        return render(request, "custom_orders.html", context)


@login_required
@user_passes_test(check_user_role(["Shop Admin"]))
def custom_order_edit(request):
    if request.method == "POST":
        orderID = request.POST.get("order_ID")  # Using .get() to avoid KeyError
        custom_orders = Orders.objects.filter(order_ID=orderID).first()

        if not custom_orders:
            return JsonResponse({'status': 'fail', 'message': 'Order not found'}, status=404)

        form = CustomOrderForm(request.POST, instance=custom_orders)

        if form.is_valid():
            grand_total = request.POST.get("grand_total", "0")  # Ensure value exists
            form.save()
            try:
                # razorpay_client = razorpay.Client(auth=(settings.RAZORPAY_KEY_ID, settings.RAZORPAY_KEY_SECRET))
                # razorpay_order = razorpay_client.payment_link.create({
                #     "upi_link": True,
                #     "amount": grand_total,
                #     "currency": "INR",
                #     "description": "For custom order payment purpose",
                #     "customer": {
                #         "name": f"{request.user.first_name} {request.user.last_name}",
                #         "email": request.user.email,
                #         "contact": request.user.phone_number
                #     },
                #     "notify": {"sms": True, "email": True},
                #     "reminder_enable": True,
                #     "notes": {"orderID": orderID}
                # })

                # Payment.objects.create(
                #     order=custom_orders,
                #     razorpay_order_id=razorpay_order["id"],
                #     payment_status="pending",  # payment status pending
                # )
                pass


            except Exception as e:
                form.add_error("order_ID", "Payment link creation failed!")
                return JsonResponse({'status': 'fail', 'errors': form.errors})

            # **Ensure Order Status is updated to "Bill Created"**
            custom_orders.order_status = "Bill Created"
            custom_orders.save()  # Save the updated status

            title = "Bill Created"
            description = f"Bill Created for your custom order {orderID}"
            NotificationInit(custom_orders.user_uuid.fcm_token, custom_orders, title, description, custom_orders.user_uuid)
            EmailSend(title, description, [custom_orders.user_uuid.email], False)
            WhatsappMessage(custom_orders.user_uuid.phone_number, "Navya Bakers", title, description)
            # try:
            #     initialize_fcm_app()
            #     send_notification.s(custom_orders.user_uuid.fcm_token, custom_orders, title, description,
            #                       custom_orders.user_uuid)
            # except Exception as e:
            #     logging.error(f"Bill Created Notification Send Error: {e}")
            #     # print(e)
            #
            #     pass

            return JsonResponse({
                'status': 'success',
                'order_id': custom_orders.uuid,
                'OrderID': custom_orders.order_ID
            })
        # return redirect('custom_orders')
        return JsonResponse({'status': 'fail', 'errors': form.errors})

    elif request.method == "GET":
        order_id = request.GET.get('order_id')
        custom_orders = Orders.objects.filter(uuid=order_id).first()

        if custom_orders:
            form = CustomOrderForm(instance=custom_orders)
        else:
            form = CustomOrderForm()

        form_data = render_to_string('order_status_update_form.html', {'order_form': form, "order_id": order_id,
                                                                       'order_status': custom_orders.order_status})
        return JsonResponse({'success': True, 'form': form_data})

    return JsonResponse({'status': 'fail', 'message': 'Invalid request method'}, status=400)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def system_settings(request):
    return render(request, "system_settings.html")


@csrf_exempt
async def custom_order_payment(request):
    if request.method == "POST":
        try:
            data = await request.json()
            body = request.body.decode('utf-8')
            received_signature = request.headers.get("X-Razorpay-Signature")

            # Compute HMAC SHA256 signature
            generated_signature = hmac.new(
                RAZORPAY_WEBHOOK_SECRET.encode(),
                body.encode(),
                hashlib.sha256
            ).hexdigest()

            # Compare signatures
            if hmac.compare_digest(generated_signature, received_signature):
                event_data = json.loads(body)  # Parse JSON data
                orderID = data["notes"].get("orderID", "")
                if orderID:
                    custom_orders = Orders.objects.filter(order_ID=orderID).first()
                    custom_orders.order_status = "Confirmed"
                    custom_orders.save()
                    Payment.objects.create(
                        order=custom_orders,
                        razorpay_order_id=data["order_id"],
                        payment_status="paid",  # payment status pending
                    )

                return JsonResponse({"status": "success"}, status=200)
            else:
                return JsonResponse(
                    {"status": 0, "message": "Payment verification failed"}, status=400
                )
        except Exception as e:
            return JsonResponse({
                "status": 0, "message": "Something went wrong!", "error": str(e)
            }, status=400)

    else:
        return JsonResponse({'status': 'fail', 'errors': "Method not allowed"}, status=403)


def choice_list(request):
    choice = request.GET.get('choice')

    if choice == 'Discount':
        discount_choice = [{"id": i.id, "name": i.DiscountName} for i in Discount.objects.all()]
        try:
            if discount_choice:
                return JsonResponse({"tables": list(discount_choice), "type": choice},
                                    safe=False)
            else:
                return JsonResponse({"tables": [], "type": choice}, safe=False)

        except StopIteration:
            return JsonResponse({"tables": [], "type": choice}, safe=False)

    if choice == "Coupon":
        coupon_choice = [{"id": i.id, "name": i.CouponName} for i in Coupons.objects.all()]
        try:
            if coupon_choice:
                return JsonResponse({"tables": list(coupon_choice), "type": choice},
                                    safe=False)
            else:
                return JsonResponse({"tables": [], "type": choice}, safe=False)

        except StopIteration:
            return JsonResponse({"tables": [], "type": choice}, safe=False)

    if choice == 'Product':

        product_choice = [{"id": i.id, "name": i.item_name} for i in Products.objects.all()]
        try:
            if product_choice:
                return JsonResponse({"tables": list(product_choice), "type": choice},
                                    safe=False)
            else:
                return JsonResponse({"tables": [], "type": choice}, safe=False)

        except StopIteration:
            return JsonResponse({"tables": [], "type": choice}, safe=False)


def my_polling_task(request):
    # Add your polling logic here, such as fetching data from an API
    today = date.today()
    orders = Orders.objects.filter(order_status__in=["Confirmed", "Viewed", "Order Packed", "Failed"])
    # print(f"Orders : {orders}")
    status_excluded = ["Delivery Assigned", "Despatched", "Contacted", "Bill Created", "Delivered", "Failed"]
    for i in orders:
        try:
            if (i.delivery_slot_time and i.delivery_slot_time):
                combined_datetime = datetime.combine(i.delivery_slot_date, i.delivery_slot_time)
                time_dif_with_del_slot = combined_datetime.astimezone() - datetime.now().astimezone()
                duration_in_s_data = time_dif_with_del_slot.total_seconds()
                hours_del = duration_in_s_data / 3600
                # print(f"Hours  : {hours_del}")

            else:
                hours_del = 0
            if i.order_status == 'Confirmed' or i.order_status == 'Viewed':

                if hours_del < 2:

                    if i.order_status != 'Order Packed' and i.order_status not in status_excluded:
                        i.color_status = "Yellow"
                        i.color_status_updation_time = datetime.now()
                        i.save()

                if hours_del <= 0:
                    if i.order_status not in status_excluded:
                        i.color_status = "Red"
                        i.color_status_updation_time = datetime.now()
                        i.save()

                if hours_del < 1 and hours_del > 0:
                    if i.order_status not in status_excluded:
                        i.color_status = "Orange"
                        i.color_status_updation_time = datetime.now()
                        i.save()

                if hours_del > 2:
                    i.color_status = "White"
                    i.color_status_updation_time = datetime.now()
                    i.save()


            if i.order_status == 'Order Packed':

                if hours_del <= 0:
                    if i.order_status not in status_excluded:
                        i.color_status = "Red"
                        i.color_status_updation_time = datetime.now()
                        i.save()

                if hours_del < 1 and hours_del > 0:
                    if i.order_status not in status_excluded:
                        i.color_status = "Orange"
                        i.color_status_updation_time = datetime.now()
                        i.save()

                if hours_del > 2:
                    i.color_status = "White"
                    i.color_status_updation_time = datetime.now()
                    i.save()


            if i.order_status == 'Failed':
                i.color_status = "Dark Red"
                i.color_status_updation_time = datetime.now()
                i.save()
        except:
            if i.order_status == "Failed":
                i.color_status = "Dark Red"
                i.color_status_updation_time = datetime.now()
                i.save()
            pass

    return JsonResponse({"Status": True})


def list_communication(request):
    msg = Communication.objects.select_related("user", "message").prefetch_related("message__ads").order_by(
        "-created_at")
    paginator = Paginator(msg, 6)
    page = request.GET.get("page")
    msg_list_data = paginator.get_page(page)

    context = {"msg_data": msg_list_data, "promotion_active": "active"}
    return render(request, "communication_list.html", context)


def send_message(request):
    if request.method == 'POST':
        form = MessageForm(request.POST)
        errors = []
        if form.is_valid():
            msg_form = form.save()
            customers = Users.objects.filter(user_type="Customer")

            for customer in customers:
                if customer.email and customer.phone_number:
                    if msg_form.message_type == 'sms':
                        try:
                            smsmsg = Message.objects.filter(message_type='email').order_by("-created_at").first()
                            sms.apply_async(
                                args=[f"Dear {customer.first_name},\nSurpricing offer for you from Navya🎉 \n{smsmsg.message}\n{smsmsg.ads.AdDescription}",
                                "+919961073407"],
                                countdown=5)
                            context = {"message": "Verification link sent to your phone number.", 'status': 1,
                                       "send": 1}
                            # send_sms(f"Navya Bakers Offer",customer.phone_number) #phone_number must contain+91
                            Communication.objects.create(user=customer, message=msg_form)
                        except Exception as e:
                            logging.error(f"SMS Send Error: {e}")
                            # print("SMS Error:", e)

                    elif msg_form.message_type == 'email':
                        try:
                            # send_email_communication(msg_form, customer.email)
                            emailmsg = Message.objects.filter(message_type='email').order_by("-created_at").first()
                            # print(emailmsg,emailmsg.message,emailmsg.ads.AdTitle,emailmsg.ads.AdDescription)
                            subject = emailmsg.ads.AdTitle
                            email_body = f"Dear {customer.first_name},\nwe are providing\n{emailmsg.message}\n{emailmsg.ads.AdDescription}"
                            to_email = [customer.email]
                            media_root = settings.MEDIA_ROOT
                            filepath = os.path.join(media_root, str(emailmsg.ads.BannerImage))

                            send_email_attachment(subject, email_body, to_email, filepath)
                            Communication.objects.create(user=customer, message=msg_form)
                        except Exception as e:
                            # print("Email Error:", e)
                            logging.error(f"Email Send Error on Communication: {e}")

                    else:
                        try:
                            # send_whatsapp_message(msg_form, customer.phone_number)
                            send_whatsapp_message("+917012951573", msg_form.ads.AdTitle, customer.first_name,
                                                  msg_form.ads.AdDescription)  # 7994035508
                            Communication.objects.create(user=customer, message=msg_form)
                        except Exception as e:
                            logging.error(f"Whatsapp Send Error on Communication: {e}")
                            # print("WhatsApp Error:", e)

            return redirect('communication_list')
        else:
            errors.append(form.errors)

    else:
        form = MessageForm()
    return render(request, 'send_message.html', {'form': form})


# def send_email_communication(msg, to_email):
#     subject = msg.ads.AdTitle
#     message = f"{msg.message} {msg.ads.AdDescription}"
#     to_email = [to_email]  # Wrap in a list

#     send_mail(
#         subject=subject,
#         message=message,
#         from_email=settings.EMAIL_HOST_USER,
#         recipient_list=to_email,
#         fail_silently=False,
#     )
#     print("Email Sent Successfully!")

def send_email_attachment(subject, body, to_email, filepath):
    mail = EmailMessage(subject=subject, body=body, from_email=settings.EMAIL_HOST_USER, to=to_email)
    mail.attach_file(filepath)
    mail.send()







@api_view(['POST'])
def coupon_subcategory_list(request):
    try:

        category_ids = request.data.get('category', None)
        sub_category_ids = request.data.get('sub_category', None)
        product_ids = request.data.get('product', None)
        sku_ids = request.data.get('sku', None)
        trigger = request.data.get('trigger', None)
        if '' in category_ids:
            category_ids.remove('')
        if '' in sub_category_ids:
            sub_category_ids.remove('')
        if '' in product_ids:
            product_ids.remove('')
        if '' in sku_ids:
            sku_ids.remove('')

        if trigger == "category":
            categories = ProductCategory.objects.values('uuid', 'category_name')
            for i in categories:
                if str(i["uuid"]) in category_ids:
                    i["checked"] = "True"
                else:
                    i["checked"] = "False"
            subcategories = ProductSubCategory.objects.filter(category_id__in=category_ids).values('uuid',
                                                                                                   'sub_category_name')

            for j in subcategories:
                if str(j["uuid"]) in sub_category_ids:
                    j["checked"] = "True"
                else:
                    j["checked"] = "False"
            products = Products.objects.filter(item_category__in=category_ids).values('id', 'item_name')

            for k in products:
                if str(k["id"]) in product_ids:
                    k["checked"] = "True"
                else:
                    k["checked"] = "False"

            sku = SKU.objects.filter(product__item_category__in=category_ids).values('id', 'sku_name')

            for l in sku:
                if str(l["id"]) in sku_ids:
                    l["checked"] = "True"
                else:
                    l["checked"] = "False"

            return JsonResponse(
                {"categories": list(categories), "sub_categories": list(subcategories), "products": list(products),
                 "sku": list(sku), "trigger": trigger}, safe=False)

        elif trigger == "sub_category":
            categories = ProductCategory.objects.values('uuid', 'category_name')
            for i in categories:
                if str(i["uuid"]) in category_ids:
                    i["checked"] = "True"
                else:
                    i["checked"] = "False"
            subcategories = ProductSubCategory.objects.filter(category_id__in=category_ids).values('uuid',
                                                                                                   'sub_category_name')

            for j in subcategories:
                if str(j["uuid"]) in sub_category_ids:
                    j["checked"] = "True"
                else:
                    j["checked"] = "False"
            products = Products.objects.filter(item_sub_category__in=sub_category_ids).values('id', 'item_name')

            for k in products:
                if str(k["id"]) in product_ids:
                    k["checked"] = "True"
                else:
                    k["checked"] = "False"

            sku = SKU.objects.filter(product__item_sub_category__in=sub_category_ids).values('id', 'sku_name')

            for l in sku:
                if str(l["id"]) in sku_ids:
                    l["checked"] = "True"
                else:
                    l["checked"] = "False"

            return JsonResponse(
                {"categories": list(categories), "sub_categories": list(subcategories), "products": list(products),
                 "sku": list(sku), "trigger": trigger}, safe=False)

        elif trigger == "product":
            categories = ProductCategory.objects.values('uuid', 'category_name')
            for i in categories:
                if str(i["uuid"]) in category_ids:
                    i["checked"] = "True"
                else:
                    i["checked"] = "False"
            subcategories = ProductSubCategory.objects.filter(category_id__in=category_ids).values('uuid',
                                                                                                   'sub_category_name')

            for j in subcategories:
                if str(j["uuid"]) in sub_category_ids:
                    j["checked"] = "True"
                else:
                    j["checked"] = "False"
            products = Products.objects.filter(item_sub_category__in=sub_category_ids).values('id', 'item_name')
            for k in products:
                if str(k["id"]) in product_ids:
                    k["checked"] = "True"
                else:
                    k["checked"] = "False"

            sku = SKU.objects.filter(product_id__in=product_ids).values('id', 'sku_name')

            for l in sku:
                if str(l["id"]) in sku_ids:
                    l["checked"] = "True"
                else:
                    l["checked"] = "False"

            return JsonResponse(
                {"categories": list(categories), "sub_categories": list(subcategories), "products": list(products),
                 "sku": list(sku), "trigger": trigger}, safe=False)


    except Exception as e:
        return JsonResponse({"status": False, "error": str(e)}, safe=False)


@api_view(['POST'])
def order_status_update_to_delivered(request, orderID, status):
    if request.method == 'POST':
        try:
            order = Orders.objects.filter(order_ID=orderID).first()
            if order.order_status == 'Delivery Assigned':
                if status == "Delivered":
                    order.order_status = 'Delivered'
                elif status == "Failed":
                    order.order_status = "Failed"
                else:
                    return JsonResponse({"status": 0, "msg": "Status change not allowed"})
                order.save()
            elif order.order_status == "Failed":
                return JsonResponse({"status": 0, "msg":"Status already updated to Failed"})

            elif order.order_status == "Delivered":
                return JsonResponse({"status": "success", "msg": "Status already updated to Delivered"})
            else:
                return JsonResponse({"status": 0, "msg": "Status change not allowed"})
                # send notification

            title = "Order Delivered"
            description = f"Your Order {orderID} Delivered!"
            NotificationInit(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
            EmailSend(title, description, [order.user_uuid.email], False)
            WhatsappMessage(order.user_uuid.phone_number, "Navya Bakers", title, description)
            # try:
            #     initialize_fcm_app()
            #     send_notification.s(order.user_uuid.fcm_token, order, title, description, order.user_uuid)
            # except Exception as e:
            #     logging.error(f"Notification Send Failed on Order Delivered with link: {e}")
            #     # print(e)
            #
            #     pass
            return JsonResponse({'status': 1, "msg": f"Status of your order changes to {status}"})
        except Exception as e:
            return JsonResponse({'status': 0, 'msg': "something went wrong", 'error': str(e)})
    else:
        return JsonResponse({'status': 1, 'msg': "method not allowed"})


# ...................................... start product .......................................................

@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def you_may_also_like_list(request):
    """
    Liked List menu on Product section in the side bar will list the You may also like list.
    It is only available for NBC admin.
    from Liked list we can add a product and we can map product related to that product.
    if one person orders a jam, liked products will contain bread it will be defined in
    the liked list

    :param request:
    :return:

    """
    liked_data = YouMayAlsoLike.objects.order_by("-created_at")
    paginator = Paginator(liked_data, 6)
    page = request.GET.get("page")
    try:
        data = paginator.get_page(page)
    except (PageNotAnInteger, TypeError):
        data = paginator.get_page(1)
    except EmptyPage:
        data = paginator.get_page(paginator.num_pages)
    context = {"like_list": data, "category_active": "active"}
    return render(request, "people_also_like_list.html", context)


@require_POST
@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def you_may_also_like_delete(request, id):
    """
    Liked List menu on Product section in the side bar will list the You may also like list.
    Deleting a Liked Data. for each liked data listed on
    liked list, we have a delete button. clicking on
    the delete button will open a confirm dialog box. if you confirm
    it will delete the liked data.
    """
    you_may_also_like_data = YouMayAlsoLike.objects.filter(pk=id)
    if you_may_also_like_data:
        you_may_also_like_data.delete()
    return redirect("you-may-also-like")


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def you_may_also_like_edit(request, id):
    """
    Liked List menu on Product section in the side bar will list the You may also like list.
    Editing a Liked data. for each liked data listed, we have a edit button. clicking on
    the edit button will open a form. fields are product and liked products.

    """
    errors = []
    you_may_also_like_data = get_object_or_404(YouMayAlsoLike, pk=id)
    request_post = request.POST.copy()
    # if select all button selected we will get a empty string
    # if empty string exists we have to remove it else will raise error
    if '' in request_post.getlist('liked_products', []):
        liked_product_list  = request_post.getlist('liked_products', [])
        if liked_product_list:
            liked_product_list.remove('')
        request_post.setlist('liked_products', liked_product_list)
    if request.method == "POST":
        form = YouMayAlsoLikeForm(request_post, instance=you_may_also_like_data)
        if form.is_valid():
            form.save()
            return redirect("you-may-also-like")

        else:
            errors.append(form.errors)
    else:
        form = YouMayAlsoLikeForm(instance=you_may_also_like_data)
    context = {"like_form": form, "category_active": "active"}
    return render(request, "people_also_like_edit.html", context)


@login_required
@user_passes_test(check_user_role(["Super Admin", "NBC Admin"]))
def you_may_also_like_add(request):
    """
    Adding a liked product.
    Liked List menu on Product section in the side bar will list the You may also like list.
    clicking on add Liked list will open the form. fields are product and liked products.
    on submission create a liked product data.
    """
    errors = []
    # if couldn't update the request so we make a copy and update
    request_post = request.POST.copy()
    # if select all button selected we will get a empty string
    # if empty string exists we have to remove it else will raise error
    if '' in request_post.getlist('liked_products', []):
        liked_product_list  = request_post.getlist('liked_products', [])
        if liked_product_list:
            liked_product_list.remove('')
        request_post.setlist('liked_products', liked_product_list)
    if request.method == "POST":
        form = YouMayAlsoLikeForm(request_post)
        if form.is_valid():
            form.save()
            return redirect("you-may-also-like")
        else:
            errors.append(form.errors)
            context = {
                "errors": errors,
                "like_form": form,
                "category_active": "active"
            }
            return render(request, "people_also_like_add.html", context)
    else:
         form = YouMayAlsoLikeForm()

    context = {
        "errors": errors,
        "like_form": form,
        "category_active": "active"

    }
    return render(request, "people_also_like_add.html", context)



def delivery_settings_view(request):

    """
    delivery settings.
    delivery settings menu on Settings section in the side bar will list Delivery setting.
    delivery fee configuration, current delivery fee discount, add delivery discount option are
    listed on the delivery settings.
    :param request:
    :return:
    """
    settings = DeliverySettings.objects.first()
    if not settings:
        settings = DeliverySettings.objects.create()  # create default if missing

    discounts = DeliveryDiscount.objects.all().order_by('min_order_value')

    if request.method == 'POST':

        # delivery fee configuration
        if 'save_settings' in request.POST:
            settings_form = DeliverySettingsForm(request.POST, instance=settings)
            if settings_form.is_valid():
                settings_form.save()
                return redirect('delivery_settings')

        # add delivery fee discount
        if 'add_discount' in request.POST:
            discount_form = DeliveryDiscountForm(request.POST)
            if discount_form.is_valid():
                discount_form.save()
                return redirect('delivery_settings')

        # delete delivery fee discount
        if 'delete_discount' in request.POST:
            discount_id = request.POST.get('discount_id')
            DeliveryDiscount.objects.filter(id=discount_id).delete()
            return redirect('delivery_settings')

    else:
        settings_form = DeliverySettingsForm(instance=settings)
        discount_form = DeliveryDiscountForm()

    context = {
        'settings_form': settings_form,
        'discount_form': discount_form,
        'delivery_discounts': discounts,
    }

    return render(request, 'delivery_settings.html', context)



def coupon_settings_view(request):
    """
    custom coupon settings.
    Custom Coupon settings menu on Promotions section in the side bar will list Custom coupon list.
    if a customer buys a product between a price range and there is a
    coupon for that price range. A coupon code will send through the email, whatsapp or sms.
    settings view for entering the price range is here

    :param request:
    :return:
    """
    coupon_settings = CustomCouponSettings.objects.first()
    if not coupon_settings:
        coupon_settings = CustomCouponSettings.objects.create()  # create default if missing

    # there is only instance for the same
    # we can update the data
    if request.method == 'POST':
        form = CustomCouponSettingsForm(request.POST, instance = coupon_settings)
        if form.is_valid():
            form.save()
            return redirect("coupon-settings")
    else:
        form = CustomCouponSettingsForm(instance=coupon_settings)

    context = {
        'coupon_form': form,
    }

    return render(request, 'custom_coupon_setting.html', context)
