from adminModule.utils import whatsapp_send_initiated, email_send_initiated, whatsapp_send_proof, email_send_proof, sms_send_initiated, sms_send_proof, get_unique_tracking_id
from userModule.models import PersonalDetails, SelectedTile, Transaction, Screenshot, ContactMessage
from django.shortcuts import render, redirect, get_object_or_404
from adminModule.models import Project, Institution, NotificationPreference, ProjectUpdates
from django.db import transaction as db_transaction
from django.db import transaction, IntegrityError
from django.contrib import messages
from django.urls import reverse
from django.db.models import F, Count, ExpressionWrapper, FloatField
import urllib.parse
import os


# Create your views here.
def userIndex(request, ins_id):
    ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    projects = Project.objects.filter(created_by=ins,current_amount__lt=F('funding_goal'),table_status=True
                                      ).annotate(contributor_count=Count('transaction', distinct=True),
                                                 progress=ExpressionWrapper((F('current_amount') * 100.0 / F('funding_goal')),
                                                                            output_field=FloatField())).order_by('-started_at')[:3]
    return render(request, 'index.html',{'ins':ins, 'prj':projects})


def contact_us(request, ins_id):
    try:
        ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    except Exception:
        messages.error(request, "Institution not found.")
        return redirect('/')

    redirect_url = request.META.get('HTTP_REFERER', f'/user/{ins_id}/')

    if request.method == 'POST':
        try:
            with transaction.atomic():
                first_name = request.POST.get('first_name')
                last_name = request.POST.get('last_name')
                email = request.POST.get('email')
                phone = request.POST.get('phone')
                message = request.POST.get('message')

                if not all([first_name, last_name, email, phone, message]):
                    messages.error(request, 'All form fields must be filled out.')
                    return redirect(redirect_url)

                if not phone.isdigit() or len(phone) != 10:
                    messages.error(request, 'Please enter a valid 10-digit phone number.')
                    return redirect(redirect_url)

                ContactMessage.objects.create(
                    first_name=first_name,
                    last_name=last_name,
                    email=email,
                    phone=phone,
                    message=message,
                    ins=ins
                )

                messages.success(request, 'Your message has been sent successfully! We will get back to you soon.')
        except IntegrityError:
            messages.error(request, 'A database error occurred while saving your message. Please try again.')
        except Exception as e:
            messages.error(request, f'An unexpected error occurred: {e}')
    return redirect(redirect_url)


def about(request, ins_id):
    ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    return render(request,'about.html', {'ins':ins})


def credit(request, ins_id):
    ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    return render(request,'user-credit.html', {'ins':ins})


def userAllProject(request,ins_id):
    ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    projects = Project.objects.filter(created_by=ins, current_amount__lt=F('funding_goal'), table_status=True
                                      ).annotate(contributor_count=Count('transaction', distinct=True),
                                                 progress=ExpressionWrapper(
                                                     (F('current_amount') * 100.0 / F('funding_goal')),
                                                     output_field=FloatField())).order_by('-started_at')
    return render(request, 'user-projects.html',{'ins':ins, 'prj':projects})


def userSingleProject(request, prj_id, ins_id):
    ins = get_object_or_404(Institution, id=ins_id, table_status=True)
    prj = get_object_or_404(Project, id=prj_id)

    prj.progress = round((prj.current_amount / prj.funding_goal) * 100, 2) if prj.funding_goal > 0 else 0
    prj.contributors_count = Transaction.objects.filter(project=prj, status='Verified').count()
    project_updates = ProjectUpdates.objects.filter(project=prj).order_by('-update_date')

    if request.method == "POST":
        selected_tiles = request.POST.get("selected_tiles_input")
        if not selected_tiles:
            return redirect(reverse('user_single_project', kwargs={'ins_id': ins_id, 'prj_id': prj_id}))

        checkout_url = reverse('user_checkout', kwargs={'ins_id': ins.id})
        query_string = urllib.parse.urlencode({'project_id': prj.id, 'selected_tiles': selected_tiles})
        return redirect(f"{checkout_url}?{query_string}")

    else:
        project_status = 'Active'
        if prj.funding_goal <= prj.current_amount or prj.closed_by:
            project_status = 'Completed'
        if not prj.table_status:
            project_status = 'Closed'

        common_context = {
            'ins': ins,
            'project': prj,
            'project_status': project_status,
            'updates': project_updates,
        }

        if project_status in ['Completed', 'Expired', 'Closed']:
            return render(request, 'user-single-project.html', common_context)
        else:
            # --- Tile Calculation Logic remains unchanged ---
            total_tiles = int(prj.funding_goal // prj.tile_value)
            sold_tiles_set = set(
                Transaction.objects.filter(project=prj, status='Verified').values_list('tiles_bought__tiles',
                                                                                       flat=True))
            sold_tiles = set([int(t) for s in sold_tiles_set for t in s.split('-') if t.isdigit()])

            processing_tiles_set = set(
                Transaction.objects.filter(project=prj, status='Unverified').values_list('tiles_bought__tiles',
                                                                                         flat=True))
            processing_tiles = set([int(t) for s in processing_tiles_set for t in s.split('-') if t.isdigit()])

            unavailable_tiles_count = len(sold_tiles) + len(processing_tiles)
            available_tiles_count = total_tiles - unavailable_tiles_count
            tile_range = range(1, total_tiles + 1)

            # Update context for Active projects
            common_context.update({'total_tiles': total_tiles,'available_tiles_count': available_tiles_count,'t_range': tile_range,'sold_tiles_set': sold_tiles,'processing_tiles_set': processing_tiles,})
            return render(request, 'user-single-project.html', common_context)


def userCheckoutView(request, ins_id):
    institution = get_object_or_404(Institution, id=ins_id)

    if request.method == "GET":
        project_id = request.GET.get("project_id")
        selected_tiles = request.GET.get("selected_tiles")
        project = get_object_or_404(Project, id=project_id)
        selected_tile_count = len(selected_tiles.split('-'))
        return render(request, 'user-checkout.html', {'ins': institution,'project': project,
                                                      'selected_tiles': selected_tiles,'count': selected_tile_count})

    elif request.method == "POST":
        project_id = request.POST.get("project_id")
        selected_tiles_str = request.POST.get("selected_tiles")
        fname = request.POST.get("fname")
        email = request.POST.get("email")
        phn = request.POST.get("phn")
        addr = request.POST.get("addr")

        project = get_object_or_404(Project, id=project_id)
        total_amount = len(selected_tiles_str.split('-')) * project.tile_value

        if project.funding_goal <= project.current_amount or project.closed_by:
            messages.error(request, "This project has been completed.")
            return redirect(f'/user/{ins_id}/single-project/{project_id}/')
        if not project.table_status:
            messages.error(request, "This project is no longer active for contributions.")
            return redirect(f'/user/{ins_id}/single-project/{project_id}/')
        if SelectedTile.objects.filter(project=project, tiles=selected_tiles_str, table_status=True).exists():
            messages.error(request, "These tiles have already been selected.")
            return redirect(f'/user/{ins_id}/single-project/{project_id}/')

        try:
            with db_transaction.atomic():
                sender = PersonalDetails.objects.create(email=email,full_name=fname,phone=phn,address=addr)
                selected_tile_instance = SelectedTile.objects.create(project=project,sender=sender,tiles=selected_tiles_str)
                transaction = Transaction.objects.create(tiles_bought=selected_tile_instance,sender=sender,project=project,amount=total_amount,currency="INR",status="Unverified",tracking_id=get_unique_tracking_id())

            proof_upload_url = f'{ins_id}/proof/{transaction.id}'

            all_notifications_sent = True
            notification_errors = []
            noti_pref = NotificationPreference.objects.get(institution=institution)

            if noti_pref.sms_enabled:
                sms_result = sms_send_initiated(transaction, proof_upload_url)
                if sms_result['status'] == 'error':
                    all_notifications_sent = False
                    notification_errors.append(f"SMS sending failed: {sms_result['message']}")

            if noti_pref.whatsapp_enabled:
                whatsapp_result = whatsapp_send_initiated(transaction, proof_upload_url)
                if whatsapp_result['status'] == 'error':
                    all_notifications_sent = False
                    notification_errors.append(f"WhatsApp message failed to send: {whatsapp_result['message']}")

            if noti_pref.email_enabled and email:
                email_success, email_message = email_send_initiated(transaction, request.build_absolute_uri(f'/user/{proof_upload_url}/'))
                if not email_success:
                    all_notifications_sent = False
                    notification_errors.append(f"Email sending failed: {email_message}")

            if all_notifications_sent:
                messages.success(request,f'Payment for the tiles {selected_tiles_str} has been initiated and a confirmation has been sent via SMS, WhatsApp, and Email.')
            else:
                base_message = "Your payment has been successfully initiated, but we encountered issues with some of the notification services."
                detailed_errors = " ".join(notification_errors)
                messages.warning(request, f"{base_message} Details: {detailed_errors}")
        except Exception as e:
            messages.error(request, f"Failed to perform checkout: {e}")
        return redirect(f'/user/{ins_id}/single-project/{project_id}/')


def userProofUpload(request, ins_id, trans_id):
    ins = get_object_or_404(Institution, id=ins_id)
    tra = get_object_or_404(Transaction, id=trans_id)

    tiles_string = tra.tiles_bought.tiles if tra.tiles_bought else ''
    count = len(tiles_string.split('-')) if tiles_string else 0

    if request.method == 'POST':
        proof = request.FILES.get('proof_of_payment')
        if not proof:
            messages.error(request, "Please select a file to upload.")
            return redirect(f'/user/{ins_id}/proof/{tra.id}/')

        try:
            with db_transaction.atomic():

                try:
                    old_screenshot = Screenshot.objects.get(transaction=tra)
                    old_file_path = old_screenshot.screen_shot.path
                    old_screenshot.screen_shot = proof
                    old_screenshot.save()

                    if os.path.exists(old_file_path):
                        os.remove(old_file_path)

                except Screenshot.DoesNotExist:
                    new_screenshot = Screenshot.objects.create(transaction=tra, screen_shot=proof)

            all_notifications_sent = True
            notification_errors = []
            noti_pref = NotificationPreference.objects.get(institution=ins)

            if noti_pref.sms_enabled:
                sms_result = sms_send_proof(tra)
                if sms_result['status'] == 'error':
                    all_notifications_sent = False
                    notification_errors.append(f"SMS sending failed: {sms_result['message']}")

            if noti_pref.whatsapp_enabled:
                whatsapp_result = whatsapp_send_proof(tra)
                if whatsapp_result['status'] == 'error':
                    all_notifications_sent = False
                    notification_errors.append(f"WhatsApp message failed to send: {whatsapp_result['message']}")

            if noti_pref.email_enabled and tra.sender.email:
                email_success, email_message = email_send_proof(tra)
                if not email_success:
                    all_notifications_sent = False
                    notification_errors.append(f"Email sending failed: {email_message}")

            if all_notifications_sent:
                messages.success(request,"Proof of payment uploaded successfully and a confirmation has been sent via email, SMS, and WhatsApp.")
            else:
                base_message = "Proof of payment uploaded successfully, but we encountered issues with some of the notification services."
                detailed_errors = " ".join(notification_errors)
                messages.warning(request, f"{base_message} Details: {detailed_errors}")
        except Exception as e:
            messages.error(request, f"An error occurred during proof upload: {e}")
        return redirect(f'/user/{ins_id}/single-project/{tra.project.id}/')

    return render(request, "user-proof-upload.html", {'ins': ins,'tra': tra,'count': count})


def userTrackStatus(request, ins_id):
    ins = Institution.objects.get(id=ins_id)
    transactions = Transaction.objects.none()
    search_content = ''
    if request.method == 'POST':
        track_query = request.POST.get('track', '').strip()
        search_content = track_query

        if track_query:
            if '@' in track_query:
                transactions = Transaction.objects.filter(sender__email=track_query, project__created_by= ins).order_by('-transaction_time')
            elif track_query.isdigit():
                transactions = Transaction.objects.filter(sender__phone=track_query, project__created_by= ins).order_by('-transaction_time')
            else:
                transactions = Transaction.objects.filter(tracking_id=track_query, project__created_by= ins).order_by('-transaction_time')

            for t in transactions:
                if t.tiles_bought:
                    t.num_tiles = len(t.tiles_bought.tiles.split('-'))
                else:
                    t.num_tiles = 0
    return render(request, "user-track-status.html", {'ins': ins, 'tra': transactions, 'search': search_content})

