from datetime import datetime, timedelta
from time import sleep
from decimal import Decimal

from django.conf import settings
from userauth.models import MessageWhatsappTemplate
from django.template import Template, Context
# from core.management.utils import send_whatsapp_message
# from core.management.utils import send_wallet_notification, send_wallet_cost_notification
from waapi.utils import send_whatsapp, send_wallet_notification, send_wallet_cost_notification, send_create_user_notification
# from whatsapp_queue.utils import send_whatsapp
import time
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Count, F, Sum, DateField, DecimalField, FloatField
from django.db.models.functions import TruncMonth, Coalesce, Cast
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse_lazy
from django.views import View
from django.views.generic.edit import DeleteView

from core.models import PatientService, PatientServiceItem, Service
from patient.models import Patient
from .forms import *
from .models import ExpenseItem, ExpensePaid, ExpenseChangeLog
from core.models import ExchangeRate
from warehouse.models import WarehouseItem, Category, WarehouseItemHistory
from reservation.models import Reservation, ReservationWarehouse, ReservationPayment
from django.views import View
from django.http import JsonResponse
from django.utils.dateparse import parse_date
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import json

import json
from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.utils.dateparse import parse_date
from django.shortcuts import render
from django.core.paginator import Paginator
from django.db.models import Sum, Q
from django.contrib import messages
from django.views import View
from django.shortcuts import render
from django.core.paginator import Paginator
from datetime import datetime, date
import calendar
from django.utils import timezone
from collections import defaultdict
from django.db.models import Sum, F, FloatField, ExpressionWrapper
import logging

logger = logging.getLogger(__name__)

from decimal import Decimal
import json
import logging
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.views import View
from django.http import JsonResponse
from django.conf import settings

def fee_management_view(request):
    return render(request, 'accountant/fee-management.html')
@method_decorator(csrf_exempt, name='dispatch')
class DeleteExpenseView(View):
    """API view for deleting expense items"""

    def post(self, request, expenseId):
        try:
            # Try to find the expense in ExpenseItem first
            try:
                expense = ExpenseItem.objects.get(id=expenseId)
                source_model = 'ExpenseItem'
            except ExpenseItem.DoesNotExist:
                # If not found in ExpenseItem, try ExpensePaid
                try:
                    expense = ExpensePaid.objects.get(id=expenseId)
                    source_model = 'ExpensePaid'
                except ExpensePaid.DoesNotExist:
                    return JsonResponse({
                        'success': False,
                        'error': 'Expense record not found'
                    }, status=404)

            # Handle wallet changes for doctor/technician expenses
            if source_model == 'ExpensePaid':
                if expense.expense_type == 'doctor' and expense.doctor:
                    # Return the amount to doctor's wallet
                    wallet_change = expense.cost_dollar if expense.currency_type == 'dollar' \
                        else expense.cost_dinar / ExchangeRate.get_current_rate()
                    expense.doctor.wallet = (expense.doctor.wallet or Decimal('0')) + Decimal(str(wallet_change))
                    expense.doctor.save()

                elif expense.expense_type == 'technician' and expense.technician:
                    # Return the amount to technician's wallet
                    wallet_change = expense.cost_dollar if expense.currency_type == 'dollar' \
                        else expense.cost_dinar / ExchangeRate.get_current_rate()
                    expense.technician.wallet = (expense.technician.wallet or Decimal('0')) + Decimal(
                        str(wallet_change))
                    expense.technician.save()

            elif source_model == 'ExpenseItem':
                if expense.expense_type in ['doctor', 'loandoctor'] and expense.doctor:
                    # Handle doctor wallet changes
                    wallet_change = expense.cost_dollar if expense.currency_type == 'dollar' \
                        else expense.cost_dinar / ExchangeRate.get_current_rate()
                    if expense.expense_type == 'loandoctor':
                        wallet_change *= Decimal('-1')
                    expense.doctor.wallet = (expense.doctor.wallet or Decimal('0')) - Decimal(str(wallet_change))
                    expense.doctor.save()

                elif expense.expense_type in ['technician', 'loantechnician'] and expense.technician:
                    # Handle technician wallet changes
                    wallet_change = expense.cost_dollar if expense.currency_type == 'dollar' \
                        else expense.cost_dinar / ExchangeRate.get_current_rate()
                    if expense.expense_type == 'loantechnician':
                        wallet_change *= Decimal('-1')
                    expense.technician.wallet = (expense.technician.wallet or Decimal('0')) - Decimal(
                        str(wallet_change))
                    expense.technician.save()

                elif expense.expense_type == 'medicalsell':
                    # Return medical items to warehouse
                    if expense.medical_name and expense.medical_quantity > 0:
                        self.return_medical_to_warehouse(expense.medical_name, expense.medical_quantity)

            # Create log before deletion
            self.create_deletion_log(request, expense, source_model)

            # Delete the expense
            expense.delete()

            return JsonResponse({
                'success': True,
                'message': 'Expense deleted successfully'
            })

        except Exception as e:
            logger.error(f"Error deleting expense: {e}", exc_info=True)
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=500)

    def return_medical_to_warehouse(self, medical_name, quantity):
        """Return medical items to warehouse"""
        from warehouse.models import WarehouseItem
        items = WarehouseItem.objects.filter(name=medical_name).order_by('expiry_date')
        remaining = quantity

        for item in items:
            if remaining <= 0:
                break
            item.quantity += remaining
            if item.quantity > item.initial_quantity:
                remaining = item.quantity - item.initial_quantity
                item.quantity = item.initial_quantity
            else:
                remaining = 0
            item.save()

    def create_deletion_log(self, request, expense, source_model):
        """Create a log entry for expense deletion"""
        try:
            old_data = {
                'date': str(expense.date),
                'expense_type': expense.expense_type,
                'currency_type': expense.currency_type,
                'description': expense.description,
                'cost_dollar': str(expense.cost_dollar),
                'cost_dinar': str(expense.cost_dinar),
                'doctor': str(expense.doctor.id) if expense.doctor else None,
                'technician': str(expense.technician.id) if expense.technician else None,
            }

            if source_model == 'ExpenseItem':
                old_data.update({
                    'medical_name': expense.medical_name,
                    'medical_quantity': str(expense.medical_quantity),
                })

            log_entry = ExpenseChangeLog(
                action_type='delete',
                changed_by=request.user if request.user.is_authenticated else None,
                source_model=source_model,
                old_data=json.dumps(old_data, ensure_ascii=False),
                new_data=None,
                changed_fields=','.join(old_data.keys()),
                description=f"Expense {expense.id} deleted"
            )

            # Set the correct relation based on source model
            if source_model == 'ExpensePaid':
                log_entry.expense_paid_id = expense.id
            else:
                log_entry.expense_item_id = expense.id

            log_entry.save()

        except Exception as e:
            logger.error(f"Failed to create deletion log: {str(e)}")
@method_decorator(csrf_exempt, name='dispatch')
class UpdateExpenseItemView(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            expense_id = data.get('id')

            if not expense_id:
                return JsonResponse({
                    'success': False,
                    'error': 'Expense ID is required'
                }, status=400)

            # Get current exchange rate as Decimal
            exchange = ExchangeRate.objects.last()
            exchange_rate = Decimal(str(exchange.rate)) if exchange else Decimal('1.0')

            # First try to find the expense in ExpensePaid (for doctor/technician payments)
            expense = None
            source_model = None

            # Check if this is an ExpensePaid record
            try:
                expense = ExpensePaid.objects.get(id=expense_id)
                source_model = 'ExpensePaid'
                # Verify the expense_type matches what we expect for ExpensePaid
                if data.get('cost_type') not in ['doctor', 'technician']:
                    return JsonResponse({
                        'success': False,
                        'error': 'Cannot change expense type to non-payment type for this record'
                    }, status=400)
            except ExpensePaid.DoesNotExist:
                # If not found in ExpensePaid, try ExpenseItem
                try:
                    expense = ExpenseItem.objects.get(id=expense_id)
                    source_model = 'ExpenseItem'
                    # Verify the expense_type matches what we expect for ExpenseItem

                except ExpenseItem.DoesNotExist:
                    return JsonResponse({
                        'success': False,
                        'error': 'Expense record not found'
                    }, status=404)

            # ذخیره مقادیر قدیمی برای لاگ
            old_data = {
                'date': str(expense.date),
                'expense_type': expense.expense_type,
                'currency_type': expense.currency_type,
                'description': expense.description,
                'cost_dollar': str(expense.cost_dollar),
                'cost_dinar': str(expense.cost_dinar),
                'doctor': str(expense.doctor.id) if expense.doctor else None,
                'technician': str(expense.technician.id) if expense.technician else None,
            }

            # Add medical fields only for ExpenseItem
            if source_model == 'ExpenseItem':
                old_data.update({
                    'medical_name': expense.medical_name,
                    'medical_quantity': str(expense.medical_quantity),
                })

            # محاسبه تغییرات کیف پول برای انواع مختلف هزینه‌ها
            def calculate_wallet_change(old_amount, new_amount, currency_type, is_loan=False):
                old_amount = Decimal(str(old_amount))
                new_amount = Decimal(str(new_amount))

                if currency_type == "dollar":
                    change = (new_amount - old_amount) * (Decimal('-1') if is_loan else Decimal('1'))
                else:
                    change = ((new_amount - old_amount) / exchange_rate) * (Decimal('-1') if is_loan else Decimal('1'))
                return change

            # اعمال تغییرات کیف پول بر اساس نوع هزینه
            cost_type = data.get('cost_type')

            # اگر نوع هزینه تغییر کرده، ابتدا مقدار قبلی را برگردان
            if old_data['expense_type'] != cost_type:
                self.revert_old_wallet_changes(expense, old_data, exchange_rate)

            # به‌روزرسانی فیلدهای اصلی
            expense.date = parse_date(data.get('date'))
            expense.expense_type = cost_type
            expense.currency_type = data.get('currency_type')
            expense.description = data.get('description', '')
            new_cost_dollar = Decimal(str(data.get('cost_dollar', 0)))
            new_cost_dinar = Decimal(str(data.get('cost_dinar', 0)))

            # محاسبه تغییرات مبلغ
            cost_diff_dollar = new_cost_dollar - Decimal(str(expense.cost_dollar))
            cost_diff_dinar = new_cost_dinar - Decimal(str(expense.cost_dinar))

            # به‌روزرسانی فیلدهای خاص و اعمال تغییرات کیف پول
            if cost_type in ['doctor', 'loandoctor']:
                new_doctor_id = data.get('doctor_id')
                if new_doctor_id:
                    new_doctor = Doctor.objects.get(id=new_doctor_id)

                    # اگر دکتر تغییر کرده، کیف پول دکتر قبلی را برگردان
                    if expense.doctor and expense.doctor.id != new_doctor_id:
                        self.revert_doctor_wallet(expense, old_data['expense_type'], exchange_rate)

                    # محاسبه تغییرات برای دکتر جدید
                    old_amount = Decimal(str(expense.cost_dollar)) if expense.currency_type == 'dollar' else Decimal(
                        str(expense.cost_dinar))
                    new_amount = new_cost_dollar if data.get('currency_type') == 'dollar' else new_cost_dinar

                    wallet_change = calculate_wallet_change(
                        old_amount,
                        new_amount,
                        data.get('currency_type'),
                        cost_type == 'loandoctor'
                    )

                    # اعمال تغییرات کیف پول دکتر
                    new_doctor.wallet = (new_doctor.wallet if new_doctor.wallet is not None else Decimal(
                        '0')) + wallet_change
                    new_doctor.save()
                    expense.doctor = new_doctor
                else:
                    expense.doctor = None

            elif cost_type in ['technician', 'loantechnician']:
                new_technician_id = data.get('technician_id')
                if new_technician_id:
                    new_technician = Technician.objects.get(id=new_technician_id)

                    # اگر تکنسین تغییر کرده، کیف پول تکنسین قبلی را برگردان
                    if expense.technician and expense.technician.id != new_technician_id:
                        self.revert_technician_wallet(expense, old_data['expense_type'], exchange_rate)

                    # محاسبه تغییرات برای تکنسین جدید
                    old_amount = Decimal(str(expense.cost_dollar)) if expense.currency_type == 'dollar' else Decimal(
                        str(expense.cost_dinar))
                    new_amount = new_cost_dollar if data.get('currency_type') == 'dollar' else new_cost_dinar

                    wallet_change = calculate_wallet_change(
                        old_amount,
                        new_amount,
                        data.get('currency_type'),
                        cost_type == 'loantechnician'
                    )

                    # اعمال تغییرات کیف پول تکنسین
                    new_technician.wallet = (new_technician.wallet if new_technician.wallet is not None else Decimal(
                        '0')) + wallet_change
                    new_technician.save()
                    expense.technician = new_technician
                else:
                    expense.technician = None

            elif cost_type == 'medicalsell' and source_model == 'ExpenseItem':
                # برگرداندن مقدار قبلی به انبار
                if old_data['medical_name'] and Decimal(old_data['medical_quantity']) > 0:
                    self.return_medical_to_warehouse(old_data['medical_name'], Decimal(old_data['medical_quantity']))

                # کسر مقدار جدید از انبار
                medical_name = data.get('medical_name', '')
                medical_quantity = int(data.get('medical_quantity', 0))
                if medical_name and medical_quantity > 0:
                    self.deduct_medical_from_warehouse(medical_name, medical_quantity)

                expense.medical_name = medical_name
                expense.medical_quantity = medical_quantity

            # ذخیره مقادیر جدید هزینه
            expense.cost_dollar = new_cost_dollar
            expense.cost_dinar = new_cost_dinar
            expense.save()

            # ایجاد لاگ برای تغییرات
            self.create_expense_change_log(request, expense, old_data, source_model)

            logger.info(f"Updated expense item: {expense.id}")

            return JsonResponse({
                'success': True,
                'message': 'Expense updated successfully'
            })

        except Exception as e:
            logger.error(f"Error updating expense: {e}", exc_info=True)
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=400)

    def revert_old_wallet_changes(self, expense, old_data, exchange_rate):
        """برگرداندن تغییرات کیف پول برای مقدار قبلی"""
        if old_data['expense_type'] in ['doctor', 'loandoctor'] and old_data['doctor']:
            wallet_change = Decimal(old_data['cost_dollar']) if old_data['currency_type'] == 'dollar' \
                else Decimal(old_data['cost_dinar']) / exchange_rate
            if old_data['expense_type'] == 'loandoctor':
                wallet_change *= Decimal('-1')

            doctor = Doctor.objects.get(id=old_data['doctor'])
            doctor.wallet = (doctor.wallet if doctor.wallet is not None else Decimal('0')) - wallet_change
            doctor.save()

        elif old_data['expense_type'] in ['technician', 'loantechnician'] and old_data['technician']:
            wallet_change = Decimal(old_data['cost_dollar']) if old_data['currency_type'] == 'dollar' \
                else Decimal(old_data['cost_dinar']) / exchange_rate
            if old_data['expense_type'] == 'loantechnician':
                wallet_change *= Decimal('-1')

            technician = Technician.objects.get(id=old_data['technician'])
            technician.wallet = (technician.wallet if technician.wallet is not None else Decimal('0')) - wallet_change
            technician.save()

    def revert_doctor_wallet(self, expense, old_type, exchange_rate):
        """برگرداندن تغییرات کیف پول دکتر قبلی"""
        if expense.doctor:
            wallet_change = Decimal(str(expense.cost_dollar)) if expense.currency_type == 'dollar' \
                else Decimal(str(expense.cost_dinar)) / exchange_rate
            if old_type == 'loandoctor':
                wallet_change *= Decimal('-1')

            expense.doctor.wallet = (expense.doctor.wallet if expense.doctor.wallet is not None else Decimal(
                '0')) - wallet_change
            expense.doctor.save()

    def revert_technician_wallet(self, expense, old_type, exchange_rate):
        """برگرداندن تغییرات کیف پول تکنسین قبلی"""
        if expense.technician:
            wallet_change = Decimal(str(expense.cost_dollar)) if expense.currency_type == 'dollar' \
                else Decimal(str(expense.cost_dinar)) / exchange_rate
            if old_type == 'loantechnician':
                wallet_change *= Decimal('-1')

            expense.technician.wallet = (
                                            expense.technician.wallet if expense.technician.wallet is not None else Decimal(
                                                '0')) - wallet_change
            expense.technician.save()

    def return_medical_to_warehouse(self, medical_name, quantity):
        """برگرداندن مقدار قبلی به انبار"""
        from .models import WarehouseItem
        items = WarehouseItem.objects.filter(name=medical_name).order_by('expiry_date')
        remaining = quantity

        for item in items:
            if remaining <= 0:
                break
            item.quantity += remaining
            if item.quantity > item.initial_quantity:
                remaining = item.quantity - item.initial_quantity
                item.quantity = item.initial_quantity
            else:
                remaining = 0
            item.save()

    def deduct_medical_from_warehouse(self, medical_name, quantity):
        """کسر مقدار جدید از انبار"""
        from .models import WarehouseItem
        items = WarehouseItem.objects.filter(name=medical_name).order_by('expiry_date')
        remaining = quantity

        for item in items:
            if remaining <= 0:
                break
            if item.quantity >= remaining:
                item.quantity -= remaining
                remaining = 0
            else:
                remaining -= item.quantity
                item.quantity = 0
            item.save()

    def create_expense_change_log(self, request, expense, old_data, source_model):
        """ایجاد لاگ برای تغییرات با استفاده از مدل ExpenseChangeLog"""
        try:
            expense_medical_name = expense.medical_name
            expense_medical_quantity = str(expense.medical_quantity)
        except:
            expense_medical_name = ''
            expense_medical_quantity = ''
        new_data = {
            'date': str(expense.date),
            'expense_type': expense.expense_type,
            'currency_type': expense.currency_type,
            'description': expense.description,
            'cost_dollar': str(expense.cost_dollar),
            'cost_dinar': str(expense.cost_dinar),
            'doctor': str(expense.doctor.id) if expense.doctor else None,
            'technician': str(expense.technician.id) if expense.technician else None,
            'medical_name': expense_medical_name,
            'medical_quantity': expense_medical_quantity,
        }

        # شناسایی فیلدهای تغییر یافته
        changed_fields = []
        for field in old_data:
            if str(old_data[field]) != str(new_data[field]):
                changed_fields.append(field)

        # ایجاد لاگ
        change_log = ExpenseChangeLog(
            action_type='update',
            changed_by=request.user if request.user.is_authenticated else None,
            source_model=source_model,
            old_data=json.dumps(old_data, ensure_ascii=False),
            new_data=json.dumps(new_data, ensure_ascii=False),
            changed_fields=','.join(changed_fields),
            description=f"Expense {expense.id} updated"
        )

        # تنظیم رابطه با مدل مناسب
        if source_model == 'ExpensePaid':
            change_log.expense_paid = expense
        else:
            change_log.expense_item = expense

        change_log.save()

class ExpenseItemFilterMixin:
    """Mixin for filtering expense items"""

    def filter_expenses(self, queryset, filters):
        print("queryset=", queryset)
        print("filters=", filters)

        """Apply filters to expense queryset"""
        expense_type = filters.get('expense_type')
        from_date = filters.get('from_date')
        to_date = filters.get('to_date')
        service_id = filters.get('service_id')
        model_has_reservation = hasattr(queryset.model, 'reservation')
        if service_id:
            if model_has_reservation:
                queryset = queryset.filter(
                    reservation__isnull=False,
                    reservation__service_id=service_id
                )
                if from_date and to_date:
                    try:
                        queryset = queryset.filter(date__range=[from_date, to_date])
                    except ValueError as e:
                        print(f"Invalid date range: {e}")
                for item in queryset:
                    print("item=", item.reservation)
                return queryset
            else:
                return []

        if expense_type and expense_type != 'all':
            queryset = queryset.filter(expense_type=expense_type)
        # if expense_type and expense_type == 'cost':
        #     queryset = queryset.exclude(expense_type='salary')  # فقط هزینه‌ها
        # if expense_type and expense_type == 'salary':
        #     queryset = queryset.filter(expense_type='salary')  # فقط حقوق‌ها

        if from_date and to_date:
            try:
                queryset = queryset.filter(date__range=[from_date, to_date])
            except ValueError as e:
                print(f"Invalid date range: {e}")

        return queryset


from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def reservations_stat(request):
    if request.method != 'POST':
        return JsonResponse({'success': False, 'error': 'Only POST allowed'}, status=405)

    try:
        data = json.loads(request.body)
        from_date = data.get('from_date')
        to_date = data.get('to_date')

        if not from_date or not to_date:
            return JsonResponse({'success': False, 'error': 'from_date and to_date are required'}, status=400)

        # فیلتر رزروهای completed در بازه تاریخ
        reservations = Reservation.objects.select_related('doctor', 'service', 'patient').filter(
            status='completed',
            reservation_date__range=[from_date, to_date]
        )

        result = []
        for r in reservations:
            result.append({
                'paient_price': float(r.paient_price or 0),
                'deposit': float(r.deposit or 0),
                'date': r.reservation_date.strftime('%Y-%m-%d') if r.reservation_date else None,
                'doctor': r.doctor.get_full_name() if r.doctor else None,
                'service': r.service.name if r.service else None,
                'patient': r.patient.get_full_name() if r.patient else None,
            })

        return JsonResponse({'success': True, 'items': result})

    except Exception as e:
        return JsonResponse({'success': False, 'error': str(e)}, status=500)


@method_decorator(csrf_exempt, name='dispatch')
class FilterExpenseItemsView(View, ExpenseItemFilterMixin):
    """API view for filtering expense items"""

    def post(self, request):
        try:
            data = json.loads(request.body)

            # Get all expense items
            # items = ExpenseItem.objects.all().delete()
            # items = ExpensePaid.objects.all().delete()
            items = ExpenseItem.objects.select_related('doctor', 'technician', 'reservation').all()
            # items = ExpenseItem.objects.all()
            # Apply filters
            items = self.filter_expenses(items, data)

            salary_items = ExpensePaid.objects.select_related('doctor', 'technician').all()

            # فیلتر مشابه با items
            salary_items = self.filter_expenses(salary_items, data)

            # Combine expense + salary
            all_items = list(items) + list(salary_items)

            # محاسبه مجموع جدید
            total = 0
            for item in all_items:
                try:
                    total += float(item.cost_dollar or 0)
                except (ValueError, TypeError):
                    logger.warning(f"Invalid cost_dollar: {item.cost_dollar} for item id: {item.id}")

            # serialize
            result = []
            for item in all_items:
                is_salary = isinstance(item, ExpensePaid)
                result.append({
                    'id': item.id,
                    'date': item.date.strftime('%Y-%m-%d'),
                    'description': item.description,
                    'cost': float(item.cost_dollar) if item.cost_dollar is not None else 0.0,
                    'type': 'salary' if is_salary else item.expense_type,
                    'doctor': item.doctor.get_full_name() if item.doctor else None,
                    'doctor_id': item.doctor.id if item.doctor else None,
                    'technician': item.technician.get_full_name() if item.technician else None,
                    'technician_id': item.technician.id if item.technician else None,
                    'reservation': None if is_salary else (
                        item.reservation.service.name + " Service (" + item.reservation.patient.get_full_name() + ")"
                        if item.reservation else None
                    ),
                    'reservation_id': None if is_salary else (
                        item.reservation.id  if item.reservation else None
                    ),
                })
            # print("result=",result)
            services = Service.objects.all()
            return JsonResponse({
                'success': True,
                # 'services':services,
                'items': result,
                'total': float(total),
                'count': len(result)
            })

        except json.JSONDecodeError:
            return JsonResponse({
                'success': False,
                'error': 'Invalid JSON data'
            }, status=400)
        except Exception as e:
            logger.error(f"Error filtering expenses: {e}")
            return JsonResponse({
                'success': False,
                'error': str(e)
            }, status=400)


@method_decorator(csrf_exempt, name='dispatch')
class CreateExpensePaidView(View):
    """API view for creating expense paid items"""

    def post(self, request):
        logger.info("Starting CreateExpensePaidView")
        try:
            data = json.loads(request.body)
            logger.debug(f"Received data: {data}")

            exchange = ExchangeRate.objects.last()
            exchange_rate = float(exchange.rate)

            # Extract and validate data
            date_str = data.get("date")
            expense_type = data.get("cost_type")
            currency_type = data.get("currency_type", "dollar")
            description = data.get("description", "")
            cost_dollar = data.get("cost_dollar")
            cost_dinar = data.get("cost_dinar")
            doctor_id = data.get("doctor_id", None)
            technician_id = data.get("technician_id", None)

            # Validation
            required_fields = [date_str, expense_type]
            if not all(required_fields) or cost_dollar is None or cost_dinar is None:
                error_msg = "All required fields must be filled"
                logger.warning(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)

            if expense_type == 'doctor' and not doctor_id:
                error_msg = "The doctor must be selected."
                logger.warning(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)

            if expense_type == 'technician' and not technician_id:
                error_msg = "The technician must be selected."
                logger.warning(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)

            # Parse date
            try:
                date = parse_date(date_str)
                if not date:
                    raise ValueError("Invalid date format")
            except ValueError:
                error_msg = "Invalid date format"
                logger.warning(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)

            expense = None
            try:
                if expense_type == 'doctor':
                    doctor = Doctor.objects.get(id=int(doctor_id))
                    price_ = cost_dollar if currency_type == "dollar" else cost_dinar / exchange_rate

                    # Update doctor wallet
                    wallet = doctor.wallet if doctor.wallet else Decimal('0')
                    doctor.wallet = wallet - Decimal(str(price_))
                    doctor.save()

                    # Send WhatsApp notification
                    send_wallet_notification(
                        recipient_type='doctor',
                        person=doctor,
                        amount=price_,
                        wallet_after=doctor.wallet,
                        phone=doctor.country_code+doctor.phone_number
                    )

                    # Create expense
                    expense = ExpensePaid.objects.create(
                        date=date,
                        expense_type=expense_type,
                        currency_type=currency_type,
                        description=description,
                        cost_dollar=cost_dollar,
                        cost_dinar=cost_dinar,
                        doctor=doctor,
                        created_by=request.user if request.user.is_authenticated else None
                    )

                elif expense_type == 'technician':
                    technician = Technician.objects.get(id=int(technician_id))
                    price_ = cost_dollar if currency_type == "dollar" else cost_dinar / exchange_rate

                    # Update technician wallet
                    wallet = technician.wallet if technician.wallet else Decimal('0')
                    technician.wallet = wallet - Decimal(str(price_))
                    technician.save()

                    # Send WhatsApp notification
                    send_wallet_notification(
                        recipient_type='technician',
                        person=technician,
                        amount=price_,
                        wallet_after=technician.wallet,
                        phone=technician.country_code+technician.phone_numbertechnician
                    )

                    # Create expense
                    expense = ExpensePaid.objects.create(
                        date=date,
                        expense_type=expense_type,
                        currency_type=currency_type,
                        description=description,
                        cost_dollar=cost_dollar,
                        cost_dinar=cost_dinar,
                        technician=technician,
                        created_by=request.user if request.user.is_authenticated else None
                    )

                # Create log entry
                if expense:
                    self.create_expense_log(
                        request=request,
                        expense=expense,
                        action_type='create',
                        source_model='ExpensePaid'
                    )

                    logger.info(f"Successfully created expense paid: {expense.id}")
                    return JsonResponse({
                        "success": True,
                        "id": expense.id,
                        "message": "Expense item created successfully"
                    })
                else:
                    error_msg = "Failed to create expense item"
                    logger.error(error_msg)
                    return JsonResponse({
                        "success": False,
                        "error": error_msg
                    }, status=400)

            except Doctor.DoesNotExist:
                error_msg = "Selected doctor not found"
                logger.error(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)
            except Technician.DoesNotExist:
                error_msg = "Selected technician not found"
                logger.error(error_msg)
                return JsonResponse({
                    "success": False,
                    "error": error_msg
                }, status=400)
            except Exception as e:
                error_msg = f"Error creating expense: {str(e)}"
                logger.error(error_msg, exc_info=True)
                return JsonResponse({
                    "success": False,
                    "error": "Internal server error"
                }, status=500)

        except json.JSONDecodeError:
            error_msg = "Invalid JSON data"
            logger.error(error_msg)
            return JsonResponse({
                "success": False,
                "error": error_msg
            }, status=400)
        except Exception as e:
            error_msg = f"Unexpected error: {str(e)}"
            logger.error(error_msg, exc_info=True)
            return JsonResponse({
                "success": False,
                "error": "Internal server error"
            }, status=500)



    def create_expense_log(self, request, expense, action_type, source_model):
        """Create a log entry for expense creation"""
        try:
            new_data = {
                'date': str(expense.date),
                'expense_type': expense.expense_type,
                'currency_type': expense.currency_type,
                'description': expense.description,
                'cost_dollar': str(expense.cost_dollar),
                'cost_dinar': str(expense.cost_dinar),
                'doctor': str(expense.doctor.id) if expense.doctor else None,
                'technician': str(expense.technician.id) if expense.technician else None,
            }

            log_entry = ExpenseChangeLog(
                action_type=action_type,
                changed_by=request.user if request.user.is_authenticated else None,
                source_model=source_model,
                old_data=None,  # For create action, old_data is None
                new_data=json.dumps(new_data, ensure_ascii=False),
                changed_fields=','.join(new_data.keys()),
                description=f"New expense paid {expense.id} created",
                expense_paid=expense
            )
            log_entry.save()
            logger.debug(f"Created log entry for expense paid: {expense.id}")

        except Exception as e:
            logger.error(f"Failed to create expense log: {str(e)}")


@method_decorator(csrf_exempt, name='dispatch')
class CreateExpenseItemView(View):
    """API view for creating expense items"""

    def post(self, request):
        print("in CreateExpenseItemView")
        try:
            data = json.loads(request.body)
            print("data=", data)
            exchange = ExchangeRate.objects.last()
            exchange_rate = float(exchange.rate)

            # Extract and validate data
            date_str = data.get("date")
            expense_type = data.get("cost_type")
            currency_type = data.get("currency_type", "dollar")
            description = data.get("description", "")
            cost_dollar = data.get("cost_dollar")
            cost_dinar = data.get("cost_dinar")

            doctor_id = int(data.get("doctor_id")) if data.get("doctor_id") else ''
            technician_id = int(data.get("technician_id")) if data.get("technician_id") else ''
            cat_id = int(data.get("cat_id")) if data.get("cat_id") else ''
            cat_quantity = data.get("cat_quantity", 0)
            cat_quantity = int(cat_quantity) if cat_quantity else 0

            medical_quantity = 0
            medical_name = ''
            if cat_id:
                cat_obj = Category.objects.filter(id=int(cat_id))
                if not cat_obj.exists():
                    return JsonResponse({
                        "success": False,
                        "error": "The medical is not in available!"
                    }, status=400)
                warehouses = WarehouseItem.objects.filter(category_id=cat_obj)
                available_quantity = 0
                for warehouse in warehouses:
                    available_quantity += warehouse.quantity
                    medical_name = warehouse.name
                if cat_quantity > available_quantity:
                    return JsonResponse({
                        "success": False,
                        "error": "The medical quantity is more of available!"
                    }, status=400)
                medical_quantity = cat_quantity
                for warehouse in warehouses:
                    if cat_quantity > 0:
                        if cat_quantity > warehouse.quantity:
                            warehouse.quantity = 0
                            warehouse.save()
                            cat_quantity = cat_quantity - warehouse.quantity
                        else:
                            warehouse.quantity = warehouse.quantity - cat_quantity
                            warehouse.save()
                            cat_quantity = 0

            # Validation
            required_fields = [date_str, expense_type]
            if not all(required_fields) or cost_dollar is None or cost_dinar is None:
                return JsonResponse({
                    "success": False,
                    "error": "All required fields must be filled"
                }, status=400)

            if expense_type == 'medicalsell':
                if not cat_id or not cat_quantity:
                    return JsonResponse({
                        "success": False,
                        "error": "The medical type and its quantity must be selected."
                    }, status=400)
            if expense_type == 'doctor' or expense_type == 'loandoctor':
                if not doctor_id:
                    return JsonResponse({
                        "success": False,
                        "error": "The doctor must be selected."
                    }, status=400)
            if expense_type == 'technician' or expense_type == 'loantechnician':
                if not technician_id:
                    return JsonResponse({
                        "success": False,
                        "error": "The technician must be selected."
                    }, status=400)

            # Parse date
            try:
                date = parse_date(date_str)
                if not date:
                    raise ValueError("Invalid date format")
            except ValueError:
                return JsonResponse({
                    "success": False,
                    "error": "Invalid date format"
                }, status=400)

            # Get related objects
            doctor = None
            technician = None

            if expense_type == 'doctor':
                if not doctor_id:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)
                try:
                    doctor = Doctor.objects.get(id=doctor_id)
                    price_ = 0
                    if currency_type == "dollar":
                        price_ = cost_dollar
                    else:
                        price_ = cost_dinar / exchange_rate
                    wallet_ = doctor.wallet if doctor.wallet else 0
                    doctor.wallet = wallet_ + price_
                    doctor.save()

                    send_wallet_cost_notification(
                        recipient_type='doctor',
                        person=doctor,
                        amount=price_,
                        wallet_after=doctor.wallet,
                        phone=doctor.country_code+doctor.phone_number
                    )
                except Doctor.DoesNotExist:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)

            if expense_type == 'technician':
                if not technician_id:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected technician not found"
                    }, status=400)
                try:
                    technician = Technician.objects.get(id=technician_id)
                    price_ = 0
                    if currency_type == "dollar":
                        price_ = cost_dollar
                    else:
                        price_ = cost_dinar / exchange_rate
                    wallet_ = technician.wallet if technician.wallet else 0
                    technician.wallet = wallet_ + price_
                    technician.save()
                    send_wallet_cost_notification(
                        recipient_type='technician',
                        person=technician,
                        amount=price_,
                        wallet_after=technician.wallet,
                        phone=technician.country_code+technician.phone_number,
                    )
                except Doctor.DoesNotExist:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)
            if expense_type == 'loandoctor':
                if not doctor_id:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)
                try:
                    doctor = Doctor.objects.get(id=doctor_id)
                    price_ = 0
                    if currency_type == "dollar":
                        price_ = cost_dollar
                    else:
                        price_ = cost_dinar / exchange_rate
                    wallet_ = doctor.wallet if doctor.wallet else 0
                    doctor.wallet = wallet_ - price_
                    doctor.save()
                except Doctor.DoesNotExist:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)

            if expense_type == 'loantechnician':
                if not technician_id:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected technician not found"
                    }, status=400)
                try:
                    technician = Technician.objects.get(id=technician_id)
                    price_ = 0
                    if currency_type == "dollar":
                        price_ = cost_dollar
                    else:
                        price_ = cost_dinar / exchange_rate
                    wallet_ = technician.wallet if technician.wallet else 0
                    technician.wallet = wallet_ - price_
                    technician.save()
                except Doctor.DoesNotExist:
                    return JsonResponse({
                        "success": False,
                        "error": "Selected doctor not found"
                    }, status=400)

            # Create expense item
            try:
                expense = ExpenseItem.objects.create(
                    date=date,
                    expense_type=expense_type,
                    currency_type=currency_type,
                    description=description,
                    cost_dollar=cost_dollar,
                    cost_dinar=cost_dinar,
                    medical_name=medical_name,
                    medical_quantity=medical_quantity,
                    doctor=doctor,
                    technician=technician,
                    created_by=request.user if request.user.is_authenticated else None
                )

                # Create log entry
                self.create_expense_log(
                    request=request,
                    expense=expense,
                    action_type='create',
                    source_model='ExpenseItem'
                )

                logger.info(f"Created expense item: {expense}")

                return JsonResponse({
                    "success": True,
                    "id": expense.id,
                    "message": "Expense item created successfully"
                })

            except Exception as e:
                logger.error(f"Error creating expense: {e}")
                return JsonResponse({
                    "success": False,
                    "error": str(e)
                }, status=500)

        except json.JSONDecodeError:
            return JsonResponse({
                "success": False,
                "error": "Invalid JSON data"
            }, status=400)
        except Exception as e:
            logger.error(f"Error in CreateExpenseItemView: {e}")
            return JsonResponse({
                "success": False,
                "error": "Internal server error"
            }, status=500)

    def create_expense_log(self, request, expense, action_type, source_model):
        """Create a log entry for expense changes"""
        try:
            new_data = {
                'date': str(expense.date),
                'expense_type': expense.expense_type,
                'currency_type': expense.currency_type,
                'description': expense.description,
                'cost_dollar': str(expense.cost_dollar),
                'cost_dinar': str(expense.cost_dinar),
                'doctor': str(expense.doctor.id) if expense.doctor else None,
                'technician': str(expense.technician.id) if expense.technician else None,
                'medical_name': expense.medical_name if hasattr(expense, 'medical_name') else None,
                'medical_quantity': str(expense.medical_quantity) if hasattr(expense, 'medical_quantity') else None,
            }
    
            log_entry = ExpenseChangeLog(
                action_type=action_type,
                changed_by=request.user if request.user.is_authenticated else None,
                source_model=source_model,
                old_data=None if action_type == 'create' else json.dumps(old_data, ensure_ascii=False),
                new_data=json.dumps(new_data, ensure_ascii=False),
                changed_fields=','.join(new_data.keys()),
                description=f"Expense {expense.id} {action_type}d",
            )
    
            # تنظیم رابطه صحیح بر اساس نوع مدل
            if source_model == 'ExpensePaid':
                log_entry.expense_paid = expense
            else:
                log_entry.expense_item = expense
    
            log_entry.save()
            logger.debug(f"Created log entry for {source_model}: {expense.id}")
            
        except Exception as e:
            logger.error(f"Failed to create expense log: {str(e)}", exc_info=True)


class AccountCostListView(View, ExpenseItemFilterMixin):
    """View for displaying expense list with pagination and filtering"""

    template_name = 'accountant/accounting-cost.html'

    def get(self, request):
        try:
            # Get filter parameters
            from_date_str = request.GET.get('from_date')
            to_date_str = request.GET.get('to_date')

            # Parse dates
            from_date = None
            to_date = None

            if from_date_str:
                try:
                    from_date = datetime.strptime(from_date_str, "%Y-%m-%d").date()
                except ValueError:
                    messages.error(request, "Invalid from date format")

            if to_date_str:
                try:
                    to_date = datetime.strptime(to_date_str, "%Y-%m-%d").date()
                except ValueError:
                    messages.error(request, "Invalid to date format")

            # Get expenses with related data
            expenses = Expense.objects.select_related().prefetch_related(
                'doctors', 'technicians', 'reservation'
            ).all()

            # Apply date filter
            if from_date and to_date:
                expenses = expenses.filter(date__range=(from_date, to_date))

            # Calculate total
            sum_dollar = 0
            for item in expenses:
                sum_dollar += item.cost_dollar if item.cost_dollar else 0

            # Prepare expenses with details
            expenses_with_details = []
            for expense in expenses:
                expenses_with_details.append({
                    'expense': expense,
                    'doctors_and_costs': expense.get_doctors_and_costs(),
                    'technicians_and_costs': expense.get_technicians_and_costs(),
                    'other_costs_and_descriptions': expense.get_other_costs_and_descriptions(),
                })

            # Pagination
            paginator = Paginator(expenses_with_details, 8)
            page_number = request.GET.get('page', 1)

            try:
                page_obj = paginator.get_page(page_number)
            except Exception:
                page_obj = paginator.get_page(1)
            services = Service.objects.all()
            context = {
                'expenses_with_details': page_obj,
                'from_date': from_date,
                'to_date': to_date,
                'sum_dollar': sum_dollar,
                'total_pages': paginator.num_pages,
                'current_page': page_obj.number,
                'services': services
            }
            print("context=", context)

            return render(request, self.template_name, context)

        except Exception as e:
            logger.error(f"Error in AccountCostListView: {e}")
            messages.error(request, "An error occurred while loading expenses")
            return render(request, self.template_name, {'expenses_with_details': []})


class AccountCostCreateView(View):
    template_name = 'accountant/accounting-cost-create.html'

    def get(self, request):
        expense_form = ExpenseForm()
        doctor_formset = ExpenseDoctorFormSet(prefix='doctors')
        technician_formset = ExpenseTechnicianFormSet(prefix='technicians')
        other_cost_formset = ExpenseOtherCostFormSet(prefix='other_costs')
        context = {
            'expense_form': expense_form,
            'doctor_formset': doctor_formset,
            'technician_formset': technician_formset,
            'other_cost_formset': other_cost_formset
        }
        return render(request, self.template_name, context)

    def post(self, request):
        expense_form = ExpenseForm(request.POST)
        doctor_formset = ExpenseDoctorFormSet(request.POST, prefix='doctors')
        technician_formset = ExpenseTechnicianFormSet(request.POST, prefix='technicians')
        other_cost_formset = ExpenseOtherCostFormSet(request.POST, prefix='other_costs')

        error_forms = []
        if not expense_form.is_valid():
            error_forms.append((expense_form.errors, 'Expense'))

        if not doctor_formset.is_valid():
            error_forms.append((doctor_formset.errors, 'Doctor'))

        if not technician_formset.is_valid():
            error_forms.append((technician_formset.errors, 'Technician'))

        if not other_cost_formset.is_valid():
            error_forms.append((other_cost_formset.errors, 'Other Costs'))

        # If any form has errors, add them to messages and return early
        if error_forms:
            for error, name in error_forms:
                messages.error(request, error)

            messages.error(request, "Please correct the errors below.")
            return redirect('accountant:cost-create')

        if (expense_form.is_valid() and doctor_formset.is_valid() and
                technician_formset.is_valid() and other_cost_formset.is_valid()):

            # Use transaction.atomic to ensure atomicity of the operation
            with transaction.atomic():
                # Save the main expense instance
                expense = expense_form.save(commit=False)
                expense.created_by = request.user  # Set the user who created the expense
                expense.save()

                # Set the parent instance for all formsets
                doctor_formset.instance = expense
                technician_formset.instance = expense
                other_cost_formset.instance = expense

                for form in doctor_formset:
                    if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                        doctor = form.save(commit=False)
                        doctor.expense = expense
                        doctor.save()

                if len(request.POST.getlist('doctors-2-doctor')) >= 2:
                    for doctor_data in zip(request.POST.getlist('doctors-2-doctor')[:-1],
                                           request.POST.getlist('doctors-2-cost_dollar')[:-1]):
                        # Handle this set of doctors in the same form

                        doctor = ExpenseDoctor(
                            expense=expense,
                            doctor=Doctor.objects.get(id=int(doctor_data[0])),
                            cost_dollar=doctor_data[1],

                        )
                        doctor.save()
                else:
                    None

                # Save technician formset rows
                for form in technician_formset:
                    if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                        technician = form.save(commit=False)
                        technician.expense = expense
                        technician.save()

                if len(request.POST.getlist('technicians-2-technician')) >= 2:
                    for technician_data in zip(request.POST.getlist('technicians-2-technician')[:-1],
                                               request.POST.getlist('technicians-2-cost_dollar')[:-1]):
                        technician = ExpenseTechnician(
                            expense=expense,
                            technician=Technician.objects.get(id=int(technician_data[0])),
                            cost_dollar=technician_data[1],

                        )
                        technician.save()
                else:
                    None

                # Save other_cost formset rows
                for form in other_cost_formset:
                    if form.cleaned_data and not form.cleaned_data.get('DELETE', False):
                        other_cost = form.save(commit=False)
                        other_cost.expense = expense
                        other_cost.save()

                if len(request.POST.getlist('other_costs-2-description')) >= 2:
                    for other_costs_data in zip(request.POST.getlist('other_costs-2-description')[:-1],
                                                request.POST.getlist('other_costs-2-cost_dollar')[:-1]):
                        other_costs = ExpenseOtherCost(
                            expense=expense,
                            description=other_costs_data[0],
                            cost_dollar=other_costs_data[1],

                        )
                        other_costs.save()
                else:
                    None

            messages.success(request, ' New Cost Successfully added')
            return redirect('accountant:cost-view')

        else:
            # Handle form errors
            context = {
                'expense_form': expense_form,
                'doctor_formset': doctor_formset,
                'technician_formset': technician_formset,
                'other_cost_formset': other_cost_formset
            }
            messages.error(request, '%s' % expense_form.errors)
            return render(request, self.template_name, context)


class AccountingReportView(View):
    template_name = "accountant/accounting-report.html"

    def get(self, request):
        today = timezone.now().date()
        first_day_of_month = today.replace(day=1)

        # فیلترهای ورودی
        quick_filter = request.GET.get('quick_filter')
        year = request.GET.get('year') or ''
        month = request.GET.get('month') or ''
        from_date_str = request.GET.get('fromDate')
        to_date_str = request.GET.get('toDate')
        service_id = request.GET.get('serviceFilter', '')

        from_date = to_date = None

        # --- اعمال فیلتر سریع
        if quick_filter == "today":
            from_date = to_date = today
        elif quick_filter == "this_month":
            from_date = first_day_of_month
            to_date = today
        elif year.isdigit() and month.isdigit():
            try:
                from_date = date(int(year), int(month), 1)
                last_day = calendar.monthrange(int(year), int(month))[1]
                to_date = date(int(year), int(month), last_day)
            except:
                pass

        # --- فیلتر بر اساس تاریخ‌های دستی
        try:
            if from_date_str:
                from_date = datetime.strptime(from_date_str, '%Y-%m-%d').date()
            if to_date_str:
                to_date = datetime.strptime(to_date_str, '%Y-%m-%d').date()
        except ValueError:
            pass

        # --- تبدیل service_id به عدد


        # --- اطلاعات اولیه
        reservations = Reservation.objects.filter(status="completed")
        warehouses = ReservationWarehouse.objects.filter(reservation__in=reservations)
        paid_expenses = ExpensePaid.objects.all()
        item_expenses = ExpenseItem.objects.exclude(expense_type='reservation')

        # --- فیلتر بر اساس سرویس (نمایش فقط رزروها و انبار)
        try:
            service_id_int = int(service_id)
            if service_id_int != 0:
                reservations = reservations.filter(service_id=service_id_int)
                warehouses = warehouses.filter(reservation__in=reservations)
                # هزینه‌های دیگر را در نظر نمی‌گیریم
                paid_expenses = ExpensePaid.objects.none()
                item_expenses = ExpenseItem.objects.none()
        except (ValueError, TypeError):
            service_id_int = 0
            if service_id == 'all':
                # هزینه‌های دیگر را در نظر نمی‌گیریم
                paid_expenses = ExpensePaid.objects.none()
                item_expenses = ExpenseItem.objects.none()


        # --- فیلتر زمانی
        if from_date and to_date:
            reservations = reservations.filter(reservation_date__range=(from_date, to_date))
            warehouses = warehouses.filter(reservation__reservation_date__range=(from_date, to_date))
            paid_expenses = paid_expenses.filter(date__range=(from_date, to_date))
            item_expenses = item_expenses.filter(date__range=(from_date, to_date))

        # --- پردازش داده‌ها
        data_by_date = defaultdict(lambda: {'income': 0, 'cost': 0, 'services': '', 'cost_info': ''})
        all_dates = set()

        for res in reservations:
            date_ = res.reservation_date
            income = (res.paient_price or 0) + (res.deposit or 0)
            cost = (res.doctor_price or 0) + (res.technician_price or 0)

            data_by_date[date_]['income'] += income
            data_by_date[date_]['cost'] += cost
            data_by_date[date_]['services'] += f"{res.service.name} , "
            all_dates.add(date_)

        for wh in warehouses:
            date_ = wh.reservation.reservation_date
            warehouse_cost = (wh.quantity or 0) * (wh.price or 0)
            data_by_date[date_]['cost'] += warehouse_cost
            all_dates.add(date_)

        # for exp in paid_expenses:
        #     date_ = exp.date
        #     data_by_date[date_]['cost'] += exp.cost_dollar or 0
        #     all_dates.add(date_)

        for exp in item_expenses:
            date_ = exp.date
            data_by_date[date_]['cost'] += exp.cost_dollar or 0
            data_by_date[date_]['cost_info'] += f"{exp.expense_type} , "
            all_dates.add(date_)

        # --- داده‌ها برای نمایش
        income_dict = {d: data_by_date[d]['income'] for d in data_by_date}
        cost_dict = {d: data_by_date[d]['cost'] for d in data_by_date}
        service_dict = {d: data_by_date[d]['services'] for d in data_by_date}
        cost_info_dict = {d: data_by_date[d]['cost_info'] for d in data_by_date}

        dic3 = {
            'income': income_dict,
            'cost': cost_dict,
            'services': service_dict,
            'cost_info': cost_info_dict,
        }

        total_income = sum(income_dict.values())
        total_cost = sum(cost_dict.values())
        profit = total_income - total_cost

        # --- صفحه‌بندی
        sorted_dates = sorted(all_dates)
        paginator = Paginator(sorted_dates, 8)
        page_number = request.GET.get('page')
        page_dates = paginator.get_page(page_number)

        services = Service.objects.all()

        context = {
            'from_date': from_date_str or '',
            'to_date': to_date_str or '',
            'year': year,
            'month': month,
            'datas': page_dates,
            'dic3': dic3,
            'total_income': total_income,
            'total_cost': total_cost,
            'profit': profit,
            'services': services,
            'selected_service': service_id_int,
        }

        return render(request, self.template_name, context)



class CostDeleteView(SuccessMessageMixin, DeleteView):
    model = Expense
    success_url = reverse_lazy('accountant:cost-view')
    success_message = "Cost item deleted successfully."
    pk_url_kwarg = 'expense_id'

    def delete(self, request, *args, **kwargs):
        messages.success(self.request, self.success_message)
        return super().delete(request, *args, **kwargs)


class PatientIncomeReportView(View):
    template_name = 'accountant/patient-income-report.html'

    def get(self, request):
        year = request.GET.get('year')
        month = request.GET.get('month')
        service_id = request.GET.get('serviceFilter',0)
        monthly_income = []
        reservations = Reservation.objects.all()
        print("year,month=",year,month)
        if year and month:
            start_date = datetime(int(year), int(month), 1)
            print("start_date=",start_date)
            end_date = (start_date + timedelta(days=32)).replace(day=1)

            print("end_date=",end_date)
            reservations = reservations.filter(reservation_date__gte=start_date, reservation_date__lt=end_date)
        if service_id:
            reservations = reservations.filter( service_id=service_id)


        reservations = reservations.filter(status='completed')
        monthly_data = reservations.annotate(
            month=TruncMonth('reservation_date')
        ).values('month').annotate(
            total_income=Sum(F('paient_price') + F('deposit')),
            total_patients=Count('patient', distinct=True)
        ).order_by('month')
        total_income = 0
        print("monthly_data=",monthly_data)
        for data in monthly_data:

            print("data=", data)
            print("data['total_income']=", data['total_income'])
            print("data['total_patients']=", data['total_patients'])
            total_income += (data['total_income'] + data['total_patients'])
            month_start = data['month']
            print("month_start=", month_start)
            month_end = (month_start + timedelta(days=32)).replace(day=1)
            print("month_end=", month_end)
            total_patients_complete_services = Reservation.objects.filter(reservation_date__gte=month_start, reservation_date__lt=month_end,status='completed')
            total_patients_complete_services_count = total_patients_complete_services.count()
            services_name = []
            for item in total_patients_complete_services:
                print(item)
                print(item.service)
                print(item.service.name)
                services_name.append(item.service.name)
            total_patients_not_complete_services = Reservation.objects.filter(reservation_date__gte=month_start, reservation_date__lt=month_end).exclude(status='completed').count()
            total_patients_added_without_service = ''
            total_patients_added_with_service = ''
            total_patients_added = Patient.objects.filter(created_at__gte=month_start, created_at__lt=month_end)
            for patient in total_patients_added:
                res = Reservation.objects.filter(patient=patient)
                if res.exists():
                    total_patients_added_with_service += patient.get_full_name()+" ,"
                else:
                    total_patients_added_without_service += patient.get_full_name()+" ,"
            services_name_ = ''
            services_name = set(services_name)
            for item in services_name:
                services_name_ += item + ' , '
            print(services_name)
            monthly_income.append({
                'month': data['month'],
                'service_id':service_id if service_id else 0,
                'total_income': total_income,
                'total_patients_complete_services': total_patients_complete_services_count,
                'services_names': services_name_,
                'total_patients_not_complete_services': total_patients_not_complete_services,
                'total_patients_added_without_service':total_patients_added_without_service,
                'total_patients_added_with_service':total_patients_added_with_service
            })
            print("services_name_=",services_name_)
        services = Service.objects.all()
        context = {
            'monthly_income': monthly_income,
            'year': year,
            'month': month,
            'service_id':service_id if service_id else 0,
            'services': services,
        }
        print("context=",context)
        return render(request, self.template_name, context)


# class PatientIncomeReportView(View):
#     template_name = 'accountant/patient-income-report.html'
#
#     def get(self, request):
#         year = request.GET.get('year')
#         month = request.GET.get('month')
#         monthly_income = []
#
#         if year and month:
#             start_date = datetime(int(year), int(month), 1)
#             if int(month) == 12:
#                 end_date = datetime(int(year) + 1, 1, 1)
#             else:
#                 end_date = datetime(int(year), int(month) + 1, 1)
#
#             patient_services = PatientService.objects.filter(
#                 created_at__gte=start_date,
#                 created_at__lt=end_date,
#                 patientserviceitem__isnull=False
#             ).distinct()
#         else:
#             patient_services = PatientService.objects.filter(
#                 patientserviceitem__isnull=False
#             ).distinct()
#
#         monthly_data = patient_services.annotate(
#             month=TruncMonth('created_at')
#         ).values('month').annotate(
#             total_income=Sum(
#                 F('patientserviceitem__service__unit_price') *
#                 F('patientserviceitem__number_of_units') -
#                 F('patientserviceitem__discount'),
#                 output_field=DecimalField()
#             ),
#             total_patients_with_services=Count('patient', distinct=True)
#         ).order_by('month')
#
#         for data in monthly_data:
#             month_start = data['month']
#             month_end = (month_start + timedelta(days=32)).replace(day=1)
#             patients_added = Patient.objects.filter(
#                 created_at__gte=month_start,
#                 created_at__lt=month_end
#             ).count()
#
#             monthly_income.append({
#                 'month': data['month'],
#                 'total_income': data['total_income'] or 0,
#                 'total_patients_with_services': data['total_patients_with_services'],
#                 'total_patients_added': patients_added
#             })
#
#         context = {
#             'monthly_income': monthly_income,
#             'year': year,
#             'month': month,
#         }
#         return render(request, self.template_name, context)

class PatientIncomeDetailView(View):
    template_name = 'accountant/patient-income-detail.html'

    def get(self, request, year, month,service_id):
        reservations = Reservation.objects.filter(
            reservation_date__year=year,
            reservation_date__month=month
        )
        if service_id and service_id != 0:
            reservations = reservations.filter(service_id=int(service_id))

        for res in reservations:
            # درآمد کل بیمار (قیمت + ودیعه)
            income = (res.paient_price or 0) + (res.deposit or 0)

            # هزینه پرسنل (پزشک + تکنسین)
            staff_cost = (res.doctor_price or 0) + (res.technician_price or 0)

            # هزینه اقلام انبار مربوط به این رزرو
            warehouse_items = ReservationWarehouse.objects.filter(reservation=res)
            warehouse_cost = sum([(item.price or 0) * (item.quantity or 0) for item in warehouse_items])

            # جمع کل هزینه‌ها و سود
            total_cost = staff_cost + warehouse_cost
            profit = income - total_cost

            # اضافه کردن مقادیر محاسبه‌شده به آبجکت رزرو
            res.income = income
            res.total_cost = total_cost
            res.profit = profit

        context = {
            'reservations': reservations,
            'year': year,
            'month': month,
        }
        return render(request, self.template_name, context)


# class PatientIncomeDetailView(View):
#     template_name = 'accountant/patient-income-detail.html'
#
#     def get(self, request, year, month):
#         patient_services = PatientService.objects.filter(created_at__year=year, created_at__month=month)
#
#         context = {
#             'patient_services': patient_services,
#             'year': year,
#             'month': month,
#         }
#         return render(request, self.template_name, context)


class MonthlyFinanceReportView(View):
    template_name = 'accountant/monthly-finance-report.html'

    def get(self, request):
        today = timezone.now().date()
        first_day_of_month = today.replace(day=1)

        # فیلترهای ورودی
        quick_filter = request.GET.get('quick_filter')
        year_param = request.GET.get('year', '')
        month_param = request.GET.get('month', '')
        from_date_str = request.GET.get('fromDate')
        to_date_str = request.GET.get('toDate')
        service_id = request.GET.get('serviceFilter', '')

        year = int(year_param) if year_param.isdigit() else None
        month = int(month_param) if month_param.isdigit() else None
        from_date = to_date = None

        # --- اعمال فیلتر سریع
        if quick_filter == "today":
            from_date = to_date = today
        elif quick_filter == "this_month":
            from_date = first_day_of_month
            to_date = today
        elif year and month:
            try:
                from_date = date(year, month, 1)
                last_day = calendar.monthrange(year, month)[1]
                to_date = date(year, month, last_day)
            except:
                pass

        # --- فیلتر بر اساس تاریخ‌های دستی
        try:
            if from_date_str:
                from_date = datetime.strptime(from_date_str, '%Y-%m-%d').date()
            if to_date_str:
                to_date = datetime.strptime(to_date_str, '%Y-%m-%d').date()
        except ValueError:
            pass

        # --- تبدیل service_id به عدد
        try:
            service_id_int = int(service_id)
            if service_id_int == 0:
                service_id_int = None
        except (ValueError, TypeError):
            service_id_int = None
            if service_id == 'all':
                service_id_int = 'all'

        # --- اطلاعات اولیه
        reservations = Reservation.objects.filter(status="completed")
        warehouses = ReservationWarehouse.objects.filter(reservation__in=reservations)
        warehouses_sell = WarehouseItemHistory.objects.filter(history_type='sale')
        warehouses_sell_create = WarehouseItemHistory.objects.filter(history_type='creation')
        warehouses_sell_update = WarehouseItemHistory.objects.filter(history_type='update')
        paid_expenses = ExpensePaid.objects.all()
        item_expenses = ExpenseItem.objects.exclude(expense_type='reservation')

        # --- فیلتر بر اساس سرویس
        if service_id_int and service_id_int != 'all':
            reservations = reservations.filter(service_id=service_id_int)
            warehouses = warehouses.filter(reservation__in=reservations)
            # هزینه‌های دیگر را در نظر نمی‌گیریم
            paid_expenses = ExpensePaid.objects.none()
            item_expenses = ExpenseItem.objects.none()
        elif service_id == 'all':
            # هزینه‌های دیگر را در نظر نمی‌گیریم
            paid_expenses = ExpensePaid.objects.none()
            item_expenses = ExpenseItem.objects.none()

        # --- فیلتر زمانی
        if from_date and to_date:
            reservations = reservations.filter(reservation_date__range=(from_date, to_date))
            warehouses = warehouses.filter(reservation__reservation_date__range=(from_date, to_date))
            warehouses_sell = warehouses_sell.filter(changed_at__range=(from_date, to_date))
            warehouses_sell_create = warehouses_sell_create.filter(changed_at__range=(from_date, to_date))
            warehouses_sell_update = warehouses_sell_update.filter(changed_at__range=(from_date, to_date))
            paid_expenses = paid_expenses.filter(date__range=(from_date, to_date))
            item_expenses = item_expenses.filter(date__range=(from_date, to_date))

        # --- پردازش داده‌ها برای ماه‌ها
        data_by_month = defaultdict(lambda: {'income': 0, 'cost': 0, 'services': set()})
        all_months = set()

        # رزروها
        for res in reservations:
            month_key = res.reservation_date.replace(day=1)
            income = (res.paient_price or 0) + (res.deposit or 0)
            cost = (res.doctor_price or 0) + (res.technician_price or 0)

            data_by_month[month_key]['income'] += income
            data_by_month[month_key]['cost'] += cost
            data_by_month[month_key]['services'].add(res.service.name)
            all_months.add(month_key)
        print("data_by_month=",data_by_month)
        # انبار

        for wh in warehouses_sell:
            print("wh.changed_at=",wh.changed_at)
            print("wh.changed_at=",wh.changed_at.date())
            month_key = wh.changed_at.date().replace(day=1)
            warehouses_sell_income = -1 * (wh.quantity_changed or 0) * (wh.unit_price_changed or 0)
            print("warehouses_sell_income=",warehouses_sell_income)
            data_by_month[month_key]['income'] += warehouses_sell_income
            all_months.add(month_key)

        print("data_by_month[8]['income'=",data_by_month)

        for wh in warehouses:
            month_key = wh.reservation.reservation_date.replace(day=1)
            warehouse_cost = (wh.quantity or 0) * (wh.price or 0)
            data_by_month[month_key]['cost'] += warehouse_cost
            all_months.add(month_key)

        # for wh in warehouses_sell_create:
        #     month_key = wh.changed_at.date().replace(day=1)
        #     warehouses_sell_create_cost = (wh.quantity_changed or 0) * (wh.unit_price_changed or 0)
        #     data_by_month[month_key]['cost'] += warehouses_sell_create_cost
        #     all_months.add(month_key)
        #
        # for wh in warehouses_sell_update:
        #     month_key = wh.changed_at.date().replace(day=1)
        #     warehouses_sell_update_cost = (wh.quantity_changed or 0) * (wh.unit_price_changed or 0)
        #     data_by_month[month_key]['cost'] += warehouses_sell_update_cost
        #     all_months.add(month_key)

        # هزینه‌های پرداختی
        # for exp in paid_expenses:
        #     month_key = exp.date.replace(day=1)
        #     data_by_month[month_key]['cost'] += exp.cost_dollar or 0
        #     data_by_month[month_key]['services'].add('Paid Expense')
        #     all_months.add(month_key)

        # هزینه‌های آیتم
        for exp in item_expenses:
            month_key = exp.date.replace(day=1)
            data_by_month[month_key]['cost'] += exp.cost_dollar or 0
            data_by_month[month_key]['services'].add(exp.expense_type)
            all_months.add(month_key)

        # تبدیل به لیست برای نمایش
        monthly_data = []
        for month_key in sorted(all_months):
            income = data_by_month[month_key]['income']
            cost = data_by_month[month_key]['cost']
            services_text = ', '.join(data_by_month[month_key]['services'])

            monthly_data.append({
                'month': month_key,
                'income': income,
                'cost': cost,
                'profit': income - cost,
                'services': services_text,
                'service_id': service_id_int if service_id_int != 'all' else 0
            })

        # صفحه‌بندی
        paginator = Paginator(monthly_data, 10)
        page_number = request.GET.get('page')
        page_obj = paginator.get_page(page_number)

        # محاسبه مجموع کل
        total_income = sum(item['income'] for item in monthly_data)
        total_cost = sum(item['cost'] for item in monthly_data)
        total_profit = total_income - total_cost

        services = Service.objects.all()

        context = {
            'from_date': from_date_str or '',
            'to_date': to_date_str or '',
            'year': year_param,
            'month': month_param,
            'service_id': service_id_int,
            'page_obj': page_obj,
            'services': services,
            'total_income': total_income,
            'total_cost': total_cost,
            'total_profit': total_profit,
            'selected_service': service_id_int,
        }
        return render(request, self.template_name, context)


class MonthlyFinanceDetailView(View):
    template_name = 'accountant/monthly-finance-detail.html'

    def get(self, request, year, month, service_id):
        print("year, month, service_id =", year, month, service_id)

        total_paient_price = 0
        total_deposit = 0
        total_doctor = 0
        total_technician = 0
        total_warehouse_price = 0
        # فیلتر اولیه بر اساس سال و ماه
        reservations = Reservation.objects.filter(
            reservation_date__year=year,
            reservation_date__month=month,
            status='completed'
        ).annotate(
            warehouse_price=Sum(
                F('warehouse_reservation__price') * F('warehouse_reservation__quantity'),
                output_field=DecimalField(max_digits=10, decimal_places=2)
            ),
            warehouse_quantity=Sum('warehouse_reservation__quantity')
        ).prefetch_related('warehouse_reservation')
        for item in reservations:
            total_paient_price += item.paient_price
            total_deposit += item.deposit if item.deposit else 0
            total_doctor += item.doctor_price if item.doctor_price else 0
            total_technician += item.technician_price if item.technician_price else 0
            total_warehouse_price += item.warehouse_price if item.warehouse_price else 0


        total_expense_items = 0
        total_expense_fees = 0

        # فیلتر بر اساس سرویس
        if service_id and service_id != 0:
            reservations = reservations.filter(service_id=service_id)
            # در صورت انتخاب سرویس خاص، هزینه‌های عمومی نمایش داده نمی‌شوند
            expense_paid = ExpensePaid.objects.none()
            expense_item = ExpenseItem.objects.none()
        else:
            # نمایش همه هزینه‌های عمومی
            expense_paid = ExpensePaid.objects.filter(date__year=year, date__month=month)
            expense_item = ExpenseItem.objects.filter(
                date__year=year,
                date__month=month
            ).exclude(expense_type='reservation')
            for item in expense_item:
                total_expense_items+=float(item.cost_dollar)
            for item in expense_paid:
                total_expense_fees+=float(item.cost_dollar)

        total_warehouses = 0
        total_warehouses_sell = 0
        total_warehouses_sell_create_update = 0
        # انبار مربوط به رزروهای فیلتر شده

        warehouses = ReservationWarehouse.objects.filter(reservation__in=reservations)
        for item in warehouses:
            print("item=",item)
            total_warehouses += float(item.price * item.quantity)

        warehouses_sell = WarehouseItemHistory.objects.filter(changed_at__year=year,changed_at__month=month,history_type='sale')
        for item in warehouses_sell:
            print("item warehouses_sell=",item)
            total_warehouses_sell += (-1 *float(item.unit_price_changed * item.quantity_changed))

        warehouses_sell_create = WarehouseItemHistory.objects.filter(changed_at__year=year,changed_at__month=month,history_type='creation')
        for item in warehouses_sell_create:
            total_warehouses_sell_create_update += (float(item.unit_price_changed * item.quantity_changed))
            print("total_warehouses_sell_create_update=",total_warehouses_sell_create_update)

        warehouses_sell_update = WarehouseItemHistory.objects.filter(changed_at__year=year,changed_at__month=month,history_type='update')
        for item in warehouses_sell_update:
            total_warehouses_sell_create_update += (float(item.unit_price_changed * item.quantity_changed))
            print("total_warehouses_sell_create_update=",total_warehouses_sell_create_update)

        print("reservations =", reservations)
        for reser in reservations:
            print("paient_price =", reser.paient_price)

        # محاسبه مجموع درآمد
        total_income = sum((r.paient_price or 0) + (r.deposit or 0) for r in reservations)
        total_warehouses_sell = Decimal(total_warehouses_sell)
        total_income += total_warehouses_sell

        # محاسبه مجموع هزینه
        total_cost = sum((r.doctor_price or 0) + (r.technician_price or 0) for r in reservations)
        total_cost += sum((w.quantity or 0) * (w.price or 0) for w in warehouses)
        # total_cost += sum(e.cost_dollar or 0 for e in expense_paid)
        total_cost += sum(e.cost_dollar or 0 for e in expense_item)
        total_warehouses_sell_create_update = Decimal(total_warehouses_sell_create_update)
        total_cost += total_warehouses_sell_create_update

        services = Service.objects.all()
        print("warehouses=",warehouses)
        context = {
            'year': year,
            'month': month,
            'service_id': service_id,
            'reservations': reservations,
            'warehouses': warehouses,
            'warehouses_sell': warehouses_sell,
            'warehouses_sell_create': warehouses_sell_create,
            'warehouses_sell_update': warehouses_sell_update,
            'expense_paid': expense_paid,
            'total_expense_items':total_expense_items,
            'total_expense_fees':total_expense_fees,
            'total_warehouses':total_warehouses+total_warehouses_sell,
            'total_warehouses_sell':total_warehouses_sell,
            'total_warehouses_sell_create_update':total_warehouses_sell_create_update,
            'expense_item': expense_item,
            'total_month_income': total_income,
            'total_month_cost': total_cost,
            'profit': total_income - total_cost,
            'services': services,
            'total_paient_price' :total_paient_price,
            'total_deposit': total_deposit,
            'total_doctor': total_doctor,
            'total_technician': total_technician,
            'total_warehouse_price': total_warehouse_price,
        }
        return render(request, self.template_name, context)
