import io
import os
import secrets
import zipfile
from io import BytesIO
from tempfile import TemporaryDirectory
from PIL import Image, ImageDraw, ImageFont
from datetime import datetime
import qrcode
import random
import string
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db import IntegrityError, transaction
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from reportlab.lib.pagesizes import A4, letter
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
from rest_framework.decorators import api_view

from accounts.models import Customers, CompanyUser, Batch, Company
from qr_manager.models import Category


# BASE_URL = "https://rescuescanner.in/"

def is_id_unique_in_database(new_id):  # internal function
    return Customers.objects.filter(qr_code=new_id).count() == 0


def generate_unique_ids(length, count):  # internal function
    if length is None:
        length = 5

    if count is None:
        count = 1

    characters = string.ascii_letters + string.digits
    unique_ids = set()

    while len(unique_ids) < count:
        new_id = ''.join(secrets.choice(characters) for _ in range(length))
        if is_id_unique_in_database(new_id):
            unique_ids.add(new_id)

    return list(unique_ids)


# old function is here
@require_POST
@login_required
def generate_qr_code(request):
    try:
        try:
            BASE_URL = request.user.company.redirect_url+"/"
        except Exception as e:
            print(e,"exception")
            BASE_URL = "http://rescuescanner.in/"
        length = int(request.POST.get('length', 6))
        count = int(request.POST.get('count', 1))
        category_id = request.POST.get('category', None)
        if category_id:
            category = Category.objects.get(pk=category_id)
        color = request.POST.get('color', "black")
        bgcolor = request.POST.get('bgcolor', "transparent")
        download_option = request.POST.get('download_option', "pdf")
        batch_name=request.POST.get('batch_name',None)

        if not category:
            messages.error(request, "Please select a category", extra_tags="danger")
            return redirect("/qr_generator")

        if not color:
            messages.error(request, "Please select a color", extra_tags="danger")
            return redirect("/qr_generator")

        if not bgcolor:
            messages.error(request, "Please select a background color", extra_tags="danger")
            return redirect("/qr_generator")

        if color == bgcolor:
            messages.error(request, "Color and background color cannot be the same", extra_tags="danger")
            return redirect("/qr_generator")

    except ValueError:
        messages.error(request, "Invalid length or count value", extra_tags="danger")
        return redirect("/qr_generator")

    try:
        unique_hash_codes = generate_unique_ids(length, count)
    except Exception as e:
        messages.error(request, f"Error generating unique IDs: {str(e)}", extra_tags="danger")
        return redirect("/qr_generator")

    user = request.user
    try:
        new_batch = Batch.objects.create(created_by=user, count=count, category=category, color=color,
                                         bgcolor=bgcolor)

        if batch_name:
            new_batch.batch_name=batch_name
        new_batch.save()
        new_batch.qr_generation_price = int(new_batch.count) * int(user.company.cost_per_qr_code)
        new_batch.save()
        if new_batch.qr_generation_price:
            user.company.total_amount += new_batch.qr_generation_price
            user.company.save()
        with transaction.atomic():
            list_of_qr = []
            for unique_hash_code in unique_hash_codes:
                try:
                    url = BASE_URL + unique_hash_code
                    qr = qrcode.QRCode(
                        version=4,
                        error_correction=qrcode.constants.ERROR_CORRECT_H,
                        box_size=10,
                        border=10,
                    )
                    print(url)
                    qr.add_data(url)
                    qr.make(fit=True)

                    img = qr.make_image(fill_color=color, back_color=bgcolor)
                    img = img.convert("RGBA")
                    # ===========================================================================================
                    # Adding image on qr code
                    try:
                        logo_on_qr = request.user.company.logo_on_qr
                        # logo_on_qr = "media/media/static/company_logos/tiivra.jpeg"

                    except Exception as e:
                        print(e, "exception")
                        logo_on_qr = None

                    draw = ImageDraw.Draw(img)
                    # font = ImageFont.load_default()
                    if logo_on_qr:
                        try:
                            logo = Image.open(logo_on_qr)

                            # Resize logo
                            logo_size = img.size[0] // 5  # Make logo 1/4th the size of QR code
                            logo = logo.resize((logo_size, logo_size), Image.LANCZOS)

                            # Calculate position
                            xpos = (img.width - logo_size) // 2
                            ypos = (img.height - logo_size) // 2
                            # -----------------------------------------------
                            print(bgcolor,color,"bgcolor,color")
                            if bgcolor == "transparent":
                                transparent_area = Image.new("L", img.size, 255)
                                draw = ImageDraw.Draw(transparent_area)
                                # draw.rectangle([xpos, ypos, xpos + logo_size, ypos + logo_size], fill=(0, 0, 0, 0))
                                draw.ellipse([xpos, ypos, xpos + logo_size, ypos + logo_size], fill=0)
                                # ----
                                qr_alpha = img.getchannel("A")
                                qr_alpha = Image.composite(qr_alpha, Image.new("L", img.size, 0), transparent_area)
                                img.putalpha(qr_alpha)
                                print(1)
                            else:
                                draw.ellipse([xpos, ypos, xpos + logo_size, ypos + logo_size], fill=bgcolor)
                            # ----
                            # -----------------------------------------------

                            # Paste logo onto QR code
                            img.paste(logo, (xpos, ypos), mask=logo if logo.mode == 'RGBA' else None)
                        except Exception as e:
                            print(f"Error adding logo: {e}")

                    # ================================================================================================


                    #===============================================================================================
                    # font
                    print(2)
                    font_path = "/var/www/rescue_company/static/TTSupermolotNeue-CondBoldIt.ttf"  # Update with actual path
                    font_size = 35
                    font = ImageFont.truetype(font_path, font_size)
                    text_below_qr = "SCAN CODE IN CASE OF AN EMERGENCY"
                    #===============================================================================================

                    # text_bbox = draw.textbbox((0, 0), unique_hash_code, font=font)
                    text_bbox = draw.textbbox((0, 0), text_below_qr, font=font)

                    x = (img.width - text_bbox[2]) // 2
                    print(text_bbox[3])
                    y = img.height - text_bbox[3] - 40

                    # draw.text((x, y), unique_hash_code, font=font, fill=color)
                    print(color,"color")
                    draw.text((x, y), text_below_qr, font=font, fill=color)
                    print(3)

                    buffered = BytesIO()
                    img.save(buffered, format="PNG")

                    buffered.seek(0)  # Rewind the BytesIO object to the beginning

                    list_of_qr.append((unique_hash_code, buffered.getvalue()))

                except Exception as e:
                    print(e)
                    messages.error(request, f"1)Error creating QR code for {unique_hash_code}", extra_tags="danger")
                    return redirect("/qr_generator")
                    # return JsonResponse({'error': f'Error creating QR code for {unique_hash_code}: {str(e)}'},status=500)

            # print(list_of_qr,"list_of_qr")
        # Customers.objects.bulk_create([
        #     Customers(qr_code=code, batch=new_batch) for code, _ in list_of_qr
        # ])

    except IntegrityError as e:
        messages.error(request, f'Database error occurred', extra_tags="danger")
        return redirect("/")
    except Exception as e:
        print(e)
        messages.error(request, 'Unexpected error occurred', extra_tags="danger")
        return redirect("/")

    try:
        Customers.objects.bulk_create([
            Customers(qr_code=code, batch=new_batch) for code, _ in list_of_qr
        ])
        if download_option == 'pdf':
            download_response = create_pdf_with_qr_codes(list_of_qr, new_batch)
        elif download_option == 'zip':
            download_response = create_zip_with_qr_codes(list_of_qr, new_batch)

        # messages.success(request, 'Successfully generated QR codes', extra_tags="success")
        return download_response
    except Exception as e:
        print(e)
        messages.error(request, 'Error creating PDF or ZIP', extra_tags="danger")
        return redirect("/qr_generator")
        # return JsonResponse({'error': f'Error creating PDF or ZIP: {str(e)}'}, status=500)


@login_required
def download_batch(request, batch_id, download_option):
    # download_option = request.POST.get('download_option', "pdf")
    if not download_option:
        download_option = 'pdf'
    list_of_qr = []
    batch = Batch.objects.get(id=batch_id)
    all_customers = Customers.objects.filter(batch=batch)
    unique_hash_codes = [customer.qr_code for customer in all_customers]
    for unique_hash_code in unique_hash_codes:
        try:
            try:
                BASE_URL = request.user.company.redirect_url+"/"
            except Exception as e:
                print(e, "exception")
                BASE_URL = "http://rescuescanner.in/"
            url = BASE_URL + unique_hash_code
            qr = qrcode.QRCode(
                version=4,
                error_correction=qrcode.constants.ERROR_CORRECT_H,
                box_size=10,
                border=4,
            )
            print(url)
            qr.add_data(url)
            qr.make(fit=True)

            img = qr.make_image(fill_color=batch.color, back_color=batch.bgcolor)
            img = img.convert("RGBA")

            draw = ImageDraw.Draw(img)
            font = ImageFont.load_default()  # You can customize the font as needed

            text_bbox = draw.textbbox((0, 0), unique_hash_code, font=font)
            x = (img.width - text_bbox[2]) // 2
            y = img.height - text_bbox[3] - 8

            draw.text((x, y), unique_hash_code, font=font, fill=batch.color)

            buffered = BytesIO()
            img.save(buffered, format="PNG")
            buffered.seek(0)  # Rewind the BytesIO object to the beginning
            list_of_qr.append((unique_hash_code, buffered.getvalue()))

        except Exception as e:
            print(e, "error")
            messages.error(request, 'Error creating QR code', extra_tags="danger")
            return redirect("/qr_generator")
            # return JsonResponse({'error': f'Error creating QR code for {unique_hash_code}: {str(e)}'},status=500)

    try:
        if download_option == 'pdf':
            download_response = create_pdf_with_qr_codes(list_of_qr, batch)
        elif download_option == 'zip':
            download_response = create_zip_with_qr_codes(list_of_qr, batch)
        return download_response
    except Exception as e:
        messages.error(request, 'Error creating PDF or ZIP', extra_tags="danger")
        return redirect("/qr_generator")
        # return JsonResponse({'error': f'Error creating PDF or ZIP: {str(e)}'}, status=500)


@login_required
def download_individual_qr(request, cust_id, download_option):
    # download_option = request.POST.get('download_option', "pdf")
    if not download_option:
        download_option = 'pdf'
    list_of_qr = []

    all_customers = Customers.objects.filter(id=cust_id)
    if len(all_customers) != 0:
        batch = all_customers[0].batch
    else:
        messages.error(request, 'Error downloading QR code', extra_tags="danger")
        return redirect("/qr_generator")
        # return JsonResponse({'error': f'Error downloading QR code'}, status=500)
    unique_hash_codes = [customer.qr_code for customer in all_customers]
    try:
        for unique_hash_code in unique_hash_codes:
            try:
                try:
                    BASE_URL = request.user.company.redirect_url+"/"
                except Exception as e:
                    print(e, "exception")
                    BASE_URL = "http://rescuescanner.in/"



                url = BASE_URL + unique_hash_code
                qr = qrcode.QRCode(
                    version=4,
                    error_correction=qrcode.constants.ERROR_CORRECT_H,
                    box_size=10,
                    border=4,
                )
                qr.add_data(url)
                qr.make(fit=True)

                img = qr.make_image(fill_color=batch.color, back_color=batch.bgcolor)
                img = img.convert("RGBA")
                # ===========================================================================================
                # Adding image on qr code
                try:
                    # logo_on_qr = request.user.company.logo_on_qr
                    logo_on_qr = "https://rescuescanner.in/media/media/static/user_profile_image/customer_12_524RtOM.jpg"
                    print("logo_on_qr", logo_on_qr)

                except Exception as e:
                    print(e, "exception")
                    logo_on_qr = None
                if logo_on_qr:
                    try:
                        logo = Image.open(logo_on_qr)

                        # Resize logo
                        logo_size = img.size[0] // 4  # Make logo 1/4th the size of QR code
                        logo = logo.resize((logo_size, logo_size), Image.LANCZOS)

                        # Calculate position
                        xpos = (img.width - logo_size) // 2
                        ypos = (img.height - logo_size) // 2

                        # Paste logo onto QR code
                        img.paste(logo, (xpos, ypos), mask=logo if logo.mode == 'RGBA' else None)
                    except Exception as e:
                        print(f"Error adding logo: {e}")

                # ================================================================================================
                draw = ImageDraw.Draw(img)
                font = ImageFont.load_default()  # You can customize the font as needed

                text_bbox = draw.textbbox((0, 0), unique_hash_code, font=font)
                x = (img.width - text_bbox[2]) // 2
                y = img.height - text_bbox[3] - 8

                draw.text((x, y), unique_hash_code, font=font, fill=batch.color)

                buffered = BytesIO()
                img.save(buffered, format="PNG")
                buffered.seek(0)  # Rewind the BytesIO object to the beginning
                list_of_qr.append((unique_hash_code, buffered.getvalue()))

            except Exception as e:
                print(e, "error")
                messages.error(request, f'Error creating QR code for {unique_hash_code}', extra_tags="danger")
                return redirect("/qr_generator")
                # return JsonResponse({'error': f'Error creating QR code for {unique_hash_code}: {str(e)}'},status=500)
    except Exception as e:
        print(e, "error")
        messages.error(request, 'Error creating QR code', extra_tags="danger")
        return redirect("/qr_generator")
        # return JsonResponse({'error': f'Error creating QR code for {unique_hash_code}: {str(e)}'},status=500)

    try:
        if download_option == 'pdf':
            download_response = create_pdf_with_qr_codes(list_of_qr, batch, 'individual', all_customers[0].qr_code)
        elif download_option == 'zip':
            download_response = create_zip_with_qr_codes(list_of_qr, batch, 'individual', all_customers[0].qr_code)
        return download_response
    except Exception as e:
        messages.error(request, 'Error creating PDF or ZIP', extra_tags="danger")
        return redirect("/qr_generator")
        # return JsonResponse({'error': f'Error creating PDF or ZIP: {str(e)}'}, status=500)


def create_pdf_with_qr_codes(list_of_qr, new_batch, called_by=None, qr=None):
    response = HttpResponse(content_type='application/pdf')
    # response['Content-Disposition'] = f'attachment; filename="qr_codes_{datetime.now().strftime("%d%m%Y")}.pdf"'
    if called_by == "individual":
        response['Content-Disposition'] = f'attachment; filename="qr_codes_{qr}.pdf"'
    else:
        response['Content-Disposition'] = f'attachment; filename="qr_codes_{new_batch.batch_number}.pdf"'

    c = canvas.Canvas(response, pagesize=A4)
    width, height = A4

    for i, (unique_hash_code, img_data) in enumerate(list_of_qr):
        if i > 0:
            c.showPage()  # Start a new page for each QR code

        buffered = BytesIO(img_data)
        img = ImageReader(buffered)
        img_width, img_height = img.getSize()

        # Center the image on the page
        c.drawImage(img, (width - img_width) / 2, (height - img_height) / 2, width=img_width, height=img_height)

    c.save()
    return response


def create_zip_with_qr_codes(list_of_qr, new_batch, called_by=None, qr=None):
    # Create an in-memory bytes buffer
    zip_buffer = io.BytesIO()

    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for unique_hash_code, img_data in list_of_qr:
            img_filename = f"{unique_hash_code}.png"
            # Directly write the QR code image data to the zip file
            zipf.writestr(img_filename, img_data)

    # Seek to the beginning of the BytesIO buffer
    zip_buffer.seek(0)

    # Create the HTTP response with the ZIP file
    response = HttpResponse(zip_buffer, content_type='application/zip')
    if called_by == 'individual':
        response['Content-Disposition'] = f'attachment; filename="qr_codes_{qr}.zip"'
    else:
        response['Content-Disposition'] = f'attachment; filename="qr_codes_{new_batch.batch_number}.zip"'

    return response
