from django.shortcuts import render, get_object_or_404, redirect
import logging
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.utils import timezone
from django.conf import settings
from django.urls import reverse
from decimal import Decimal, InvalidOperation
import json

from processes.models import ProcessInstance, ProcessStep, StepInstance, StepRejection, StepApproval
from accounts.models import Role
from common.consts import UserRoles
from .models import Item, Quote, QuoteItem, Payment, Invoice, InvoiceItem
from installations.models import InstallationReport, InstallationItemChange
from processes.utils import get_scoped_instance_or_404

@login_required
def quote_step(request, instance_id, step_id):
    """مرحله انتخاب اقلام و ساخت پیش‌فاکتور"""
    # Enforce scoped access to prevent URL tampering
    instance = get_scoped_instance_or_404(request, instance_id)

    # Enforce scoped access to prevent URL tampering
    instance = get_object_or_404(
        ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile'),
        id=instance_id
    )
    step = get_object_or_404(instance.process.steps, id=step_id)
    
    # بررسی دسترسی به مرحله
    if not instance.can_access_step(step):
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
        return redirect('processes:request_list')
    
    # دریافت آیتم‌ها
    items = Item.objects.filter(is_active=True, is_special=False, is_deleted=False).order_by('name')
    existing_quote = Quote.objects.filter(process_instance=instance).first()
    existing_quote_items = {}
    if existing_quote:
        existing_quote_items = {qi.item_id: qi.quantity for qi in existing_quote.items.all()}
    
    step_instance = instance.step_instances.filter(step=step).first()
    
    # Navigation logic
    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()
    
    # determine if current user is broker
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
    except Exception:
        is_broker = False

    return render(request, 'invoices/quote_step.html', {
        'instance': instance,
        'step': step,
        'step_instance': step_instance,
        'items': items,
        'existing_quote_items': existing_quote_items,
        'existing_quote': existing_quote,
        'previous_step': previous_step,
        'next_step': next_step,
        'is_broker': is_broker,
    })


@require_POST
@login_required
def create_quote(request, instance_id, step_id):
    """ساخت/بروزرسانی پیش‌فاکتور از اقلام انتخابی"""
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    # enforce permission: only BROKER can create/update quote
    profile = getattr(request.user, 'profile', None)
    try:
        if not (profile and profile.has_role(UserRoles.BROKER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز ثبت/ویرایش پیش‌فاکتور را ندارید'})
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز ثبت/ویرایش پیش‌فاکتور را ندارید'})
    
    try:
        items_payload = json.loads(request.POST.get('items') or '[]')
    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'message': 'داده‌های اقلام نامعتبر است'})

    # اطمینان از حضور اقلام پیش‌فرض حتی اگر کلاینت ارسال نکرده باشد
    payload_by_id = {}
    for entry in items_payload:
        try:
            iid = int(entry.get('id'))
            payload_by_id[iid] = int(entry.get('qty') or 1)
        except Exception:
            continue

    default_item_ids = set(Item.objects.filter(is_default_in_quotes=True, is_deleted=False, is_special=False).values_list('id', flat=True))
    if default_item_ids:
        for default_id in default_item_ids:
            if default_id not in payload_by_id:
                # مقدار پیش فرض را قرار بده
                default_qty = Item.objects.filter(id=default_id).values_list('default_quantity', flat=True).first() or 1
                payload_by_id[default_id] = int(default_qty)

    # بازسازی payload نهایی معتبر
    items_payload = [{'id': iid, 'qty': qty} for iid, qty in payload_by_id.items() if qty and qty > 0]

    if not items_payload:
        return JsonResponse({'success': False, 'message': 'هیچ آیتمی انتخاب نشده است'})

    # Create or reuse quote
    quote, created_q = Quote.objects.get_or_create(
        process_instance=instance,
        defaults={
            'name': f"پیش‌فاکتور {instance.code}",
            'customer': instance.representative or request.user,
            'valid_until': timezone.now().date(),
            'created_by': request.user,
        }
    )

    # Track whether this step was already completed before this edit
    step_instance_existing = instance.step_instances.filter(step=step).first()
    was_already_completed = bool(step_instance_existing and step_instance_existing.status == 'completed')

    # Snapshot previous items before overwrite for change detection
    previous_items_map = {}
    if not created_q:
        previous_items_map = {qi.item_id: int(qi.quantity) for qi in quote.items.filter(is_deleted=False).all()}

    # Replace quote items with submitted ones
    quote.items.all().delete()
    for entry in items_payload:
        try:
            item_id = int(entry.get('id'))
            qty = int(entry.get('qty') or 1)
        except (TypeError, ValueError):
            continue
        if qty <= 0:
            continue
        item = Item.objects.filter(id=item_id).first()
        if not item:
            continue
        QuoteItem.objects.create(
            quote=quote,
            item=item,
            quantity=qty,
            unit_price=item.unit_price,
            total_price=item.unit_price * qty,
        )

    quote.calculate_totals()

    # Detect changes versus previous state and mark audit fields if editing after completion
    try:
        new_items_map = {int(entry.get('id')): int(entry.get('qty') or 1) for entry in items_payload}
    except Exception:
        new_items_map = {}

    next_step = instance.process.steps.filter(order__gt=step.order).first()

    if was_already_completed and new_items_map != previous_items_map:
        # StepInstance-level generic audit (for reuse across steps)
        if step_instance_existing:
            step_instance_existing.edited_after_completion = True
            step_instance_existing.last_edited_at = timezone.now()
            step_instance_existing.last_edited_by = request.user
            step_instance_existing.edit_count = (step_instance_existing.edit_count or 0) + 1
            step_instance_existing.completed_at = timezone.now()
            step_instance_existing.save(update_fields=['edited_after_completion', 'last_edited_at', 'last_edited_by', 'edit_count', 'completed_at'])

        
        if quote.status != 'draft':
            quote.status = 'draft'
            quote.save(update_fields=['status'])

        # Reset ALL subsequent completed steps to in_progress
        subsequent_steps = instance.process.steps.filter(order__gt=step.order)
        for subsequent_step in subsequent_steps:
            subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
            if subsequent_step_instance and subsequent_step_instance.status == 'completed':
                # Bypass validation by using update() instead of save()
                instance.step_instances.filter(step=subsequent_step).update(
                    status='in_progress',
                    completed_at=None
                )
                # Clear previous approvals if the step requires re-approval
                try:
                    subsequent_step_instance.approvals.all().delete()
                except Exception:
                    pass

        # Set current step to the next step
        if next_step:
            instance.current_step = next_step
            instance.save(update_fields=['current_step'])
    
    # تکمیل مرحله
    step_instance, created = StepInstance.objects.get_or_create(
        process_instance=instance,
        step=step
    )
    if not was_already_completed:
        step_instance.status = 'completed'
        step_instance.completed_at = timezone.now()
        step_instance.save(update_fields=['status', 'completed_at'])
    
    # انتقال به مرحله بعدی
    redirect_url = None
    if next_step:
        # Only advance current step if we are currently on this step to avoid regressions
        if instance.current_step_id == step.id:
            instance.current_step = next_step
            instance.save(update_fields=['current_step'])
        # هدایت مستقیم به مرحله پیش‌نمایش پیش‌فاکتور
        redirect_url = reverse('invoices:quote_preview_step', args=[instance.id, next_step.id])

    return JsonResponse({'success': True, 'quote_id': quote.id, 'redirect': redirect_url})


@login_required
def quote_preview_step(request, instance_id, step_id):
    """مرحله صدور پیش‌فاکتور - نمایش و تایید فاکتور"""
    # Enforce scoped access to prevent URL tampering
    instance = get_scoped_instance_or_404(request, instance_id)

    instance = get_object_or_404(
        ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile', 'broker', 'broker__company', 'broker__affairs', 'broker__affairs__county', 'broker__affairs__county__city'),
        id=instance_id
    )
    step = get_object_or_404(instance.process.steps, id=step_id)
    
    # بررسی دسترسی به مرحله
    if not instance.can_access_step(step):
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
        return redirect('processes:request_list')
    
    # دریافت پیش‌فاکتور
    quote = get_object_or_404(Quote, process_instance=instance)
    
    step_instance = instance.step_instances.filter(step=step).first()
    
    # Navigation logic
    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()
    
    # determine if current user is broker for UI controls
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
    except Exception:
        is_broker = False

    return render(request, 'invoices/quote_preview_step.html', {
        'instance': instance,
        'step': step,
        'step_instance': step_instance,
        'quote': quote,
        'previous_step': previous_step,
        'next_step': next_step,
        'is_broker': is_broker,
    })


@login_required
def quote_print(request, instance_id):
    """صفحه پرینت پیش‌فاکتور"""
    instance = get_scoped_instance_or_404(request, instance_id)
    quote = get_object_or_404(Quote, process_instance=instance)
    
    return render(request, 'invoices/quote_print.html', {
        'instance': instance,
        'quote': quote,
    })


@require_POST
@login_required  
def approve_quote(request, instance_id, step_id):
    """تایید پیش‌فاکتور و انتقال به مرحله بعدی"""
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    quote = get_object_or_404(Quote, process_instance=instance)
    # enforce permission: only BROKER can approve
    profile = getattr(request.user, 'profile', None)
    try:
        if not (profile and profile.has_role(UserRoles.BROKER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز تایید پیش‌فاکتور را ندارید'})
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز تایید پیش‌فاکتور را ندارید'})
    
    # تایید پیش‌فاکتور
    quote.status = 'sent'
    quote.save()
    
    # تکمیل مرحله
    step_instance, created = StepInstance.objects.get_or_create(
        process_instance=instance,
        step=step
    )
    step_instance.status = 'completed'
    step_instance.completed_at = timezone.now()
    step_instance.save()
    
    # انتقال به مرحله بعدی
    next_step = instance.process.steps.filter(order__gt=step.order).first()
    redirect_url = None
    if next_step:
        instance.current_step = next_step
        instance.save()
        redirect_url = reverse('processes:step_detail', args=[instance.id, next_step.id])
    else:
        # در صورت نبود مرحله بعدی، بازگشت به لیست درخواست‌ها
        redirect_url = reverse('processes:request_list')

    messages.success(request, 'پیش‌فاکتور با موفقیت تایید شد.')
    return JsonResponse({'success': True, 'message': 'پیش‌فاکتور با موفقیت تایید شد.', 'redirect': redirect_url})


@login_required
def quote_payment_step(request, instance_id, step_id):
    """مرحله سوم: ثبت فیش‌های واریزی پیش‌فاکتور"""
    # Enforce scoped access to prevent URL tampering
    instance = get_scoped_instance_or_404(request, instance_id)

    instance = get_object_or_404(
        ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile'),
        id=instance_id
    )
    step = get_object_or_404(instance.process.steps, id=step_id)

    # بررسی دسترسی
    if not instance.can_access_step(step):
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
        return redirect('processes:request_list')

    quote = get_object_or_404(Quote, process_instance=instance)
    invoice = Invoice.objects.filter(quote=quote).first()
    payments = invoice.payments.select_related('created_by').filter(is_deleted=False).all() if invoice else []

    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()

    totals = {
        'final_amount': quote.final_amount,
        'paid_amount': quote.get_paid_amount(),
        'remaining_amount': quote.get_remaining_amount(),
        'is_fully_paid': quote.get_remaining_amount() <= 0,
    }

    step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step, defaults={'status': 'in_progress'})

    reqs = list(step.approver_requirements.select_related('role').all())
    user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', None)
    user_roles = list(user_roles_qs.all()) if user_roles_qs is not None else []
    approvals_list = list(step_instance.approvals.select_related('role', 'approved_by').filter(is_deleted=False))
    rejections_list = list(step_instance.rejections.select_related('role', 'rejected_by').filter(is_deleted=False))
    approvals_by_role = {a.role_id: a for a in approvals_list}
    rejections_by_role = {r.role_id: r for r in rejections_list}
    approver_statuses = []
    for r in reqs:
        appr = approvals_by_role.get(r.role_id)
        rejection = rejections_by_role.get(r.role_id)
        
        if appr:
            status = 'approved'
            reason = appr.reason
        elif rejection:
            status = 'rejected'
            reason = rejection.reason
        else:
            status = None
            reason = ''
            
        approver_statuses.append({
            'role': r.role,
            'status': status,
            'reason': reason,
        })

    # dynamic permission: who can approve/reject this step (based on requirements)
    try:
        req_role_ids = {r.role_id for r in reqs}
        user_role_ids = {ur.id for ur in user_roles}
        can_approve_reject = len(req_role_ids.intersection(user_role_ids)) > 0
    except Exception:
        can_approve_reject = False

    # Compute whether current user has already decided (approved/rejected)
    current_user_has_decided = False
    try:
        user_has_approval = step_instance.approvals.filter(approved_by=request.user, is_deleted=False).exists()
        user_has_rejection = step_instance.rejections.filter(rejected_by=request.user, is_deleted=False).exists()
        current_user_has_decided = bool(user_has_approval or user_has_rejection)
    except Exception:
        current_user_has_decided = False
        

    # Accountant/Admin approval and rejection via POST (multi-role)
    if request.method == 'POST' and request.POST.get('action') in ['approve', 'reject']:
        # match user's role against step required approver roles
        req_roles = [req.role for req in step.approver_requirements.select_related('role').all()]
        user_roles = list(getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()).all())
        matching_role = next((r for r in user_roles if r in req_roles), None)
        if matching_role is None:
            messages.error(request, 'شما دسترسی لازم برای تایید/رد این مرحله را ندارید.')
            return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)

        action = request.POST.get('action')
        if action == 'approve':
            StepApproval.objects.create(
                step_instance=step_instance,
                role=matching_role,
                approved_by=request.user,
                reason=''
            )
            if step_instance.is_fully_approved():
                step_instance.status = 'completed'
                step_instance.completed_at = timezone.now()
                step_instance.save()
                # move to next step
                redirect_url = 'processes:request_list'
                if next_step:
                    instance.current_step = next_step
                    instance.save()
                    return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
                return redirect(redirect_url)
            messages.success(request, 'تایید شما ثبت شد. منتظر تایید سایر نقش‌ها.')
            return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)

        if action == 'reject':
            reason = (request.POST.get('reject_reason') or '').strip()
            if not reason:
                messages.error(request, 'علت رد شدن را وارد کنید')
                return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)
            StepRejection.objects.create(
                step_instance=step_instance,
                role=matching_role,
                rejected_by=request.user,
                reason=reason
                )
            # If current step is ahead of this step, reset it back to this step
            try:
                if instance.current_step and instance.current_step.order > step.order:
                    instance.current_step = step
                    instance.save(update_fields=['current_step'])
            except Exception:
                pass
            messages.success(request, 'مرحله پرداخت‌ها رد شد و برای اصلاح بازگشت.')
            return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id)

    # role flags for permissions (legacy flags kept for compatibility)
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    is_accountant = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
        is_accountant = bool(profile and profile.has_role(UserRoles.ACCOUNTANT))
    except Exception:
        is_broker = False
        is_accountant = False

    return render(request, 'invoices/quote_payment_step.html', {
        'instance': instance,
        'step': step,
        'step_instance': step_instance,
        'quote': quote,
        'payments': payments,
        'totals': totals,
        'previous_step': previous_step,
        'next_step': next_step,
        'approver_statuses': approver_statuses,
        'is_broker': is_broker,
        'is_accountant': is_accountant,
        'can_approve_reject': can_approve_reject,
        'current_user_has_decided': current_user_has_decided,
    })


@require_POST
@login_required
def add_quote_payment(request, instance_id, step_id):
    """افزودن فیش واریزی جدید برای پیش‌فاکتور"""
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    quote = get_object_or_404(Quote, process_instance=instance)
    invoice, _ = Invoice.objects.get_or_create(
        process_instance=instance,
        quote=quote,
        defaults={
            'name': f"Invoice {quote.name}",
            'customer': quote.customer,
            'due_date': timezone.now().date(),
            'created_by': request.user,
        }
    )

    # who can add payments
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    is_accountant = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
        is_accountant = bool(profile and profile.has_role(UserRoles.ACCOUNTANT))
    except Exception:
        is_broker = False
        is_accountant = False
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن فیش را ندارید'})

    logger = logging.getLogger(__name__)
    try:
        amount = (request.POST.get('amount') or '').strip()
        payment_date = (request.POST.get('payment_date') or '').strip()
        payment_method = (request.POST.get('payment_method') or '').strip()
        reference_number = (request.POST.get('reference_number') or '').strip()
        notes = (request.POST.get('notes') or '').strip()
        receipt_image = request.FILES.get('receipt_image')
        # Server-side validation for required fields
        if not amount:
            return JsonResponse({'success': False, 'message': 'مبلغ را وارد کنید'})
        if not payment_date:
            return JsonResponse({'success': False, 'message': 'تاریخ پرداخت را وارد کنید'})
        if not payment_method:
            return JsonResponse({'success': False, 'message': 'روش پرداخت را انتخاب کنید'})
        if not reference_number:
            return JsonResponse({'success': False, 'message': 'شماره مرجع را وارد کنید'})
        if not receipt_image:
            return JsonResponse({'success': False, 'message': 'تصویر فیش را بارگذاری کنید'})
        # Normalize date to YYYY-MM-DD (accept YYYY/MM/DD from Persian datepicker)
        if '/' in payment_date:
            payment_date = payment_date.replace('/', '-')

        # Prevent overpayment
        try:
            amount_dec = Decimal(amount)
        except InvalidOperation:
            return JsonResponse({'success': False, 'message': 'مبلغ نامعتبر است'})
        remaining = quote.get_remaining_amount()
        if amount_dec > remaining:
            return JsonResponse({'success': False, 'message': 'مبلغ فیش بیشتر از مانده پیش‌فاکتور است'})

        Payment.objects.create(
            invoice=invoice,
            amount=amount_dec,
            payment_date=payment_date,
            payment_method=payment_method,
            reference_number=reference_number,
            receipt_image=receipt_image,
            notes=notes,
            created_by=request.user,
        )
    except Exception as e:
        logger.exception('Error adding quote payment (instance=%s, step=%s)', instance_id, step_id)
        return JsonResponse({'success': False, 'message': 'خطا در ثبت فیش', 'error': str(e)})

    # After modifying payments, set step back to in_progress (awaiting approval)
    try:
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
        si.status = 'in_progress'
        si.completed_at = None
        si.save()
    except Exception:
        pass

    try:
        for appr in list(si.approvals.all()):
            appr.delete()
    except Exception:
        pass
    try:
        for rej in list(si.rejections.all()):
            rej.delete()
    except Exception:
        pass
    
    # Reset ALL subsequent completed steps to in_progress
    try:
        subsequent_steps = instance.process.steps.filter(order__gt=step.order)
        for subsequent_step in subsequent_steps:
            subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
            if subsequent_step_instance and subsequent_step_instance.status == 'completed':
                # Bypass validation by using update() instead of save()
                instance.step_instances.filter(step=subsequent_step).update(
                    status='in_progress',
                    completed_at=None
                )
                # Clear previous approvals if the step requires re-approval
                try:
                    for appr in list(subsequent_step_instance.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
    except Exception:
        pass
    
    # If current step is ahead of this step, reset it back to this step
    try:
        if instance.current_step and instance.current_step.order > step.order:
            instance.current_step = step
            instance.save(update_fields=['current_step'])
    except Exception:
        pass
    redirect_url = reverse('invoices:quote_payment_step', args=[instance.id, step.id])
    return JsonResponse({'success': True, 'redirect': redirect_url})


@require_POST
@login_required
def delete_quote_payment(request, instance_id, step_id, payment_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    quote = get_object_or_404(Quote, process_instance=instance)
    invoice = Invoice.objects.filter(quote=quote).first()
    if not invoice:
        return JsonResponse({'success': False, 'message': 'فاکتور یافت نشد'})
    payment = get_object_or_404(Payment, id=payment_id, invoice=invoice)
    
    # who can delete payments
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    is_accountant = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
        is_accountant = bool(profile and profile.has_role(UserRoles.ACCOUNTANT))
    except Exception:
        is_broker = False
        is_accountant = False
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن فیش را ندارید'})

    try:
        # soft delete using project's BaseModel delete override
        payment.hard_delete()
    except Exception:
        return JsonResponse({'success': False, 'message': 'خطا در حذف فیش'})
    # On delete, return to awaiting approval
    try:
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
        si.status = 'in_progress'
        si.completed_at = None
        si.save()
        for appr in list(si.approvals.all()):
            appr.delete()
        for rej in list(si.rejections.all()):
            rej.delete()
    except Exception:
        pass

    # Reset ALL subsequent completed steps to in_progress
    try:
        subsequent_steps = instance.process.steps.filter(order__gt=step.order)
        for subsequent_step in subsequent_steps:
            subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
            if subsequent_step_instance and subsequent_step_instance.status == 'completed':
                # Bypass validation by using update() instead of save()
                instance.step_instances.filter(step=subsequent_step).update(
                    status='in_progress',
                    completed_at=None
                )
                # Clear previous approvals if the step requires re-approval
                try:
                    for appr in list(subsequent_step_instance.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
    except Exception:
        pass

    # If current step is ahead of this step, reset it back to this step
    try:
        if instance.current_step and instance.current_step.order > step.order:
            instance.current_step = step
            instance.save(update_fields=['current_step'])
    except Exception:
        pass
    redirect_url = reverse('invoices:quote_payment_step', args=[instance.id, step.id])
    return JsonResponse({'success': True, 'redirect': redirect_url})


@login_required
def final_invoice_step(request, instance_id, step_id):
    """تجمیع اقلام پیش‌فاکتور با تغییرات نصب و صدور فاکتور نهایی"""
    # Enforce scoped access to prevent URL tampering
    instance = get_scoped_instance_or_404(request, instance_id)

    instance = get_object_or_404(
        ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile'),
        id=instance_id
    )
    step = get_object_or_404(instance.process.steps, id=step_id)

    if not instance.can_access_step(step):
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
        return redirect('processes:request_list')

    quote = get_object_or_404(Quote, process_instance=instance)

    # Helper to make safe Decimal from various inputs (handles commas/persian digits)
    def _to_decimal(value):
        if isinstance(value, Decimal):
            return value
        try:
            if isinstance(value, (int, float)):
                return Decimal(str(value))
            s = str(value or '').strip()
            if not s:
                return Decimal('0')
            # normalize commas and Persian digits
            persian = '۰۱۲۳۴۵۶۷۸۹'
            latin = '0123456789'
            tbl = str.maketrans({persian[i]: latin[i] for i in range(10)})
            s = s.translate(tbl).replace(',', '')
            return Decimal(s)
        except Exception:
            return Decimal('0')

    # Build initial map from quote
    item_id_to_row = {}
    for qi in quote.items.all():
        item_id_to_row[qi.item_id] = {
            'item': qi.item,
            'base_qty': qi.quantity,
            'base_price': _to_decimal(qi.unit_price),
            'added_qty': 0,
            'removed_qty': 0,
        }

    # Read installation changes from latest report (if any)
    latest_report = InstallationReport.objects.filter(assignment__process_instance=instance).order_by('-created').first()
    if latest_report:
        for ch in latest_report.item_changes.all():
            row = item_id_to_row.setdefault(ch.item_id, {
                'item': ch.item,
                'base_qty': 0,
                'base_price': _to_decimal(ch.unit_price or ch.item.unit_price),
                'added_qty': 0,
                'removed_qty': 0,
            })
            if ch.change_type == 'add':
                row['added_qty'] += ch.quantity
                if ch.unit_price:
                    row['base_price'] = _to_decimal(ch.unit_price)
            else:
                row['removed_qty'] += ch.quantity
                if ch.unit_price:
                    row['base_price'] = _to_decimal(ch.unit_price)

    # Compute final invoice lines (include fully removed items for display)
    rows = []
    total_amount = Decimal('0')
    for _, r in item_id_to_row.items():
        final_qty = max(0, (r['base_qty'] + r['added_qty'] - r['removed_qty']))
        unit_price_dec = _to_decimal(r['base_price'])
        line_total = Decimal(final_qty) * unit_price_dec if final_qty > 0 else Decimal('0')
        if final_qty > 0:
            total_amount += line_total
        rows.append({
            'item': r['item'],
            'quantity': final_qty,
            'unit_price': unit_price_dec,
            'total_price': line_total,
            'base_qty': r['base_qty'],
            'added_qty': r['added_qty'],
            'removed_qty': r['removed_qty'],
            'is_removed': True if final_qty == 0 else False,
        })

    # Create or reuse final invoice
    invoice, _ = Invoice.objects.get_or_create(
        process_instance=instance,
        customer=quote.customer,
        quote=quote,
        defaults={
            'name': f"فاکتور نهایی {instance.code}",
            'due_date': timezone.now().date(),
            'created_by': request.user,
        }
    )
    # Replace only non-special items (preserve special charges added by user)
    qs = invoice.items.select_related('item').filter(item__is_special=False)
    try:
        qs._raw_delete(qs.db)
    except Exception:
        qs.delete()
    for r in rows:
        if r['quantity'] <= 0:
            continue
        from .models import InvoiceItem
        InvoiceItem.objects.create(
            invoice=invoice,
            item=r['item'],
            quantity=r['quantity'],
            unit_price=r['unit_price'],
        )
    invoice.calculate_totals()

    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()

    # Choices for special items from DB
    special_choices = list(Item.objects.filter(is_special=True).values('id', 'name'))

    # role flag for manager-only actions
    profile = getattr(request.user, 'profile', None)
    is_manager = False
    try:
        is_manager = bool(profile and profile.has_role(UserRoles.MANAGER))
    except Exception:
        is_manager = False

    return render(request, 'invoices/final_invoice_step.html', {
        'instance': instance,
        'step': step,
        'invoice': invoice,
        'rows': rows,
        'special_choices': special_choices,
        'invoice_specials': invoice.items.select_related('item').filter(item__is_special=True, is_deleted=False).all(),
        'previous_step': previous_step,
        'next_step': next_step,
        'is_manager': is_manager,
    })


@login_required
def final_invoice_print(request, instance_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    items = invoice.items.select_related('item').filter(is_deleted=False).all()
    return render(request, 'invoices/final_invoice_print.html', {
        'instance': instance,
        'invoice': invoice,
        'items': items,
    })


@require_POST
@login_required
def approve_final_invoice(request, instance_id, step_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    # only MANAGER can approve
    try:
        if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403)
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403)

    step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
    step_instance.status = 'completed'
    step_instance.completed_at = timezone.now()
    step_instance.save()
    # move to next
    next_step = instance.process.steps.filter(order__gt=step.order).first()
    redirect_url = reverse('processes:request_list')
    if next_step:
        instance.current_step = next_step
        instance.save()
        redirect_url = reverse('processes:step_detail', args=[instance.id, next_step.id])
    return JsonResponse({'success': True, 'message': 'فاکتور نهایی تایید شد', 'redirect': redirect_url})


@require_POST
@login_required
def add_special_charge(request, instance_id, step_id):
    """افزودن هزینه ویژه تعمیر/تعویض به فاکتور نهایی به‌صورت آیتم جداگانه"""
    instance = get_scoped_instance_or_404(request, instance_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    # only MANAGER can add special charges
    try:
        if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403)
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403)

    item_id = request.POST.get('item_id')
    amount = (request.POST.get('amount') or '').strip()
    if not item_id:
        return JsonResponse({'success': False, 'message': 'آیتم را انتخاب کنید'})
    try:
        amount_dec = Decimal(amount)
    except (InvalidOperation, TypeError):
        return JsonResponse({'success': False, 'message': 'مبلغ نامعتبر است'})
    if amount_dec <= 0:
        return JsonResponse({'success': False, 'message': 'مبلغ باید مثبت باشد'})

    # Fetch existing special item from DB
    special_item = get_object_or_404(Item, id=item_id, is_special=True)

    
    InvoiceItem.objects.create(
        invoice=invoice,
        item=special_item,
        quantity=1,
        unit_price=amount_dec,
    )
    invoice.calculate_totals()
    # If the next step was completed, reopen it (set to in_progress) due to invoice change
    try:
        step = get_object_or_404(instance.process.steps, id=step_id)
        next_step = instance.process.steps.filter(order__gt=step.order).first()
        if next_step:
            si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=next_step)
            if si.status in ['completed', 'approved']:
                si.status = 'in_progress'
                si.completed_at = None
                si.save(update_fields=['status', 'completed_at'])
                # Clear prior approvals/rejections as the underlying totals changed
                try:
                    for appr in list(si.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
                try:
                    for rej in list(si.rejections.all()):
                        rej.delete()
                except Exception:
                    pass
    except Exception:
        pass
    return JsonResponse({'success': True, 'redirect': reverse('invoices:final_invoice_step', args=[instance.id, step_id])})


@require_POST
@login_required
def delete_special_charge(request, instance_id, step_id, item_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    # only MANAGER can delete special charges
    try:
        if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403)
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403)
    inv_item = get_object_or_404(InvoiceItem, id=item_id, invoice=invoice)
    # allow deletion only for special items
    try:
        if not getattr(inv_item.item, 'is_special', False):
            return JsonResponse({'success': False, 'message': 'امکان حذف این مورد وجود ندارد'})
    except Exception:
        return JsonResponse({'success': False, 'message': 'امکان حذف این مورد وجود ندارد'})
    inv_item.hard_delete()
    invoice.calculate_totals()
    # If the next step was completed, reopen it (set to in_progress) due to invoice change
    try:
        step = get_object_or_404(instance.process.steps, id=step_id)
        next_step = instance.process.steps.filter(order__gt=step.order).first()
        if next_step:
            si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=next_step)
            if si.status in ['completed', 'approved']:
                si.status = 'in_progress'
                si.completed_at = None
                si.save(update_fields=['status', 'completed_at'])
                # Clear prior approvals/rejections as the underlying totals changed
                try:
                    for appr in list(si.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
                try:
                    for rej in list(si.rejections.all()):
                        rej.delete()
                except Exception:
                    pass
    except Exception:
        pass
    return JsonResponse({'success': True, 'redirect': reverse('invoices:final_invoice_step', args=[instance.id, step_id])})


@login_required
def final_settlement_step(request, instance_id, step_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)

    if not instance.can_access_step(step):
        messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.')
        return redirect('processes:request_list')
    invoice = get_object_or_404(Invoice, process_instance=instance)

    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()

    # Ensure step instance exists
    step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step, defaults={'status': 'in_progress'})

    # Auto-complete step when invoice is fully settled (no approvals needed)
    try:
        invoice.calculate_totals()
        if invoice.get_remaining_amount() == 0:
            if step_instance.status != 'completed':
                step_instance.status = 'completed'
                step_instance.completed_at = timezone.now()
                step_instance.save()
            # if next_step:
            #     instance.current_step = next_step
            #     instance.save(update_fields=['current_step'])
            #     return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
            # return redirect('processes:request_list')
    except Exception:
        # If totals calculation fails, continue with normal flow
        pass
    
    # Build approver statuses for template (include reason to display in UI)
    reqs = list(step.approver_requirements.select_related('role').all())
    approvals = list(step_instance.approvals.select_related('role', 'approved_by').filter(is_deleted=False))
    rejections = list(step_instance.rejections.select_related('role', 'rejected_by').filter(is_deleted=False))
    approvals_by_role = {a.role_id: a for a in approvals}
    rejections_by_role = {r.role_id: r for r in rejections}
    approver_statuses = []
    for r in reqs:
        appr = approvals_by_role.get(r.role_id)
        rejection = rejections_by_role.get(r.role_id)
        
        if appr:
            status = 'approved'
            reason = appr.reason
        elif rejection:
            status = 'rejected'
            reason = rejection.reason
        else:
            status = None
            reason = ''
            
        approver_statuses.append({
            'role': r.role,
            'status': status,
            'reason': reason,
        })
    # dynamic permission to control approve/reject UI
    try:
        user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none())
        user_role_ids = set(user_roles_qs.values_list('id', flat=True))
        req_role_ids = {r.role_id for r in reqs}
        can_approve_reject = len(req_role_ids.intersection(user_role_ids)) > 0
    except Exception:
        can_approve_reject = False

    # Compute whether current user has already decided (approved/rejected)
    current_user_has_decided = False
    try:
        user_has_approval = step_instance.approvals.filter(approved_by=request.user, is_deleted=False).exists()
        user_has_rejection = step_instance.rejections.filter(rejected_by=request.user, is_deleted=False).exists()
        current_user_has_decided = bool(user_has_approval or user_has_rejection)
    except Exception:
        current_user_has_decided = False

    # Accountant/Admin approval and rejection (multi-role)
    if request.method == 'POST' and request.POST.get('action') in ['approve', 'reject', 'force_approve']:
        req_roles = [req.role for req in step.approver_requirements.select_related('role').all()]
        user_roles = list(getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()).all())
        matching_role = next((r for r in user_roles if r in req_roles), None)
        if matching_role is None and request.POST.get('action') != 'force_approve':
            messages.error(request, 'شما دسترسی لازم برای تایید/رد این مرحله را ندارید.')
            return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)

        action = request.POST.get('action')
        if action == 'approve':
            # enforce zero remaining
            invoice.calculate_totals()
            if invoice.get_remaining_amount() != 0:
                messages.error(request, f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.get_remaining_amount()})")
                return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
            StepApproval.objects.create(
                step_instance=step_instance,
                role=matching_role,
                approved_by=request.user,
                reason=''
            )
            if step_instance.is_fully_approved():
                step_instance.status = 'completed'
                step_instance.completed_at = timezone.now()
                step_instance.save()
                if next_step:
                    instance.current_step = next_step
                    instance.save()
                    return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
                return redirect('processes:request_list')
            messages.success(request, 'تایید شما ثبت شد. منتظر تایید سایر نقش‌ها.')
            return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)

        if action == 'reject':
            reason = (request.POST.get('reject_reason') or '').strip()
            if not reason:
                messages.error(request, 'علت رد شدن را وارد کنید')
                return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
            StepRejection.objects.create(
                step_instance=step_instance,
                role=matching_role,
                rejected_by=request.user,
                reason=reason
                )
            # If current step is ahead of this step, reset it back to this step (align behavior with other steps)
            try:
                if instance.current_step and instance.current_step.order > step.order:
                    instance.current_step = step
                    instance.save(update_fields=['current_step'])
            except Exception:
                pass
            messages.success(request, 'مرحله تسویه نهایی رد شد و برای اصلاح بازگشت.')
            return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)

        if action == 'force_approve':
            # Only MANAGER can force approve
            try:
                if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.MANAGER)):
                    messages.error(request, 'فقط مدیر مجاز به تایید اضطراری است.')
                    return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
            except Exception:
                messages.error(request, 'فقط مدیر مجاز به تایید اضطراری است.')
                return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
            # Allow emergency approval only when invoice has a remaining (non-zero)
            try:
                invoice.calculate_totals()
                if invoice.get_remaining_amount() == 0:
                    messages.error(request, 'فاکتور تسویه شده است؛ تایید اضطراری لازم نیست.')
                    return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id)
            except Exception:
                pass
            # Mark step completed regardless of remaining amount/approvals
            step_instance.status = 'approved'
            step_instance.save()
            if next_step:
                instance.current_step = next_step
                instance.save()
                return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
            return redirect('processes:request_list')

    # broker flag for payment management permission
    profile = getattr(request.user, 'profile', None)
    is_broker = False
    try:
        is_broker = bool(profile and profile.has_role(UserRoles.BROKER))
    except Exception:
        is_broker = False

    return render(request, 'invoices/final_settlement_step.html', {
        'instance': instance,
        'step': step,
        'invoice': invoice,
        'payments': invoice.payments.filter(is_deleted=False).all(),
        'step_instance': step_instance,
        'previous_step': previous_step,
        'next_step': next_step,
        'approver_statuses': approver_statuses,
        'can_approve_reject': can_approve_reject,
        'is_broker': is_broker,
        'current_user_has_decided': current_user_has_decided,
        'is_manager': bool(getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()).filter(slug=UserRoles.MANAGER.value).exists()) if getattr(request.user, 'profile', None) else False,
    })


@require_POST
@login_required
def add_final_payment(request, instance_id, step_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    # Only BROKER can add final settlement payments
    try:
        if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.BROKER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403)
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403)

    # Prevent adding payments if invoice already settled
    try:
        invoice.calculate_totals()
        if invoice.get_remaining_amount() == 0:
            return JsonResponse({'success': False, 'message': 'فاکتور تسویه شده است؛ افزودن تراکنش مجاز نیست'})
    except Exception:
        pass

    amount = (request.POST.get('amount') or '').strip()
    payment_date = (request.POST.get('payment_date') or '').strip()
    payment_method = (request.POST.get('payment_method') or '').strip()
    reference_number = (request.POST.get('reference_number') or '').strip()
    direction = (request.POST.get('direction') or 'in').strip()
    receipt_image = request.FILES.get('receipt_image')
    if not amount:
        return JsonResponse({'success': False, 'message': 'مبلغ را وارد کنید'})
    if not payment_date:
        return JsonResponse({'success': False, 'message': 'تاریخ پرداخت را وارد کنید'})
    if not payment_method:
        return JsonResponse({'success': False, 'message': 'روش پرداخت را انتخاب کنید'})
    if not reference_number:
        return JsonResponse({'success': False, 'message': 'شماره مرجع را وارد کنید'})
    if not receipt_image:
        return JsonResponse({'success': False, 'message': 'تصویر فیش الزامی است'})
    if '/' in payment_date:
        payment_date = payment_date.replace('/', '-')
    try:
        amount_dec = Decimal(amount)
    except InvalidOperation:
        return JsonResponse({'success': False, 'message': 'مبلغ نامعتبر است'})
    # Only allow outgoing (پرداخت به مشتری) when current net due is negative
    # Compute net due explicitly from current items/payments
    try:
        current_paid = sum((p.amount if p.direction == 'in' else -p.amount) for p in invoice.payments.filter(is_deleted=False).all())
    except Exception:
        current_paid = Decimal('0')
    # Ensure invoice totals are up-to-date for final_amount
    invoice.calculate_totals()
    net_due = invoice.final_amount - current_paid
    if direction == 'out' and net_due >= 0:
        return JsonResponse({'success': False, 'message': 'در حال حاضر مانده به نفع مشتری نیست'})

    # Amount constraints by sign of net due
    if net_due > 0 and direction == 'in' and amount_dec > net_due:
        return JsonResponse({'success': False, 'message': 'مبلغ فیش بیشتر از مانده فاکتور است'})
    if net_due < 0 and direction == 'out' and amount_dec > abs(net_due):
        return JsonResponse({'success': False, 'message': 'مبلغ فیش بیشتر از مانده بدهی شرکت به مشتری است'})
    if net_due < 0 and direction == 'in':
        return JsonResponse({'success': False, 'message': 'در حال حاضر مانده به نفع مشتری است؛ دریافت از مشتری مجاز نیست'})

    Payment.objects.create(
        invoice=invoice,
        amount=amount_dec,
        payment_date=payment_date,
        payment_method=payment_method,
        reference_number=reference_number,
        direction='in' if direction != 'out' else 'out',
        receipt_image=receipt_image,
        created_by=request.user,
    )
    # After creation, totals auto-updated by model save. Respond with redirect and new totals for UX.
    invoice.refresh_from_db()

    # On delete, return to awaiting approval
    try:
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
        if si.status != 'approved':
            si.status = 'in_progress'
        si.completed_at = None
        si.save()
        try:
            for appr in list(si.approvals.all()):
                appr.delete()
        except Exception:
            pass
        try:
            for rej in list(si.rejections.all()):
                rej.delete()
        except Exception:
            pass
    except Exception:
        pass
    
    # Reset ALL subsequent completed steps to in_progress
    try:
        subsequent_steps = instance.process.steps.filter(order__gt=step.order)
        for subsequent_step in subsequent_steps:
            subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
            if subsequent_step_instance and subsequent_step_instance.status == 'completed':
                # Bypass validation by using update() instead of save()
                instance.step_instances.filter(step=subsequent_step).update(
                    status='in_progress',
                    completed_at=None
                )
                # Clear previous approvals if the step requires re-approval
                try:
                    for appr in list(subsequent_step_instance.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
    except Exception:
        pass

    # If current step is ahead of this step, reset it back to this step
    try:
        if instance.current_step and instance.current_step.order > step.order:
            instance.current_step = step
            instance.save(update_fields=['current_step'])
    except Exception:
        pass


    return JsonResponse({
        'success': True,
        'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]),
        'totals': {
            'final_amount': str(invoice.final_amount),
            'paid_amount': str(invoice.get_paid_amount()),
            'remaining_amount': str(invoice.get_remaining_amount()),
        }
    })


@require_POST
@login_required
def delete_final_payment(request, instance_id, step_id, payment_id):
    instance = get_scoped_instance_or_404(request, instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    invoice = get_object_or_404(Invoice, process_instance=instance)
    payment = get_object_or_404(Payment, id=payment_id, invoice=invoice)
    
    # Only BROKER can delete final settlement payments
    try:
        if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.BROKER)):
            return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403)
    except Exception:
        return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403)
    
    # Delete payment and recalculate invoice totals
    payment.hard_delete()
    invoice.calculate_totals()  # This is what was missing!

    # On delete, return to awaiting approval
    try:
        si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
        si.status = 'in_progress'
        si.completed_at = None
        si.save()
        # Clear approvals and rejections (like in quote_payment)
        for appr in list(si.approvals.all()):
            appr.delete()
        for rej in list(si.rejections.all()):
            rej.delete()
    except Exception:
        pass

    # Reset ALL subsequent completed steps to in_progress
    try:
        subsequent_steps = instance.process.steps.filter(order__gt=step.order)
        for subsequent_step in subsequent_steps:
            subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first()
            if subsequent_step_instance and subsequent_step_instance.status == 'completed':
                # Bypass validation by using update() instead of save()
                instance.step_instances.filter(step=subsequent_step).update(
                    status='in_progress',
                    completed_at=None
                )
                # Clear previous approvals if the step requires re-approval
                try:
                    for appr in list(subsequent_step_instance.approvals.all()):
                        appr.delete()
                except Exception:
                    pass
    except Exception:
        pass

    # If current step is ahead of this step, reset it back to this step
    try:
        if instance.current_step and instance.current_step.order > step.order:
            instance.current_step = step
            instance.save(update_fields=['current_step'])
    except Exception:
        pass

    return JsonResponse({'success': True, 'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]), 'totals': {
        'final_amount': str(invoice.final_amount),
        'paid_amount': str(invoice.get_paid_amount()),
        'remaining_amount': str(invoice.get_remaining_amount()),
    }})

