from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render
from inventory.models import *
from .models import *
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from home.models import CompanyProfile
from datetime import date
from django.db.models import Sum
from datetime import datetime
from dateutil.parser import parse
from django.db.models import Q
from inventory.models import *
from .models import PaymentToDoctor
from django.shortcuts import render, get_object_or_404, redirect
from inventory.models import Doctor
from django.contrib.auth.decorators import login_required
from datetime import date
from .forms import PaymentToDoctorForm


from django.db.models import Sum
from django.utils.timezone import now

from django.shortcuts import render, get_object_or_404
from inventory.models import DoctorCommission
from django.utils.timezone import now
from django.contrib.auth.decorators import login_required


def parse_date_safe(value):
    """Safely parse an ISO formatted date string into a date object."""
    try:
        return date.fromisoformat(value)
    except (TypeError, ValueError):
        return None

@login_required
def doctor_commission_payment_slip_view(request, commission_id):
    commission = get_object_or_404(DoctorCommission, id=commission_id)
    return render(request, 'finance/doctor_commission_payment.html', {
        'commission': commission,
        'title': 'Doctor Commission Slip',
        'now': now(),
        'request': request,
    })


@login_required
def payment_to_doctor_create_view(request, doctor_id):
    doctor = get_object_or_404(Doctor, id=doctor_id)

    # Calculate doctor balance: sum of credit - sum of debit
    from finance.models import DoctorCommission
    summary = DoctorCommission.objects.filter(doctor=doctor).aggregate(
        total_credit=Sum('credit'),
        total_debit=Sum('debit')
    )
    credit = summary['total_credit'] or 0
    debit = summary['total_debit'] or 0
    balance = credit - debit

    if request.method == 'POST':
        form = PaymentToDoctorForm(request.POST)
        if form.is_valid():
            payment = form.save(commit=False)
            payment.doctor = doctor
            payment.placed_at = now().date()
            payment.created_by = request.user
            payment.updated_by = request.user
            payment.save()
            return redirect('inventory:doctor_commission_list', doctor_id=doctor.id)
    else:
        form = PaymentToDoctorForm()

    return render(request, 'finance/payment_to_doctor_form.html', {
        'form': form,
        'doctor': doctor,
        'title': 'Pay Doctor Commission',
        'balance': balance,
    })


# owners withdrawal voucher print


def owner_withdrawal_voucher(request, pk):
    current_date = date.today()
    company_profile = CompanyProfile.objects.first()
    withdraw = get_object_or_404(ShareHolderWithdrawal, pk=pk)
    context = {'expense': withdraw,
               'company_profile': company_profile,
               'current_date': current_date}
    return render(request, 'finance/holder_withdrawal_voucher.html', context)


def owner_deposit_voucher(request, pk):
    current_date = date.today()
    company_profile = CompanyProfile.objects.first()
    withdraw = get_object_or_404(ShareHolderDeposit, pk=pk)
    context = {'expense': withdraw,
               'company_profile': company_profile,
               'current_date': current_date}
    return render(request, 'finance/holder_deposit_voucher.html', context)


@login_required
def expense_voucher(request, pk):
    current_date = date.today()
    company_profile = CompanyProfile.objects.first()
    expense = get_object_or_404(AddExpense, pk=pk)
    user = request.user  # Get the currently logged-in user

    # Prepare the context dictionary with data to pass to the template
    context = {
        'expense': expense,
        'company_profile': company_profile,
        'current_date': current_date,
        'user': user,  # Include the user details in the context
    }

    # Render the 'finance/expense_voucher.html' template with the context data and return the rendered response
    return render(request, 'finance/expense_voucher.html', context)


@login_required
def payment_voucher_to_doctor(request, pk):
    current_date = date.today()
    company_profile = CompanyProfile.objects.first()
    payment = get_object_or_404(PaymentToDoctor, pk=pk)
    user = request.user  # Get the currently logged-in user

    # Retrieve the related DoctorCommission for the Doctor associated with the PaymentToDoctor instance
    doctor_commission = DoctorCommission.objects.filter(
        doctor=payment.doctor).order_by('-id').first()

    # If there's a related DoctorCommission, get its current_balance, otherwise set it to 0
    balance = doctor_commission.current_balance if doctor_commission else Decimal(
        '0')

    # Prepare the context dictionary with data to pass to the template
    context = {
        'payment': payment,
        'company_profile': company_profile,
        'current_date': current_date,
        'user': user,  # Include the user details in the context
        'balance': balance,  # Include the current_balance as balance in the context
    }

    # Render the 'finance/doctor_expense_voucher.html' template with the context data and return the rendered response
    return render(request, 'finance/doctor_expense_voucher.html', context)


def expense_report_parametrized(request):
    current_date = date.today()
    start_date_persian = None
    end_date_persian = None

    if request.method == 'POST':
        start_date = request.POST.get('start_date')
        end_date = request.POST.get('end_date')
        approved_by_choices = request.POST.getlist('approved-by-choice')
        expense_category_choices = request.POST.getlist(
            'expense_category-choice')

        if start_date and end_date:
            start_date = parse_date_safe(start_date)
            end_date = parse_date_safe(end_date)
            if start_date and end_date:
                start_date_persian = date2jalali(start_date)
                end_date_persian = date2jalali(end_date)
            else:
                start_date = None
                end_date = None

        expenses = AddExpense.objects.all()

        if approved_by_choices:
            expenses = expenses.filter(approved_by__in=approved_by_choices)
        if expense_category_choices:
            expenses = expenses.filter(
                expense_category__in=expense_category_choices)
        if start_date and end_date:
            expenses = expenses.filter(created_at__date__range=[
                                       start_date, end_date])

        total_amount = expenses.aggregate(Sum('amount'))['amount__sum'] or 0

        context = {
            'expenses': expenses,
            'current_date': current_date,
            'approved_by_choices': approved_by_choices,
            'start_date': start_date_persian,
            'end_date': end_date_persian,
            'total_amount': total_amount
        }

        return render(request, 'finance/expense_report_print.html', context)

    approved_by_choices = Employment.objects.all()
    expense_category = ExpenseCategory.objects.all()

    context = {
        'expense_category': expense_category,
        'approved_by_choices': approved_by_choices,
    }

    return render(request, 'finance/expense_report_parameter.html', context)


def share_holder_summary(request):
    # Fetch all ShareHolders
    share_holders = ShareHolders.objects.all()

    # Fetch ShareHolderDeposit records and generate summaries
    summaries = []
    total_currency_summaries = {}  # To store the total summary for each currency

    for holder in share_holders:
        deposits = ShareHolderDeposit.objects.filter(share_holder=holder)
        if deposits.exists():  # Check if there are associated records
            currency_summaries = deposits.values('currency__code').annotate(
                total_in=Sum('amount_in'), total_out=Sum('amount_out')
            )

            for currency_summary in currency_summaries:
                currency_code = currency_summary['currency__code']
                balance = currency_summary['total_in'] - \
                    currency_summary['total_out']
                currency_summary['balance'] = balance

                if currency_code in total_currency_summaries:
                    total_currency_summaries[currency_code]['total_in'] += currency_summary['total_in']
                    total_currency_summaries[currency_code]['total_out'] += currency_summary['total_out']
                    total_currency_summaries[currency_code]['total_balance'] += balance
                else:
                    total_currency_summaries[currency_code] = {
                        'total_in': currency_summary['total_in'],
                        'total_out': currency_summary['total_out'],
                        'total_balance': balance,
                    }

            summaries.append(
                {'holder': holder, 'currencies': currency_summaries})

    context = {'summaries': summaries,
               'total_currency_summaries': total_currency_summaries}
    return render(request, 'finance/share_holder_widraw_deposit_details.html', context)


@login_required
def cash_details(request):
    if request.method == 'POST':
        start_date = request.POST.get('start_date')
        end_date = request.POST.get('end_date')
        selected_doctor = request.POST.get('account_type')
        current_date = date.today()
        account_type = CashAccount.objects.all()
        cash_balance = CashAccount.objects.filter().order_by('date')

        if start_date and end_date:
            start_date = parse_date_safe(start_date)
            end_date = parse_date_safe(end_date)
            if not start_date or not end_date:
                start_date = None
                end_date = None
            else:
                cash_balance = cash_balance.filter(
                    date__range=(start_date, end_date))

        if selected_doctor:
            # Create a filter condition for the selected doctor
            doctor_filter = Q(account_type=selected_doctor)

            # Apply the doctor filter to the queryset
            cash_balance = cash_balance.filter(doctor_filter)

        # Check if there are any records within the selected date range
        records_exist = cash_balance.exists()

        if records_exist:
            # Order the queryset
            cash_balance = cash_balance.order_by('date')

            # Aggregate the debit, credit, and current_balance fields for the queryset
            totals = cash_balance.aggregate(
                total_debit=Sum('amount_out'),
                total_credit=Sum('amount_in'),
                total_balance=Sum('current_balance')
            )
        # ------------------ cash in hand amount Portion -------------------------------
        cash_summary = CashAccount.objects.values('account_type').annotate(
            total_balance=Sum('amount_in') - Sum('amount_out')
        )
    context = {
        'current_user_name': current_user_name,
        'current_date': current_date,
        'cash_summary': cash_summary,
        'stock_total_amount_in_sale_rate': stock_total_amount_in_sale_rate,
        'stock_total_amount_in_purchased_rate': stock_total_amount_in_purchased_rate,
        'commission_total_debit': commission_totals['commission_total_debit'],
        'commission_total_credit': commission_totals['commission_total_credit'],
        'commission_net_balance': commission_net_balance,
        'outstanding_total_balance': outstanding_net_balance,
        'expense_total_amount': expense_total_amount,
        'total_received_debit': received_totals_amount['total_received_debit'],
        'received_order_items': received_order_items,
        'total_amount': purchased_total_amount,
        'summaries': summaries,
        'total_currency_summaries': total_currency_summaries,
        'grand_total': grand_total,
    }

    return render(request, 'finance/general_report.html', context)

@login_required
def general_details_report(request):
    current_date = date.today()
    # Get the currently logged-in user's name
    current_user_name = request.user.get_full_name()
    # ------------------ Total Purchased Amount  Portion ----------------------
    received_order_items = ReceivedOrderItem.objects.all()

    purchased_total_amount = 0  # Initialize total_amount

    # Calculate the total amount for each item and update the total_amount
    for item in received_order_items:
        item.purchased_total_amount = item.received_quantity * item.get_GP_Price()
        purchased_total_amount += item.purchased_total_amount
    # ------------------ Total Purchased Amount  Portion ----------------------

    # ------------------ Total Deposit Amount  Portion ------------------------
    # Fetch all ShareHolders
    share_holders = ShareHolders.objects.all()

    # Fetch ShareHolderDeposit records and generate summaries
    summaries = []
    total_currency_summaries = {}  # To store the total summary for each currency

    for holder in share_holders:
        deposits = ShareHolderDeposit.objects.filter(share_holder=holder)
        if deposits.exists():  # Check if there are associated records
            currency_summaries = deposits.values('currency__code').annotate(
                total_in=Sum('amount_in'), total_out=Sum('amount_out')
            )

            for currency_summary in currency_summaries:
                currency_code = currency_summary['currency__code']
                balance = currency_summary['total_in'] - \
                    currency_summary['total_out']
                currency_summary['balance'] = balance

                if currency_code in total_currency_summaries:
                    total_currency_summaries[currency_code]['total_in'] += currency_summary['total_in']
                    total_currency_summaries[currency_code]['total_out'] += currency_summary['total_out']
                    total_currency_summaries[currency_code]['total_balance'] += balance
                else:
                    total_currency_summaries[currency_code] = {
                        'total_in': currency_summary['total_in'],
                        'total_out': currency_summary['total_out'],
                        'total_balance': balance,
                    }

            summaries.append(
                {'holder': holder, 'currencies': currency_summaries})
    # ------------------ Total Deposit Amount  Portion ------------------------

    # ------------------ Total Sale's Amount  Portion -------------------------
    sale_order_items = SaleOrderItems.objects.all()

    # Calculate the grand total of all sale order items
    grand_total = sum(item.total_amount for item in sale_order_items)
    # ------------------ Total Sale's Amount  Portion -------------------------

    # ------------------ Total Received & Pending Amount From Customer Portion -------------------------
    consumer = Consumer.objects.all()
    consumer_balance = ConsumerAccount.objects.filter().order_by('tx_date')
    # Check if there are any records within the selected date range
    records_exist = consumer_balance.exists()

    if records_exist:
        # Order the queryset
        consumer_balance = consumer_balance.order_by('tx_date')

    # Aggregate the debit, credit, and current_balance fields for the queryset
        received_totals_amount = consumer_balance.aggregate(
            total_received_debit=Sum('debit'),
            total_received_credit=Sum('credit'),
        )
        outstanding_net_balance = received_totals_amount['total_received_credit'] - \
            received_totals_amount['total_received_debit']
    # ------------------ Total Received & Pending Amount From Customer Portion -------------------------

    # ------------------ Total expenses Amount Portion -------------------------------
    expenses = AddExpense.objects.all()
    expense_total_amount = expenses.aggregate(
        Sum('amount'))['amount__sum'] or 0
    # ------------------ Total expenses Amount Portion -------------------------------

    # ------------------ Total Commission Amount Portion -------------------------------
    doctor_balance = DoctorCommission.objects.filter().order_by('placed_at')
    # Check if there are any records within the selected date range
    records_exist = doctor_balance.exists()

    if records_exist:
        # Order the queryset
        doctor_balance = doctor_balance.order_by('placed_at')

        # Aggregate the debit, credit, and current_balance fields for the queryset
        commission_totals = doctor_balance.aggregate(
            commission_total_debit=Sum('debit'),
            commission_total_credit=Sum('credit'),
            commission_total_balance=Sum('current_balance')
        )
        commission_net_balance = commission_totals['commission_total_credit'] - \
            commission_totals['commission_total_debit']
       # ------------------ End of Total Commission Amount Portion -------------------------------
       # ------------------ Stock available purchased amount Portion -------------------------------
        product_batches = ProductBatches.objects.all()

        stock_total_amount_in_purchased_rate = 0
        for item in product_batches:
            item.stock_total_amount_in_purchased_rate = item.quantity * item.purchased_price
            stock_total_amount_in_purchased_rate += item.stock_total_amount_in_purchased_rate
        # ------------------ Stock available purchased amount Portion -------------------------------
        # ------------------ Stock available sale amount Portion -------------------------------
        product_batches = ProductBatches.objects.all()

        stock_total_amount_in_sale_rate = 0
        for item in product_batches:
            item.stock_total_amount_in_sale_rate = item.quantity * item.product.unit_price
            stock_total_amount_in_sale_rate += item.stock_total_amount_in_sale_rate
        # ------------------ Stock available sale amount Portion -------------------------------
        # ------------------ cash in hand amount Portion -------------------------------
        cash_summary = CashAccount.objects.values('account_type').annotate(
            total_balance=Sum('amount_in') - Sum('amount_out')
        )
    context = {
        'current_user_name': current_user_name,
        'current_date': current_date,
        'cash_summary': cash_summary,
        'stock_total_amount_in_sale_rate': stock_total_amount_in_sale_rate,
        'stock_total_amount_in_purchased_rate': stock_total_amount_in_purchased_rate,
        'commission_total_debit': commission_totals['commission_total_debit'],
        'commission_total_credit': commission_totals['commission_total_credit'],
        'commission_net_balance': commission_net_balance,
        'outstanding_total_balance': outstanding_net_balance,
        'expense_total_amount': expense_total_amount,
        'total_received_debit': received_totals_amount['total_received_debit'],
        'received_order_items': received_order_items,
        'total_amount': purchased_total_amount,
        'summaries': summaries,
        'total_currency_summaries': total_currency_summaries,
        'grand_total': grand_total,
    }

    return render(request, 'finance/general_report.html', context)

@login_required
def comprehensive_report(request):
    """Generate a summarized financial report with optional date filtering."""
    start_date = request.POST.get('start_date') if request.method == 'POST' else None
    end_date = request.POST.get('end_date') if request.method == 'POST' else None

    if start_date and end_date:
        start_date = parse_date_safe(start_date)
        end_date = parse_date_safe(end_date)
        if not start_date or not end_date:
            start_date = end_date = None

    cash_qs = CashAccount.objects.all()
    consumer_qs = ConsumerAccount.objects.all()
    expense_qs = AddExpense.objects.all()
    doctor_payment_qs = PaymentToDoctor.objects.all()
    commission_qs = DoctorCommission.objects.all()

    if start_date and end_date:
        cash_qs = cash_qs.filter(date__range=(start_date, end_date))
        consumer_qs = consumer_qs.filter(tx_date__range=(start_date, end_date))
        expense_qs = expense_qs.filter(date__range=(start_date, end_date))
        doctor_payment_qs = doctor_payment_qs.filter(placed_at__range=(start_date, end_date))
        commission_qs = commission_qs.filter(placed_at__range=(start_date, end_date))

    cash_summary = cash_qs.values('account_type').annotate(
        total_balance=Sum('amount_in') - Sum('amount_out')
    )

    consumer_totals = consumer_qs.aggregate(
        total_debit=Sum('debit'),
        total_credit=Sum('credit')
    )
    consumer_collection_total = consumer_totals['total_debit'] or 0
    consumer_outstanding_total = (
        consumer_totals['total_credit'] or 0
    ) - consumer_collection_total

    expense_total = expense_qs.aggregate(total=Sum('amount'))['total'] or 0
    doctor_payment_total = doctor_payment_qs.aggregate(total=Sum('amount_paid'))['total'] or 0

    commission_totals = commission_qs.aggregate(
        total_debit=Sum('debit'),
        total_credit=Sum('credit')
    )
    doctor_outstanding_total = (
        commission_totals['total_credit'] or 0
    ) - (commission_totals['total_debit'] or 0)

    if request.method == 'POST':
        context = {
            'cash_summary': cash_summary,
            'consumer_collection_total': consumer_collection_total,
            'consumer_outstanding_total': consumer_outstanding_total,
            'expense_total': expense_total,
            'doctor_payment_total': doctor_payment_total,
            'doctor_outstanding_total': doctor_outstanding_total,
            'start_date': start_date,
            'end_date': end_date,
            'current_date': date.today(),
            'current_user_name': request.user.get_full_name(),
        }
        return render(request, 'finance/comprehensive_report.html', context)

    return render(request, 'finance/comprehensive_report_parameter.html')

@login_required
def consumer_ledger(request, consumer_id):
    """Display ledger entries for a specific consumer."""
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date')

    consumer = get_object_or_404(Consumer, id=consumer_id)
    company_profile = CompanyProfile.objects.first()

    qs = (ConsumerAccount.objects
          .filter(consumer=consumer)
          .order_by('-tx_date', '-id'))

    if start_date:
        qs = qs.filter(tx_date__gte=start_date)
    if end_date:
        qs = qs.filter(tx_date__lte=end_date)

    totals = qs.aggregate(total_credit=Sum('credit'), total_debit=Sum('debit'))
    balance = (totals['total_credit'] or 0) - (totals['total_debit'] or 0)

    context = {
        'accounts': qs,
        'consumer': consumer,
        'totals': totals,
        'balance': balance,
        'start_date': start_date,
        'end_date': end_date,
        'title': f'{consumer.name} - Ledger',
        'company_profile': company_profile,
    }
    return render(request, 'finance/consumer_ledger.html', context)
@login_required
@permission_required('home.expense_report', raise_exception=True)
def expense_report_view(request):
    expenses = AddExpense.objects.select_related(
        'expense_category', 'currency').order_by('-date')
    categories = ExpenseCategory.objects.all()
    return render(request, 'finance/expense_report.html', {
        'expenses': expenses,
        'categories': categories,
    })