from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.urls import reverse
from django.utils import timezone
from accounts.models import Profile
from common.consts import UserRoles
from processes.models import ProcessInstance, StepInstance, StepRejection, StepApproval
from accounts.models import Role
from invoices.models import Item, Quote, QuoteItem
from .models import InstallationAssignment, InstallationReport, InstallationPhoto, InstallationItemChange
from decimal import Decimal, InvalidOperation

@login_required
def installation_assign_step(request, instance_id, step_id):
    instance = get_object_or_404(ProcessInstance, id=instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()

    # Installers list (profiles that have installer role)
    installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value).select_related('user').all()
    assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance)

    # Role flags
    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

    if request.method == 'POST':
        if not is_manager:
            messages.error(request, 'شما اجازه تعیین نصاب را ندارید')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
        installer_id = request.POST.get('installer_id')
        scheduled_date = (request.POST.get('scheduled_date') or '').strip()
        assignment.installer_id = installer_id or None
        if scheduled_date:
            assignment.scheduled_date = scheduled_date.replace('/', '-')
        assignment.assigned_by = request.user
        assignment.save()

        # complete step
        StepInstance.objects.update_or_create(
            process_instance=instance,
            step=step,
            defaults={'status': 'completed', 'completed_at': timezone.now()}
        )

        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')

    # Read-only logic for non-managers
    read_only = not is_manager
    show_denied_msg = (not is_manager) and (assignment.installer_id is None)

    return render(request, 'installations/installation_assign_step.html', {
        'instance': instance,
        'step': step,
        'assignment': assignment,
        'installers': installers,
        'previous_step': previous_step,
        'next_step': next_step,
        'is_manager': is_manager,
        'read_only': read_only,
        'show_denied_msg': show_denied_msg,
    })


@login_required
def installation_report_step(request, instance_id, step_id):
    instance = get_object_or_404(ProcessInstance, id=instance_id)
    step = get_object_or_404(instance.process.steps, id=step_id)
    previous_step = instance.process.steps.filter(order__lt=step.order).last()
    next_step = instance.process.steps.filter(order__gt=step.order).first()
    assignment = InstallationAssignment.objects.filter(process_instance=instance).first()
    existing_report = InstallationReport.objects.filter(assignment=assignment).order_by('-created').first()
    # Only installers can enter edit mode
    user_is_installer = hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.INSTALLER)
    edit_mode = True if (request.GET.get('edit') == '1' and user_is_installer) else False
    # current quote items baseline
    quote = Quote.objects.filter(process_instance=instance).first()
    quote_items = list(quote.items.select_related('item').all()) if quote else []
    quote_price_map = {qi.item_id: qi.unit_price for qi in quote_items}
    items = Item.objects.filter(is_active=True, is_special=False, is_deleted=False).order_by('name')

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

    # Build approver requirements/status for UI
    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 []
    user_can_approve = any(r.role in user_roles for r in reqs)
    approvals_list = list(step_instance.approvals.select_related('role').all())
    approvals_by_role = {a.role_id: a for a in approvals_list}
    approver_statuses = [
        {
            'role': r.role,
            'status': (approvals_by_role.get(r.role_id).decision if approvals_by_role.get(r.role_id) else None),
            'reason': (approvals_by_role.get(r.role_id).reason if approvals_by_role.get(r.role_id) else ''),
        }
        for r in reqs
    ]

    # Manager approval/rejection actions
    if request.method == 'POST' and request.POST.get('action') in ['approve', 'reject']:
        action = request.POST.get('action')
        # find a matching approver role based on step requirements
        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('processes:step_detail', instance_id=instance.id, step_id=step.id)

        if not existing_report:
            messages.error(request, 'گزارش برای تایید/رد وجود ندارد.')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)

        if action == 'approve':
            existing_report.approved = True
            existing_report.save()
            StepApproval.objects.update_or_create(
                step_instance=step_instance,
                role=matching_role,
                defaults={'approved_by': request.user, 'decision': 'approved', '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('processes:step_detail', 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('processes:step_detail', instance_id=instance.id, step_id=step.id)
            StepApproval.objects.update_or_create(
                step_instance=step_instance,
                role=matching_role,
                defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason}
            )
            StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason)
            existing_report.approved = False
            existing_report.save()
            messages.success(request, 'گزارش رد شد و برای اصلاح به نصاب بازگشت.')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)

    if request.method == 'POST':
        # Only installers can submit or edit reports (non-approval actions)
        if request.POST.get('action') not in ['approve', 'reject'] and not user_is_installer:
            messages.error(request, 'شما مجوز ثبت/ویرایش گزارش نصب را ندارید')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
        description = (request.POST.get('description') or '').strip()
        visited_date = (request.POST.get('visited_date') or '').strip()
        if '/' in visited_date:
            visited_date = visited_date.replace('/', '-')
        new_serial = (request.POST.get('new_water_meter_serial') or '').strip()
        seal_number = (request.POST.get('seal_number') or '').strip()
        is_suspicious = True if request.POST.get('is_meter_suspicious') == 'on' else False
        utm_x = request.POST.get('utm_x') or None
        utm_y = request.POST.get('utm_y') or None

        # Build maps from form fields: remove and add
        remove_map = {}
        add_map = {}
        for key in request.POST.keys():
            if key.startswith('rem_') and key.endswith('_type'):
                # rem_{id}_type = 'remove'
                try:
                    item_id = int(key.split('_')[1])
                except Exception:
                    continue
                if request.POST.get(key) != 'remove':
                    continue
                qty_val = request.POST.get(f'rem_{item_id}_qty') or '1'
                try:
                    qty = int(qty_val)
                except Exception:
                    qty = 1
                remove_map[item_id] = qty
            if key.startswith('add_') and key.endswith('_type'):
                try:
                    item_id = int(key.split('_')[1])
                except Exception:
                    continue
                if request.POST.get(key) != 'add':
                    continue
                qty_val = request.POST.get(f'add_{item_id}_qty') or '1'
                price_val = request.POST.get(f'add_{item_id}_price')
                try:
                    qty = int(qty_val)
                except Exception:
                    qty = 1
                # resolve unit price
                unit_price = None
                if price_val:
                    try:
                        unit_price = Decimal(price_val)
                    except InvalidOperation:
                        unit_price = None
                if unit_price is None:
                    item_obj = Item.objects.filter(id=item_id).first()
                    unit_price = item_obj.unit_price if item_obj else None
                add_map[item_id] = {'qty': qty, 'price': unit_price}

        # اجازهٔ ثبت همزمان حذف و افزودن برای یک قلم (بدون محدودیت و ادغام)

        if existing_report and edit_mode:
            report = existing_report
            report.description = description
            report.visited_date = visited_date or None
            report.new_water_meter_serial = new_serial or None
            report.seal_number = seal_number or None
            report.is_meter_suspicious = is_suspicious
            report.utm_x = utm_x
            report.utm_y = utm_y
            report.approved = False  # back to awaiting approval after edits
            report.save()
            # delete selected existing photos
            for key, val in request.POST.items():
                if key.startswith('del_photo_') and val == '1':
                    try:
                        pid = int(key.split('_')[-1])
                        InstallationPhoto.objects.filter(id=pid, report=report).delete()
                    except Exception:
                        continue
            # append new photos
            for f in request.FILES.getlist('photos'):
                InstallationPhoto.objects.create(report=report, image=f)
            # replace item changes with new submission
            report.item_changes.all().delete()
            for item_id, qty in remove_map.items():
                up = quote_price_map.get(item_id)
                total = (up * qty) if up is not None else None
                InstallationItemChange.objects.create(
                    report=report,
                    item_id=item_id,
                    change_type='remove',
                    quantity=qty,
                    unit_price=up,
                    total_price=total,
                )
            for item_id, data in add_map.items():
                unit_price = data.get('price')
                qty = data.get('qty') or 1
                total = (unit_price * qty) if (unit_price is not None) else None
                InstallationItemChange.objects.create(
                    report=report,
                    item_id=item_id,
                    change_type='add',
                    quantity=qty,
                    unit_price=unit_price,
                    total_price=total,
                )
        else:
            report = InstallationReport.objects.create(
                assignment=assignment,
                description=description,
                visited_date=visited_date or None,
                new_water_meter_serial=new_serial or None,
                seal_number=seal_number or None,
                is_meter_suspicious=is_suspicious,
                utm_x=utm_x,
                utm_y=utm_y,
                created_by=request.user,
            )
            # photos
            for f in request.FILES.getlist('photos'):
                InstallationPhoto.objects.create(report=report, image=f)
            # item changes
            for item_id, qty in remove_map.items():
                up = quote_price_map.get(item_id)
                total = (up * qty) if up is not None else None
                InstallationItemChange.objects.create(
                    report=report,
                    item_id=item_id,
                    change_type='remove',
                    quantity=qty,
                    unit_price=up,
                    total_price=total,
                )
            for item_id, data in add_map.items():
                unit_price = data.get('price')
                qty = data.get('qty') or 1
                total = (unit_price * qty) if (unit_price is not None) else None
                InstallationItemChange.objects.create(
                    report=report,
                    item_id=item_id,
                    change_type='add',
                    quantity=qty,
                    unit_price=unit_price,
                    total_price=total,
                )

        # After installer submits/edits, set step back to in_progress and clear approvals
        step_instance.status = 'in_progress'
        step_instance.completed_at = None
        step_instance.save()
        try:
            step_instance.approvals.all().delete()
        except Exception:
            pass

        messages.success(request, 'گزارش ثبت شد و در انتظار تایید است.')
        return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)

    # Build prefill maps from existing report changes
    removed_ids = set()
    removed_qty = {}
    added_map = {}
    if existing_report:
        for ch in existing_report.item_changes.all():
            if ch.change_type == 'remove':
                removed_ids.add(ch.item_id)
                removed_qty[ch.item_id] = ch.quantity
            elif ch.change_type == 'add':
                added_map[ch.item_id] = {'qty': ch.quantity, 'price': ch.unit_price}

    return render(request, 'installations/installation_report_step.html', {
        'instance': instance,
        'step': step,
        'assignment': assignment,
        'report': existing_report,
        'edit_mode': edit_mode,
        'quote': quote,
        'quote_items': quote_items,
        'all_items': items,
        'removed_ids': removed_ids,
        'removed_qty': removed_qty,
        'added_map': added_map,
        'previous_step': previous_step,
        'next_step': next_step,
        'step_instance': step_instance,
        'approver_statuses': approver_statuses,
        'user_can_approve': user_can_approve,
    })


