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 django.core.exceptions import ValidationError
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 wells.models import WaterMeterManufacturer
from .models import InstallationAssignment, InstallationReport, InstallationPhoto, InstallationItemChange
from .forms import InstallationReportForm
from decimal import Decimal, InvalidOperation
from processes.utils import get_scoped_instance_or_404

@login_required
def installation_assign_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)
    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, county=instance.well.county).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,
    })


def create_item_changes_for_report(report, remove_map, add_map, quote_price_map):
    """Helper function to create item changes for a report"""
    # Create remove 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,
        )
    
    # Create add changes
    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,
        )


@login_required
def installation_report_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)

    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 the assigned installer can create/edit the report
    try:
        has_installer_role = bool(getattr(request.user, 'profile', None) and request.user.profile.has_role(UserRoles.INSTALLER))
    except Exception:
        has_installer_role = False
    is_assigned_installer = bool(assignment and assignment.installer_id == request.user.id)
    user_is_installer = bool(has_installer_role and is_assigned_installer)
    edit_mode = True if (request.GET.get('edit') == '1' and user_is_installer) else False
    # Prevent edit mode if an approved report exists
    if existing_report and existing_report.approved:
        edit_mode = False

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

    # 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')
    manufacturers = WaterMeterManufacturer.objects.all().order_by('name')
    
    # Initialize the form
    form = None
    if request.method == 'POST' and request.POST.get('action') not in ['approve', 'reject']:
        # Handle form submission for report creation/editing
        if not user_is_installer:
            messages.error(request, 'شما مجوز ثبت/ویرایش گزارش نصب را ندارید')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
        # Block editing approved reports
        if existing_report and existing_report.approved:
            messages.error(request, 'این گزارش قبلا تایید شده و قابل ویرایش نیست')
            return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
            
        form = InstallationReportForm(
            request.POST, 
            instance=existing_report if edit_mode else None,
            user_is_installer=user_is_installer,
            instance_well=instance.well
        )
        
        if form.is_valid():
            # Validate photos
            photo_validation_passed = False
            try:
                deleted_photo_ids = []
                for key, val in request.POST.items():
                    if key.startswith('del_photo_') and val == '1':
                        try:
                            pid = key.split('_')[-1]
                            deleted_photo_ids.append(pid)
                        except Exception:
                            continue
                
                existing_photos = existing_report.photos.all() if existing_report else None
                form.validate_photos(request.FILES, existing_photos, deleted_photo_ids)
                photo_validation_passed = True
            except ValidationError as e:
                form.add_error(None, str(e))
                # Re-render form with photo validation error
                photo_validation_passed = False
            
            # Always clear approvals/rejections when form is submitted (even if photo validation fails)
            # Reset step status and clear approvals/rejections
            step_instance.status = 'in_progress'
            step_instance.completed_at = None
            step_instance.save()
            try:
                for appr in list(step_instance.approvals.all()):
                    appr.delete()
            except Exception:
                pass
            try:
                for rej in list(step_instance.rejections.all()):
                    rej.delete()
            except Exception:
                pass

            # Reopen subsequent steps
            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':
                        instance.step_instances.filter(step=subsequent_step).update(
                            status='in_progress',
                            completed_at=None
                        )
                        try:
                            for appr in list(subsequent_step_instance.approvals.all()):
                                appr.delete()
                        except Exception:
                            pass
            except Exception:
                pass

            # Reset current step if needed
            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
            
            # Only save the report if photo validation passed
            if photo_validation_passed:
                # Save the form
                report = form.save(commit=False)
                if not existing_report:
                    report.assignment = assignment
                    report.created_by = request.user
                report.approved = False  # Reset approval status
                report.save()
                
                # Handle photo uploads and deletions
                if existing_report and edit_mode:
                    # 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
                
                # Add new photos
                for f in request.FILES.getlist('photos'):
                    InstallationPhoto.objects.create(report=report, image=f)
                
                # Handle item changes (this logic remains the same)
                remove_map = {}
                add_map = {}
                for key in request.POST.keys():
                    if key.startswith('rem_') and key.endswith('_type'):
                        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}
                
                # Replace item changes with new submission
                if existing_report and edit_mode:
                    report.item_changes.all().delete()
                create_item_changes_for_report(report, remove_map, add_map, quote_price_map)

                messages.success(request, 'گزارش ثبت شد و در انتظار تایید است.')
                return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
    else:
        # GET request or approval/rejection actions - initialize form for display
        form = InstallationReportForm(
            instance=existing_report if existing_report else None,
            user_is_installer=user_is_installer,
            instance_well=instance.well
        )

    # 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 []
    # Align permission check with invoices flow (role id intersection)
    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
    user_can_approve = can_approve_reject
    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,
        })

    # Determine if current user has already approved/rejected (to disable buttons)
    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

    # 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':
            # Record this user's approval for their role
            StepApproval.objects.create(
                step_instance=step_instance,
                role=matching_role,
                approved_by=request.user,
                reason=''
            )
            # Only mark report approved when ALL required roles have approved
            if step_instance.is_fully_approved():
                existing_report.approved = True
                existing_report.save()
                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')
            else:
                # Not fully approved yet; keep report as not approved
                if existing_report.approved:
                    existing_report.approved = False
                    existing_report.save(update_fields=['approved'])
            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)
            # Only create StepRejection for rejections, not StepApproval
            StepRejection.objects.create(step_instance=step_instance, role=matching_role, rejected_by=request.user, reason=reason)
            existing_report.approved = False
            existing_report.save()
            # If current step moved ahead of this step, reset it back for correction (align with invoices)
            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('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,
        'form': form,
        'edit_mode': edit_mode,
        'user_is_installer': user_is_installer,
        'quote': quote,
        'quote_items': quote_items,
        'all_items': items,
        'manufacturers': manufacturers,
        '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,
        'can_approve_reject': can_approve_reject,
        'current_user_has_decided': current_user_has_decided,
    })


