from django.shortcuts import get_object_or_404, redirect
from django.db import transaction
from django.contrib import messages
from django.template.response import TemplateResponse
from django.db.models import Sum
from .models import Tenant, TenantRoom, TenantRent
from paymentlog.models import PaymentLog
from paymentlog.services import generate_electricity_log_instances, get_food_log_instance, get_rent_log_instance
from django.views import View
from room.models import Room
from room.services import get_active_tenants
from utils.services import current_date
from utils.logging import log_action
from datetime import timedelta
import logging

logger = logging.getLogger(__name__)

class DetailsView(View):
    template_name = 'tenant/tenant_details.html'

    def get(self, request, tenant_id):
        tenant = Tenant.objects.get(pk=tenant_id)  # type: ignore
        tenant_rooms = TenantRoom.objects.filter(tenant=tenant)  # type: ignore
        tenant_rent = TenantRent.objects.get(tenant=tenant)  # type: ignore
        payment_logs = PaymentLog.objects.filter(tenant=tenant) # type: ignore
        total_revenue = round(payment_logs.aggregate(Sum('amount'))['amount__sum'] or 0, 2)
        amount_paid = round(payment_logs.filter(status="paid").aggregate(Sum('amount'))['amount__sum'] or 0, 2)
        amount_pending = round(payment_logs.filter(status="pending").aggregate(Sum('amount'))['amount__sum'] or 0, 2)
        print(tenant.id_proof)

        context = {
            'tenant': tenant,
            'tenant_rooms': tenant_rooms,
            'tenant_rent': tenant_rent,
            'payment_logs': payment_logs,
            'total_revenue': total_revenue,
            'amount_paid': amount_paid,
            'amount_pending': amount_pending,
            'serialized_tenant': {
                'id': tenant.id,
                'hostel_id': tenant.last_room.room.hostel.id,
                'room_id': tenant.last_room.room.id,
                'calculation_dates': {
                    'last_rent_calculation': tenant.calculation_dates.last_rent_calculation,
                    'last_food_calculation': tenant.calculation_dates.last_food_calculation,
                    'last_electricity_calculation': tenant.calculation_dates.last_electricity_calculation,
                }
            }
        }
        return TemplateResponse(request, self.template_name, context)


class ShiftRoomView(View):
    template_name = 'tenant/tenant_details.html'

    def post(self, request, tenant_id):
        try:
            new_room_id = int(request.POST.get('room'))
            with transaction.atomic(): # type: ignore
                current_room = Tenant.objects.select_for_update().get(pk = tenant_id).last_room # type: ignore
                new_room_id = request.POST.get('room')
                is_single_sharing = request.POST.get('single_sharing') == "on"
                current_electricity_reading = request.POST.get('current_electricity_reading')
                move_in_date = request.POST.get('move_in_date')
                room = Room.objects.select_for_update().get(pk=new_room_id) # type: ignore
                tenants = get_active_tenants(room.id)

                if room:
                    if len(tenants) != 0: # means we need to perform the calculation someone is already living in the room.
                        electricity_payment_logs = generate_electricity_log_instances(room.id, current_electricity_reading, move_in_date)
                        PaymentLog.objects.bulk_create(electricity_payment_logs) # type: ignore

                    TenantRoom.objects.create( # type: ignore
                        tenant_id= tenant_id,
                        room= room,
                        joined_on= current_date(move_in_date),
                        is_single_sharing = is_single_sharing or False
                    )

                    if current_room.is_active:
                        current_room.left_on = current_date(move_in_date) - timedelta(days=1)  # should be one less than move_in_date
                        current_room.save()


                    room.last_electricity_reading = current_electricity_reading
                    # update the room if it was meant for single sharing
                    if(is_single_sharing):
                        room.is_available = False

                    room.save()
                return redirect(f'/tenant/tenant/tenant/{tenant_id}/details/')
        except ValueError as e:
            print(e)
            messages.error(request, e)
            return redirect(f'/tenant/tenant/tenant/{tenant_id}/details/')



class PayBillsView(View):
    template_name = 'tenant/pay_bill.html'

    def get(self, request, tenant_id):
        tenant = Tenant.objects.get(pk=tenant_id)  # type: ignore
        pending_payments = PaymentLog.objects.filter(tenant=tenant, status="pending").order_by("amount") # type: ignore
        total_amount = round(pending_payments.aggregate(Sum('amount'))['amount__sum'] or 0, 2)

        context = {
            'title': f"Pay Bills for {tenant.name}",
            'tenant': tenant,
            'pending_payments': pending_payments,
            'total_amount': total_amount
        }
        return TemplateResponse(request, self.template_name, context)

    def post(self, request, tenant_id):
        tenant = get_object_or_404(Tenant, id=tenant_id)
        try:
            given_amount = float(request.POST.get("rent_to_pay", 0))
            pay_with_security = request.POST.get("pay_with_security", False) == "on"

            if given_amount <= 0:
                messages.error(request, "Please enter a valid amount.")
                return redirect(request.path)

            pending_payments = PaymentLog.objects.filter(tenant=tenant, status="pending").order_by("amount") # type: ignore
            remaining_amount = given_amount
            paid_on = current_date().strftime('%Y-%m-%d %I:%M %p')  # type: ignore

            with transaction.atomic(): # type: ignore
                for payment in pending_payments:
                    if remaining_amount <= 0:
                        break

                    if remaining_amount >= payment.amount:
                        # Fully settle this payment
                        payment.status = "paid"
                        remaining_amount -= payment.amount
                    else:
                        # Partially settle it
                        PaymentLog.objects.create( # type: ignore
                            tenant=tenant,
                            room=payment.room,
                            amount=remaining_amount,
                            status="paid",
                            remark=f"""
                                Payment was partially settled:
                                    Settled From: {payment}
                            """,
                            created_at=current_date(),
                            updated_at=current_date()
                        )
                        payment.amount -= remaining_amount
                        payment.remark = f"""{payment.remark}
                                            This payment is partially paid:
                                                Paid Amount: {remaining_amount}
                                                Paid On: {paid_on}
                                        """
                        payment.updated_at = current_date()
                        remaining_amount = 0

                    payment.save()

                if pay_with_security:
                    # lock the tenant_rent to update
                    tenant_rent = TenantRent.objects.select_for_update().get(tenant=tenant) # type: ignore
                    tenant_rent.security_amount = 0
                    tenant_rent.save()


            messages.success(request, f"Payments updated successfully. Rs. {remaining_amount} left after settlement.")
        except ValueError as e:
            print(e)
            messages.error(request, "Invalid input. Please enter a valid number.")

        return redirect(request.path)


class CheckoutView(View):
    template_name = 'tenant/checkout.html'
    def get(self, request, tenant_id):
        tenant = Tenant.objects.get(pk=tenant_id)  # type: ignore
        context = {
            'title': f"Checkout {tenant.name}",
            'tenant': tenant,
        }
        return TemplateResponse(request, self.template_name, context)

    def post(self, request, tenant_id):
        current_electricity_reading = int(request.POST.get("current_electricity_reading", 0))
        tenant = get_object_or_404(Tenant, id=tenant_id)

        if tenant.check_out:
            messages.error(request, "Tenant is already checked out.")
            return redirect(request.path)

        with transaction.atomic(): # type: ignore
            #Lock the tenant object to prevent race conditions
            tenant = Tenant.objects.select_for_update().get(pk=tenant_id) # type: ignore

            # Lock the room object to prevent race conditions
            room = Room.objects.select_for_update().get(pk=tenant.current_room.room.pk) # type: ignore

            # Lock the room object to prevent race conditions
            tenant_room = TenantRoom.objects.select_for_update().get(pk=tenant.current_room.pk) # type: ignore

            payment_logs = []

            # generate food logs
            food_log = get_food_log_instance(tenant.rent_details)
            if food_log is not None:
                payment_logs.append(food_log)

            # generate electricity bills

            payment_logs += generate_electricity_log_instances(room.id, current_electricity_reading)
            # generate rent logs
            rent_log = get_rent_log_instance(tenant.rent_details)

            if rent_log is not None:
                payment_logs.append(rent_log)


            PaymentLog.objects.bulk_create(payment_logs) # type: ignore

            # update left_on on tenant_room
            tenant_room.left_on = current_date()
            tenant_room.save()

            # update check_out on tenant.
            tenant.check_out = current_date()
            tenant.save()

            # create log entry for dashboard.
            log_action(
                user=request.user,
                obj=tenant,
                action_flag=2,
                change_message=[{
                    "changed": {
                        "fields": ["check_out"],
                        "name": "tenant checkout"
                    }
                }]
            )


        return redirect(f'/tenant/tenant/tenant/{tenant_id}/pay-bill/')
