import requests
import re
import string
import random
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db import transaction
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import render, redirect
from expat_life.email_services import send_property_manager_signup_welcome_email
from .models import (
    PropertyManagerClientProfile,
    ManagedProperty,
    ManagedPropertyImage,
    MaintenanceServiceType,
    PropertyServiceSelection,
    ManagedPropertyDocument,
    PropertyCareGuidanceResource,
)

from django.db.models import Max
from django.urls import reverse_lazy
from django.contrib.auth.views import (
    LoginView,
    PasswordResetView,
    PasswordResetDoneView,
    PasswordResetConfirmView,
    PasswordResetCompleteView,
)
from .forms import (
    PropertyManagerSignupForm,
    PropertyManagerPasswordResetForm,
    ManagedPropertyForm,
    ManagedPropertyImageForm,
    ManagedPropertyDocumentForm,
    PropertyManagerProfileForm,
    PropertyManagerProfileImageForm,
    PropertyManagerUsernameChangeForm,
    StyledPropertyManagerPasswordChangeForm,
)
from django.views.decorators.http import require_POST
from django.utils import timezone
from .maintenance_estimator import calculate_property_maintenance_estimate
from django.contrib.auth import update_session_auth_hash

from decimal import Decimal, InvalidOperation

from expat_life.models import (
    UserPayableItem,
    WalletAccount,
    PaymentTransaction,
    PaymentTransactionItem,
)
from expat_life.utils import (
    get_property_care_plan_from_payable_item,
    mark_property_care_monthly_plan_fee_paid,
    mark_property_care_owner_transaction_items_paid,
    get_or_create_wallet_account,
    user_can_topup_wallet,
    complete_wallet_topup_transaction,
)
from rental_app.models import RentalOwner
from affiliates_app.models import AffiliateProfile
from expat_life.forms import ContactMessageForm, WalletTopUpForm
from django.urls import reverse


import stripe
from django.conf import settings

from common.payment_gateway_services import (
    get_paypal_access_token,
    get_paypal_base_url,
    get_paypal_approval_url,
    capture_paypal_order,
)

def home_property_manager(request):
    return render(request, 'property_manager_app/home_property_manager.html')

def about_us_property_manager(request):
    return render(
        request,
        'property_manager_app/about_us_property_manager.html'
    )

def property_care_terms(request):
    return render(
        request,
        'property_manager_app/property_care_terms.html'
    )

def property_care_faq(request):
    return render(
        request,
        'property_manager_app/property_care_faq.html'
    )

def property_care_privacy_policy(request):
    return render(
        request,
        'property_manager_app/property_care_privacy_policy.html'
    )

def property_care_refund_payment_policy(request):
    return render(
        request,
        'property_manager_app/property_care_refund_payment_policy.html'
    )

def property_care_disclaimer(request):
    return render(
        request,
        'property_manager_app/property_care_disclaimer.html'
    )

def property_care_cookie_policy(request):
    return render(
        request,
        'property_manager_app/property_care_cookie_policy.html'
    )

def contact_property_manager(request):
    if request.method == 'POST':
        form = ContactMessageForm(request.POST)

        if form.is_valid():
            form.save()

            return redirect(
                reverse('property_manager_app:property_manager_success_page')
                + '?type=contact'
            )
    else:
        form = ContactMessageForm()

    return render(
        request,
        'property_manager_app/contact_property_manager.html',
        {
            'form': form
        }
    )

def property_manager_success_page(request):
    success_type = request.GET.get('type', 'general')

    page_data = {
        'general': {
            'title': 'Thank You',
            'heading': 'Thank You for Reaching Out',
            'message': 'Your submission has been received successfully.',
            'button_1_text': 'Go to Home',
            'button_1_url': 'property_manager_app:home_property_manager',
            'button_2_text': 'About Ensolanka',
            'button_2_url': 'property_manager_app:about_us_property_manager',
        },

        'signup': {
            'title': 'Signup Submitted',
            'heading': 'Your Property Care Access Has Been Created',
            'message': (
                'Thank you for sharing your details. '
                'Your Property Care account has been created successfully.'
            ),
            'button_1_text': 'Go to Dashboard',
            'button_1_url': 'property_manager_app:property_manager_dashboard',
            'button_2_text': 'About Ensolanka',
            'button_2_url': 'property_manager_app:about_us_property_manager',
        },

        'contact': {
            'title': 'Message Sent',
            'heading': 'Your Message Has Been Sent Successfully',
            'message': (
                'Thank you for contacting us. '
                'We have received your message and will review it as soon as possible.'
            ),
            'button_1_text': 'Go to Home',
            'button_1_url': 'property_manager_app:home_property_manager',
            'button_2_text': 'Contact Us',
            'button_2_url': 'property_manager_app:contact_property_manager',
        },
    }

    context = page_data.get(success_type, page_data['general']).copy()

    context['signup_type'] = success_type
    context['username'] = request.session.pop('new_account_username', None)
    context['password'] = request.session.pop('new_account_password', None)
    context['new_account_email'] = request.session.pop('new_account_email', None)
    context['account_role'] = request.session.pop('account_role', None)

    return render(
        request,
        'property_manager_app/property_manager_success_page.html',
        context
    )

@login_required
def property_manager_profile_page(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return HttpResponseForbidden(
            "You do not have a Property Care owner profile."
        )

    return render(
        request,
        'property_manager_app/property_manager_profile_page.html',
        {
            'profile': profile,
            'default_profile_image': 'expat_life/images/default-profile-neutral.png',
        }
    )

@login_required
def edit_property_manager_profile(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return HttpResponseForbidden(
            "You do not have a Property Care owner profile."
        )

    if request.method == 'POST':
        form = PropertyManagerProfileForm(
            request.POST,
            instance=profile
        )

        if form.is_valid():
            # -------------------------------------------------
            # Sync full name with Django User
            # -------------------------------------------------
            full_name = form.cleaned_data.get(
                'full_name',
                ''
            ).strip()

            name_parts = full_name.split()

            request.user.first_name = (
                name_parts[0] if name_parts else ''
            )

            request.user.last_name = (
                ' '.join(name_parts[1:])
                if len(name_parts) > 1
                else ''
            )

            request.user.email = form.cleaned_data.get(
                'email',
                request.user.email
            )

            request.user.save()

            form.save()

            messages.success(
                request,
                "Your Property Care profile has been updated successfully."
            )

            return redirect(
                'property_manager_app:property_manager_profile_page'
            )
    else:
        form = PropertyManagerProfileForm(
            instance=profile
        )

    return render(
        request,
        'property_manager_app/edit_property_manager_profile.html',
        {
            'form': form,
            'profile': profile,
        }
    )

@login_required
@require_POST
def upload_property_manager_profile_image(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return JsonResponse(
            {
                'success': False,
                'error': 'You do not have a Property Care owner profile.'
            },
            status=403
        )

    if 'profile_image' not in request.FILES:
        return JsonResponse(
            {
                'success': False,
                'error': 'No image was uploaded.'
            },
            status=400
        )

    form = PropertyManagerProfileImageForm(
        request.POST,
        request.FILES,
        instance=profile
    )

    if form.is_valid():
        form.save()
        profile.refresh_from_db()

        return JsonResponse(
            {
                'success': True,
                'message': 'Profile image updated successfully.',
                'image_url': (
                    profile.profile_image.url
                    if profile.profile_image
                    else ''
                ),
            }
        )

    error_list = []

    for field_errors in form.errors.values():
        error_list.extend(field_errors)

    return JsonResponse(
        {
            'success': False,
            'error': (
                ' '.join(error_list)
                if error_list
                else 'Image upload failed.'
            ),
        },
        status=400
    )

@login_required
def change_property_manager_username(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return HttpResponseForbidden(
            "You do not have a Property Care owner profile."
        )

    if request.method == 'POST':
        form = PropertyManagerUsernameChangeForm(
            request.user,
            request.POST
        )

        if form.is_valid():
            form.save()

            messages.success(
                request,
                "Your username has been updated successfully."
            )

            return redirect(
                'property_manager_app:property_manager_profile_page'
            )
    else:
        form = PropertyManagerUsernameChangeForm(
            request.user
        )

    return render(
        request,
        'property_manager_app/change_property_manager_username.html',
        {
            'form': form,
            'profile': profile,
        }
    )

@login_required
def change_property_manager_password(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return HttpResponseForbidden(
            "You do not have a Property Care owner profile."
        )

    if request.method == 'POST':
        form = StyledPropertyManagerPasswordChangeForm(
            request.user,
            request.POST
        )

        if form.is_valid():
            user = form.save()

            update_session_auth_hash(
                request,
                user
            )

            messages.success(
                request,
                "Your password has been updated successfully."
            )

            return redirect(
                'property_manager_app:property_manager_profile_page'
            )
    else:
        form = StyledPropertyManagerPasswordChangeForm(
            request.user
        )

    return render(
        request,
        'property_manager_app/change_property_manager_password.html',
        {
            'form': form,
            'profile': profile,
        }
    )



@login_required
def managed_property_estimate(request, pk):
    """
    Displays and optionally saves a provisional monthly
    maintenance estimate for a managed property.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before generating an estimate."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty.objects.prefetch_related(
            'service_selections__service_type',
            'images',
        ),
        pk=pk,
        owner=profile
    )

    if request.method == 'POST':
        estimate = calculate_property_maintenance_estimate(
            managed_property,
            persist=True
        )

        messages.success(
            request,
            "The provisional monthly property care estimate has been generated and saved."
        )

        return render(
            request,
            'property_manager_app/managed_property_estimate.html',
            {
                'profile': profile,
                'property': managed_property,
                'estimate': estimate,
                'estimate_saved': True,
            }
        )

    estimate = calculate_property_maintenance_estimate(
        managed_property,
        persist=False
    )

    return render(
        request,
        'property_manager_app/managed_property_estimate.html',
        {
            'profile': profile,
            'property': managed_property,
            'estimate': estimate,
            'estimate_saved': False,
        }
    )

@login_required
def managed_property_list(request):
    """
    Shows all properties registered by the signed-in
    Property Care Owner.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before viewing properties."
        )
        return redirect('property_manager_app:property_manager_signup')

    properties = (
        profile.managed_properties
        .prefetch_related('images')
        .all()
    )

    total_properties = properties.count()
    approved_properties = properties.filter(is_approved=True).count()
    pending_properties = properties.filter(is_approved=False).count()
    active_properties = properties.filter(is_active=True).count()

    return render(
        request,
        'property_manager_app/managed_property_list.html',
        {
            'profile': profile,
            'properties': properties,
            'total_properties': total_properties,
            'approved_properties': approved_properties,
            'pending_properties': pending_properties,
            'active_properties': active_properties,
        }
    )

@login_required
def managed_property_detail(request, pk):
    """
    Shows full details of one managed property,
    but only if it belongs to the signed-in
    Property Care Owner.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before viewing property details."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty.objects.prefetch_related(
            'images',
            'documents',
        ),
        pk=pk,
        owner=profile
    )

    images = managed_property.images.all()
    documents = managed_property.documents.all()

    return render(
        request,
        'property_manager_app/managed_property_detail.html',
        {
            'profile': profile,
            'property': managed_property,
            'images': images,
            'documents': documents,
            'total_images': images.count(),
            'total_documents': documents.count(),
        }
    )

@login_required
def managed_property_update(request, pk):
    """
    Allows a Property Care Owner to edit one of their own
    registered managed properties.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before editing a property."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty,
        pk=pk,
        owner=profile
    )

    if request.method == 'POST':
        form = ManagedPropertyForm(
            request.POST,
            instance=managed_property
        )

        if form.is_valid():
            updated_property = form.save(commit=False)

            # Keep ownership protected and unchanged
            updated_property.owner = profile
            updated_property.save()

            updated_property.estimated_monthly_total_usd = None
            updated_property.estimate_generated_at = None
            updated_property.has_custom_quote_services = False

            updated_property.save(
                update_fields=[
                    'estimated_monthly_total_usd',
                    'estimate_generated_at',
                    'has_custom_quote_services',
                ]
            )

            messages.success(
                request,
                "Your property details have been updated successfully."
            )

            return redirect(
                'property_manager_app:managed_property_detail',
                pk=updated_property.pk
            )

        else:
            messages.error(
                request,
                "Please correct the errors below and try again."
            )

    else:
        form = ManagedPropertyForm(
            instance=managed_property
        )

    return render(
        request,
        'property_manager_app/managed_property_update.html',
        {
            'form': form,
            'profile': profile,
            'property': managed_property,
        }
    )

@login_required
def managed_property_image_upload(request, pk):
    """
    Allows a Property Care Owner to upload images
    for one of their own managed properties.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before managing property images."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty.objects.prefetch_related('images'),
        pk=pk,
        owner=profile
    )

    if request.method == 'POST':
        form = ManagedPropertyImageForm(
            request.POST,
            request.FILES
        )

        if form.is_valid():
            property_image = form.save(commit=False)
            property_image.property = managed_property

            # Automatically place new images after existing images
            current_max_order = managed_property.images.aggregate(
                max_order=Max('order')
            )['max_order']

            property_image.order = (
                current_max_order + 1
                if current_max_order is not None
                else 1
            )

            property_image.save()

            messages.success(
                request,
                "Property image uploaded successfully."
            )

            return redirect(
                'property_manager_app:managed_property_image_upload',
                pk=managed_property.pk
            )

        else:
            messages.error(
                request,
                "Please correct the image upload errors below and try again."
            )

    else:
        form = ManagedPropertyImageForm()

    images = managed_property.images.all()

    return render(
        request,
        'property_manager_app/managed_property_image_upload.html',
        {
            'profile': profile,
            'property': managed_property,
            'form': form,
            'images': images,
        }
    )

@login_required
@require_POST
def managed_property_image_delete(request, pk, image_id):
    """
    Deletes one image belonging to a property
    owned by the signed-in Property Care Owner.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before managing property images."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty,
        pk=pk,
        owner=profile
    )

    property_image = get_object_or_404(
        ManagedPropertyImage,
        pk=image_id,
        property=managed_property
    )

    # Delete the physical file from storage first
    if property_image.image:
        property_image.image.delete(save=False)

    # Delete the database row
    property_image.delete()

    messages.success(
        request,
        "Property image deleted successfully."
    )

    return redirect(
        'property_manager_app:managed_property_image_upload',
        pk=managed_property.pk
    )

@login_required
@require_POST
def managed_property_submit_for_review(request, pk):
    """
    Allows the Property Care Owner to submit
    their property for staff review.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before submitting a property for review."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty,
        pk=pk,
        owner=profile
    )

    if managed_property.review_status == 'approved':
        messages.info(
            request,
            "This property has already been approved."
        )
        return redirect(
            'property_manager_app:managed_property_detail',
            pk=managed_property.pk
        )

    if managed_property.review_status == 'submitted':
        messages.info(
            request,
            "This property has already been submitted for review."
        )
        return redirect(
            'property_manager_app:managed_property_detail',
            pk=managed_property.pk
        )

    managed_property.is_submitted_for_review = True
    managed_property.is_approved = False
    managed_property.review_status = 'submitted'
    managed_property.submitted_for_review_at = timezone.now()

    managed_property.save(
        update_fields=[
            'is_submitted_for_review',
            'is_approved',
            'review_status',
            'submitted_for_review_at',
        ]
    )

    messages.success(
        request,
        "Your property has been submitted for Ensolanka review successfully."
    )

    return redirect(
        'property_manager_app:managed_property_detail',
        pk=managed_property.pk
    )


@login_required
def managed_property_service_selection(request, pk):
    """
    Allows a Property Care Owner to select desired maintenance
    services for one of their own managed properties.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before selecting property services."
        )
        return redirect('property_manager_app:property_manager_signup')

    managed_property = get_object_or_404(
        ManagedProperty.objects.prefetch_related(
            'images',
            'service_selections__service_type',
        ),
        pk=pk,
        owner=profile
    )

    services = (
        MaintenanceServiceType.objects
        .filter(is_active=True)
        .order_by('display_order', 'category', 'name')
    )

    existing_selections = {
        selection.service_type_id: selection
        for selection in managed_property.service_selections.select_related('service_type')
    }

    frequency_choices = PropertyServiceSelection.FREQUENCY_CHOICES

    if request.method == 'POST':
        selected_service_ids = request.POST.getlist('selected_services')

        selected_service_ids = [
            int(service_id)
            for service_id in selected_service_ids
            if str(service_id).isdigit()
        ]

        valid_services = MaintenanceServiceType.objects.filter(
            id__in=selected_service_ids,
            is_active=True
        )

        valid_service_ids = set(valid_services.values_list('id', flat=True))

        # Remove unselected services from this property
        managed_property.service_selections.exclude(
            service_type_id__in=valid_service_ids
        ).delete()

        # Create/update selected service rows
        for service in valid_services:
            frequency = request.POST.get(
                f'frequency_{service.id}',
                'monthly_once'
            )

            requested_visits_per_month = request.POST.get(
                f'requested_visits_per_month_{service.id}',
                '1'
            )

            owner_note = request.POST.get(
                f'owner_note_{service.id}',
                ''
            ).strip()

            custom_frequency_note = request.POST.get(
                f'custom_frequency_note_{service.id}',
                ''
            ).strip()

            try:
                requested_visits_per_month = int(requested_visits_per_month)
            except (TypeError, ValueError):
                requested_visits_per_month = 1

            if requested_visits_per_month < 0:
                requested_visits_per_month = 0

            if requested_visits_per_month > 31:
                requested_visits_per_month = 31

            if frequency not in dict(PropertyServiceSelection.FREQUENCY_CHOICES):
                frequency = 'monthly_once'

            selection, created = PropertyServiceSelection.objects.get_or_create(
                property=managed_property,
                service_type=service,
                defaults={
                    'frequency': frequency,
                    'requested_visits_per_month': requested_visits_per_month,
                    'owner_note': owner_note,
                    'custom_frequency_note': custom_frequency_note,
                    'selection_status': 'requested',
                }
            )

            if not created:
                selection.frequency = frequency
                selection.requested_visits_per_month = requested_visits_per_month
                selection.owner_note = owner_note
                selection.custom_frequency_note = custom_frequency_note

                # If staff had previously excluded it, owner changing/requesting again
                # can move it back to requested.
                if selection.selection_status == 'not_included':
                    selection.selection_status = 'requested'

                selection.save(
                    update_fields=[
                        'frequency',
                        'requested_visits_per_month',
                        'owner_note',
                        'custom_frequency_note',
                        'selection_status',
                        'updated_at',
                    ]
                )

        managed_property.estimated_monthly_total_usd = None
        managed_property.estimate_generated_at = None
        managed_property.has_custom_quote_services = False

        managed_property.save(
            update_fields=[
                'estimated_monthly_total_usd',
                'estimate_generated_at',
                'has_custom_quote_services',
            ]
        )


        messages.success(
            request,
            "Your property care service selections have been saved successfully."
        )

        return redirect(
            'property_manager_app:managed_property_detail',
            pk=managed_property.pk
        )

    # Group services by category for template display
    grouped_services = {}

    for service in services:
        category_label = service.get_category_display()

        if category_label not in grouped_services:
            grouped_services[category_label] = []

        grouped_services[category_label].append({
            'service': service,
            'selection': existing_selections.get(service.id),
            'is_selected': service.id in existing_selections,
        })

    return render(
        request,
        'property_manager_app/managed_property_service_selection.html',
        {
            'profile': profile,
            'property': managed_property,
            'grouped_services': grouped_services,
            'frequency_choices': frequency_choices,
            'existing_selections': existing_selections,
        }
    )

@login_required
def managed_property_create(request):
    """
    Allows a Property Care Owner to add a property
    that will later be reviewed, estimated, and assigned
    service selections / maintenance plans.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    # Logged-in users from other Ensolanka apps
    # should extend their account first.
    if profile is None:
        messages.warning(
            request,
            "Please add Property Care access to your Ensolanka account before registering a property."
        )
        return redirect('property_manager_app:property_manager_signup')

    # Optional permission gate from the profile model
    if not profile.can_submit_properties:
        return HttpResponseForbidden(
            "You are not currently allowed to submit properties."
        )

    if request.method == 'POST':
        form = ManagedPropertyForm(request.POST)

        if form.is_valid():
            managed_property = form.save(commit=False)
            managed_property.owner = profile

            # Defaults are already defined in the model,
            # but keeping this explicit makes the creation intent clear.
            managed_property.is_submitted_for_review = False
            managed_property.is_approved = False
            managed_property.is_active = True

            managed_property.save()

            messages.success(
                request,
                "Your property has been added successfully. You can now continue with images, service selections, and care estimation in the next steps."
            )

            return redirect('property_manager_app:property_manager_dashboard')

        else:
            messages.error(
                request,
                "Please correct the errors below and try again."
            )

    else:
        form = ManagedPropertyForm()

    return render(
        request,
        'property_manager_app/managed_property_create.html',
        {
            'form': form,
            'profile': profile,
        }
    )

class PropertyManagerPasswordResetView(PasswordResetView):
    template_name = 'registration/property_manager_password_reset.html'
    form_class = PropertyManagerPasswordResetForm

    email_template_name = 'emails/property_manager_app/password_reset_email.txt'
    html_email_template_name = 'emails/property_manager_app/password_reset_email.html'
    subject_template_name = 'emails/property_manager_app/password_reset_subject.txt'

    success_url = reverse_lazy(
        'property_manager_app:property_manager_password_reset_done'
    )

    from_email = "Home in Sri Lanka <dsjayathunge@gmail.com>"

    def get_extra_email_context(self):
        return {
            "site_url": self.request.build_absolute_uri("/"),
        }

    def form_valid(self, form):
        self.extra_email_context = self.get_extra_email_context()
        email = form.cleaned_data.get('email')

        user_exists_as_property_manager = User.objects.filter(
            email__iexact=email,
            property_manager_client_profile__isnull=False,
            is_active=True
        ).exists()

        if not user_exists_as_property_manager:
            messages.warning(
                self.request,
                "If this email belongs to a registered Property Care Owner account, "
                "password reset instructions will be sent."
            )

        return super().form_valid(form)


class PropertyManagerPasswordResetDoneView(PasswordResetDoneView):
    template_name = 'registration/property_manager_password_reset_done.html'


class PropertyManagerPasswordResetConfirmView(PasswordResetConfirmView):
    template_name = 'registration/property_manager_password_reset_confirm.html'
    success_url = reverse_lazy(
        'property_manager_app:property_manager_password_reset_complete'
    )


class PropertyManagerPasswordResetCompleteView(PasswordResetCompleteView):
    template_name = 'registration/property_manager_password_reset_complete.html'




def generate_property_manager_username(full_name):
    base_name = re.sub(r'[^a-zA-Z0-9]', '', full_name.lower())

    if not base_name:
        base_name = 'owner'

    base = f"pm_{base_name}"
    username = base
    counter = 1

    while User.objects.filter(username=username).exists():
        username = f"{base}{counter}"
        counter += 1

    return username

def generate_random_password(length=10):
    chars = string.ascii_letters + string.digits
    return ''.join(random.choice(chars) for _ in range(length))

@transaction.atomic
def property_manager_signup(request):
    """
    Property Manager / Property Care signup flow.

    Supports:
    1. New public users:
       - Create User
       - Create PropertyManagerClientProfile
       - Log in automatically
       - Store temporary login credentials in session
       - Redirect to dashboard

    2. Already authenticated users:
       - Do NOT create another User
       - Create PropertyManagerClientProfile under same account
       - Redirect to dashboard
    """

    # ---------------------------------------------------------
    # If logged-in user already has property manager profile
    # ---------------------------------------------------------
    if request.user.is_authenticated:
        existing_profile = getattr(
            request.user,
            'property_manager_client_profile',
            None
        )

        if existing_profile:
            messages.info(
                request,
                "You already have a property care account."
            )
            return redirect('property_manager_app:property_manager_dashboard')

    # ---------------------------------------------------------
    # POST REQUEST
    # ---------------------------------------------------------
    if request.method == 'POST':
        form = PropertyManagerSignupForm(request.POST, request.FILES)

        if form.is_valid():
            profile = form.save(commit=False)

            full_name = form.cleaned_data['full_name'].strip()
            submitted_email = form.cleaned_data['email'].strip().lower()

            # =================================================
            # CASE 1: USER IS ALREADY LOGGED IN
            # Extend the existing account with property manager profile
            # =================================================
            if request.user.is_authenticated:
                user = request.user

                profile.user = user

                # Best to keep profile email aligned with account email
                # If user account email is missing, fallback to submitted email.
                profile.email = user.email.strip().lower() if user.email else submitted_email

                profile.is_approved = True
                profile.can_submit_properties = True
                profile.save()

                request.session['account_role'] = 'Property Care Owner'
                request.session['just_signed_up'] = True

                messages.success(
                    request,
                    "Your property care profile has been created successfully under your existing account."
                )

                return redirect('property_manager_app:property_manager_dashboard')

            # =================================================
            # CASE 2: PUBLIC VISITOR — CREATE NEW USER ACCOUNT
            # =================================================
            else:
                existing_user = User.objects.filter(
                    email__iexact=submitted_email
                ).first()

                if existing_user:
                    form.add_error(
                        'email',
                        'An account with this email already exists. Please sign in first and then extend your account for property care.'
                    )
                else:
                    username = generate_property_manager_username(full_name)
                    password = generate_random_password()

                    name_parts = full_name.split()
                    first_name = name_parts[0] if name_parts else ''
                    last_name = ' '.join(name_parts[1:]) if len(name_parts) > 1 else ''

                    user = User.objects.create_user(
                        username=username,
                        email=submitted_email,
                        password=password,
                        first_name=first_name,
                        last_name=last_name
                    )

                    profile.user = user
                    profile.email = submitted_email
                    profile.is_approved = True
                    profile.can_submit_properties = True
                    profile.save()

                    # -----------------------------------------------------
                    # Optional future payment sync hook
                    # Use this later if you add signup / service fees
                    # -----------------------------------------------------
                    # update_property_manager_payable_items(user)

                    # -----------------------------------------------------
                    # Optional future welcome email hook
                    # Similar to rental owner signup welcome email
                    # -----------------------------------------------------
                    transaction.on_commit(
                        lambda: send_property_manager_signup_welcome_email(
                            user=user,
                            full_name=full_name,
                            email=submitted_email,
                            username=username,
                            temporary_password=password,
                            request=request,
                        )
                    )

                    login(request, user)

                    request.session['new_account_username'] = username
                    request.session['new_account_password'] = password
                    request.session['new_account_email'] = submitted_email
                    request.session['account_role'] = 'Property Care Owner'
                    request.session['just_signed_up'] = True

                    messages.success(
                        request,
                        "Your property care account has been created successfully."
                    )

                    return redirect('property_manager_app:property_manager_dashboard')

        else:
            messages.error(
                request,
                "Please correct the errors below and try again."
            )

    # ---------------------------------------------------------
    # GET REQUEST
    # ---------------------------------------------------------
    else:
        initial_data = {}

        # Pre-fill fields for authenticated users who are extending account
        if request.user.is_authenticated:
            full_name = f"{request.user.first_name} {request.user.last_name}".strip()

            initial_data = {
                'full_name': full_name or request.user.username,
                'email': request.user.email,
            }

        form = PropertyManagerSignupForm(initial=initial_data)

    return render(
        request,
        'property_manager_app/property_manager_signup.html',
        {
            'form': form,
            'is_account_extension': request.user.is_authenticated,
        }
    )

@login_required
def property_manager_dashboard(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        return HttpResponseForbidden(
            "You do not have a property care owner profile."
        )

    has_rental_owner_profile = hasattr(
        request.user,
        'rental_owner_profile'
    )
    has_affiliate_profile = hasattr(
        request.user,
        'affiliate_profile'
    )
    properties = profile.managed_properties.prefetch_related('images').all()

    total_properties = properties.count()
    approved_properties = properties.filter(is_approved=True).count()
    pending_properties = properties.filter(is_approved=False).count()
    active_properties = properties.filter(is_active=True).count()

    default_profile_image = 'expat_life/images/default-profile-neutral.png'

    return render(
        request,
        'property_manager_app/property_manager_dashboard.html',
        {
            'profile': profile,
            'properties': properties,
            'total_properties': total_properties,
            'approved_properties': approved_properties,
            'pending_properties': pending_properties,
            'active_properties': active_properties,
            'default_profile_image': default_profile_image,
            'has_rental_owner_profile': has_rental_owner_profile,
            'has_affiliate_profile': has_affiliate_profile,
            # Just-signup session values
            'just_signed_up': request.session.pop('just_signed_up', False),
            'username': request.session.pop('new_account_username', None),
            'password': request.session.pop('new_account_password', None),
            'new_account_email': request.session.pop('new_account_email', None),
            'account_role': request.session.pop('account_role', None),
        }
    )


class PropertyManagerLoginView(LoginView):
    template_name = 'registration/property_manager_login.html'

    def form_valid(self, form):
        response = super().form_valid(form)
        user = self.request.user

        property_manager_profile = getattr(
            user,
            'property_manager_client_profile',
            None
        )

        # ✅ If valid property care owner → proceed normally
        if property_manager_profile:
            return response

        # ❌ Not a property care owner → logout + return to same login page
        messages.error(
            self.request,
            "This login is only for Property Care Owners. Please use the correct login page."
        )

        logout(self.request)

        return redirect('property_manager_app:property_manager_login')

    def get_success_url(self):
        return self.get_redirect_url() or reverse_lazy(
            'property_manager_app:property_manager_dashboard'
        )
    
@login_required
def property_care_payment_center(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if not profile:
        messages.error(
            request,
            "Property Care Owner profile not found."
        )
        return redirect('property_manager_app:home_property_manager')

    # Property Care wallet
    wallet, created = WalletAccount.objects.get_or_create(
        user=request.user,
        profile_type='property_care_owner',
        defaults={'balance_usd': Decimal('0.00')}
    )

    payable_items = list(
        UserPayableItem.objects.filter(
            user=request.user,
            profile_type='property_care_owner',
            is_active=True
        )
    )

    payment_order = {
        'property_care_monthly_plan_fee': 1,
        'other': 99,
    }

    payable_items.sort(
        key=lambda item: (
            payment_order.get(item.payment_for, 50),
            item.due_date or item.created_at.date(),
            item.created_at,
        )
    )

    due_items = [
        item for item in payable_items
        if item.payment_status == 'due'
    ]

    upcoming_items = [
        item for item in payable_items
        if item.payment_status == 'upcoming' and not item.is_optional
    ]

    optional_items = [
        item for item in payable_items
        if item.is_optional and item.payment_status not in ['due', 'paid']
    ]

    paid_items = [
        item for item in payable_items
        if item.payment_status == 'paid'
    ]

    selectable_item_ids = [
        item.id for item in payable_items
        if item.payment_status == 'due' and item.amount_usd > 0
    ]

    total_due = sum(
        (item.amount_usd for item in due_items),
        Decimal('0.00')
    )

    total_upcoming = sum(
        (item.amount_usd for item in upcoming_items),
        Decimal('0.00')
    )

    total_paid = sum(
        (item.amount_usd for item in paid_items),
        Decimal('0.00')
    )

    managed_properties_count = profile.managed_properties.count()

    active_care_plans_count = profile.managed_properties.filter(
        care_plan__plan_status='finalized'
    ).count()

    active_due_plan_fees_count = UserPayableItem.objects.filter(
        user=request.user,
        profile_type='property_care_owner',
        payment_for='property_care_monthly_plan_fee',
        payment_status='due',
        is_active=True
    ).count()

    context = {
        'profile': profile,
        'wallet': wallet,

        'payable_items': payable_items,
        'due_items': due_items,
        'upcoming_items': upcoming_items,
        'optional_items': optional_items,
        'paid_items': paid_items,
        'selectable_item_ids': selectable_item_ids,

        'total_due': total_due,
        'total_upcoming': total_upcoming,
        'total_paid': total_paid,

        'managed_properties_count': managed_properties_count,
        'active_care_plans_count': active_care_plans_count,
        'active_due_plan_fees_count': active_due_plan_fees_count,
    }

    return render(
        request,
        'property_manager_app/payment_center.html',
        context
    )

@login_required
def property_care_payment_checkout(request):
    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if not profile:
        messages.error(
            request,
            "Property Care Owner profile not found."
        )
        return redirect('property_manager_app:home_property_manager')

    if request.method != 'POST':
        messages.error(
            request,
            "Please select at least one payment item first."
        )
        return redirect('property_manager_app:property_care_payment_center')

    wallet, created = WalletAccount.objects.get_or_create(
        user=request.user,
        profile_type='property_care_owner',
        defaults={'balance_usd': Decimal('0.00')}
    )

    selected_ids = request.POST.getlist('selected_items')

    if not selected_ids:
        messages.error(
            request,
            "Please select at least one payment item."
        )
        return redirect('property_manager_app:property_care_payment_center')

    payable_items = list(
        UserPayableItem.objects.filter(
            id__in=selected_ids,
            user=request.user,
            profile_type='property_care_owner',
            is_active=True,
            payment_status='due'
        ).exclude(amount_usd__lte=0)
    )

    if not payable_items:
        messages.error(
            request,
            "No valid payable items were selected."
        )
        return redirect('property_manager_app:property_care_payment_center')

    total_amount = sum(
        (item.amount_usd for item in payable_items),
        Decimal('0.00')
    )

    context = {
        'profile': profile,
        'wallet': wallet,
        'payable_items': payable_items,
        'selected_ids': [str(item.id) for item in payable_items],
        'total_amount': total_amount,
    }

    return render(
        request,
        'property_manager_app/payment_checkout.html',
        context
    )

@login_required
def property_care_payment_checkout_pay(request):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    if request.method != 'POST':
        messages.error(request, "Invalid payment request.")
        return redirect('property_manager_app:property_care_payment_center')

    selected_ids = request.POST.getlist('selected_items')
    payment_method = request.POST.get('payment_method')
    use_wallet = request.POST.get('use_wallet') == '1'

    if not selected_ids:
        messages.error(request, "Please select at least one payment item.")
        return redirect('property_manager_app:property_care_payment_center')

    payable_items = list(
        UserPayableItem.objects.filter(
            id__in=selected_ids,
            user=request.user,
            profile_type='property_care_owner',
            is_active=True,
            payment_status='due',
        ).exclude(amount_usd__lte=0)
    )

    if not payable_items:
        messages.error(request, "No valid payable items were selected.")
        return redirect('property_manager_app:property_care_payment_center')

    total_amount = sum(
        (item.amount_usd for item in payable_items),
        Decimal('0.00')
    ).quantize(Decimal('0.01'))

    wallet, created = WalletAccount.objects.get_or_create(
        user=request.user,
        profile_type='property_care_owner',
        defaults={
            'balance_usd': Decimal('0.00'),
            'is_active': True,
        }
    )

    wallet_balance = wallet.balance_usd or Decimal('0.00')
    wallet_deduction = Decimal('0.00')
    amount_to_charge = total_amount

    if use_wallet and wallet_balance > Decimal('0.00'):
        wallet_deduction = min(wallet_balance, total_amount).quantize(Decimal('0.01'))
        amount_to_charge = (total_amount - wallet_deduction).quantize(Decimal('0.01'))

    # ---------------------------------------------------------
    # Wallet-only payment
    # ---------------------------------------------------------
    if amount_to_charge <= Decimal('0.00'):
        with transaction.atomic():
            locked_wallet = WalletAccount.objects.select_for_update().get(
                id=wallet.id
            )

            if locked_wallet.balance_usd < wallet_deduction:
                messages.error(
                    request,
                    "Your wallet balance changed. Please try again."
                )
                return redirect('property_manager_app:property_care_payment_center')

            payment_transaction = PaymentTransaction.objects.create(
                user=request.user,
                profile_type='property_care_owner',
                payment_purpose='payable_items',
                payment_method='wallet',
                total_amount_usd=total_amount,
                wallet_deduction_usd=wallet_deduction,
                gateway_amount_usd=Decimal('0.00'),
                status='paid',
                paid_at=timezone.now(),
            )

            for item in payable_items:
                PaymentTransactionItem.objects.create(
                    transaction=payment_transaction,
                    payable_item=item,
                    amount_usd=item.amount_usd,
                )

            mark_result = mark_property_care_owner_transaction_items_paid(
                payment_transaction=payment_transaction,
                notes=f"Paid using wallet. Transaction #{payment_transaction.id}",
            )

            if not mark_result["success"]:
                payment_transaction.status = 'failed'
                payment_transaction.failure_reason = "\n".join(mark_result["failed"])
                payment_transaction.failed_at = timezone.now()
                payment_transaction.save(update_fields=[
                    'status',
                    'failure_reason',
                    'failed_at',
                    'updated_at',
                ])

                messages.error(
                    request,
                    "Payment could not be fully completed. Please contact support."
                )

                return redirect(
                    'property_manager_app:property_care_payment_failed',
                    transaction_id=payment_transaction.id
                )

            locked_wallet.balance_usd -= wallet_deduction
            locked_wallet.save(update_fields=['balance_usd'])

        request.session.pop('property_care_payment_transaction_id', None)
        request.session.pop('property_care_checkout_selected_ids', None)
        request.session.pop('property_care_checkout_total', None)
        request.session.pop('property_care_checkout_profile_type', None)
        request.session.pop('property_care_use_wallet', None)
        request.session.pop('property_care_wallet_deduction', None)

        messages.success(
            request,
            "Payment completed successfully using your wallet balance."
        )

        return redirect(
            'property_manager_app:property_care_payment_success',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # Gateway payment
    # ---------------------------------------------------------
    if payment_method == 'stripe':
        final_payment_method = (
            'mixed_stripe_wallet'
            if wallet_deduction > Decimal('0.00')
            else 'stripe'
        )

    elif payment_method == 'paypal':
        final_payment_method = (
            'mixed_paypal_wallet'
            if wallet_deduction > Decimal('0.00')
            else 'paypal'
        )

    else:
        messages.error(request, "Please select a valid payment method.")
        return redirect('property_manager_app:property_care_payment_center')

    with transaction.atomic():
        payment_transaction = PaymentTransaction.objects.create(
            user=request.user,
            profile_type='property_care_owner',
            payment_purpose='payable_items',
            payment_method=final_payment_method,
            total_amount_usd=total_amount,
            wallet_deduction_usd=wallet_deduction,
            gateway_amount_usd=amount_to_charge,
            status='pending',
        )

        for item in payable_items:
            PaymentTransactionItem.objects.create(
                transaction=payment_transaction,
                payable_item=item,
                amount_usd=item.amount_usd,
            )

    request.session['property_care_payment_transaction_id'] = payment_transaction.id
    request.session['property_care_checkout_selected_ids'] = [
        item.id for item in payable_items
    ]
    request.session['property_care_checkout_total'] = str(amount_to_charge)
    request.session['property_care_checkout_profile_type'] = 'property_care_owner'
    request.session['property_care_use_wallet'] = use_wallet
    request.session['property_care_wallet_deduction'] = str(wallet_deduction)

    if payment_method == 'stripe':
        return redirect('property_manager_app:start_property_care_stripe_checkout')

    if payment_method == 'paypal':
        return redirect('property_manager_app:start_property_care_paypal_checkout')

    messages.error(request, "Please select a valid payment method.")
    return redirect('property_manager_app:property_care_payment_center')
    
@login_required
def start_property_care_stripe_checkout(request):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    transaction_id = request.session.get('property_care_payment_transaction_id')

    if not transaction_id:
        messages.error(request, "No checkout transaction found.")
        return redirect('property_manager_app:property_care_payment_center')

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        status='pending',
    )

    if payment_transaction.payment_method not in ['stripe', 'mixed_stripe_wallet']:
        messages.error(request, "Invalid payment method for Stripe checkout.")
        return redirect('property_manager_app:property_care_payment_center')

    if payment_transaction.gateway_amount_usd <= Decimal('0.00'):
        messages.error(request, "Invalid Stripe payment amount.")
        return redirect('property_manager_app:property_care_payment_center')

    payable_items = list(
        payment_transaction.items.select_related('payable_item').all()
    )

    if not payable_items:
        messages.error(request, "No payment items found for this transaction.")
        return redirect('property_manager_app:property_care_payment_center')

    if not settings.STRIPE_SECRET_KEY:
        messages.error(request, "Stripe payment is not configured.")
        return redirect('property_manager_app:property_care_payment_center')

    stripe.api_key = settings.STRIPE_SECRET_KEY

    try:
        line_items = []

        # If wallet is partially used, Stripe should charge only the remaining gateway amount.
        if payment_transaction.wallet_deduction_usd > Decimal('0.00'):
            stripe_amount_cents = int(
                (payment_transaction.gateway_amount_usd * Decimal('100')).quantize(Decimal('1'))
            )

            line_items = [{
                'price_data': {
                    'currency': 'usd',
                    'product_data': {
                        'name': f'Property Care Payment - Transaction #{payment_transaction.id}',
                        'description': (
                            f'Total: ${payment_transaction.total_amount_usd}, '
                            f'Wallet used: ${payment_transaction.wallet_deduction_usd}, '
                            f'Stripe payment: ${payment_transaction.gateway_amount_usd}'
                        ),
                    },
                    'unit_amount': stripe_amount_cents,
                },
                'quantity': 1,
            }]

        else:
            for transaction_item in payable_items:
                payable_item = transaction_item.payable_item

                stripe_amount_cents = int(
                    (transaction_item.amount_usd * Decimal('100')).quantize(Decimal('1'))
                )

                line_items.append({
                    'price_data': {
                        'currency': 'usd',
                        'product_data': {
                            'name': payable_item.title,
                            'description': (
                                getattr(payable_item, 'description', '')
                                or 'Property Care monthly plan payment'
                            ),
                        },
                        'unit_amount': stripe_amount_cents,
                    },
                    'quantity': 1,
                })

        success_url = request.build_absolute_uri(
            reverse(
                'property_manager_app:property_care_stripe_payment_success',
                kwargs={'transaction_id': payment_transaction.id}
            )
        ) + '?session_id={CHECKOUT_SESSION_ID}'

        cancel_url = request.build_absolute_uri(
            reverse(
                'property_manager_app:property_care_payment_cancelled',
                kwargs={'transaction_id': payment_transaction.id}
            )
        )

        checkout_session = stripe.checkout.Session.create(
            payment_method_types=['card'],
            mode='payment',
            line_items=line_items,
            success_url=success_url,
            cancel_url=cancel_url,
            customer_email=request.user.email or None,
            client_reference_id=str(payment_transaction.id),
            metadata={
                'payment_transaction_id': str(payment_transaction.id),
                'user_id': str(request.user.id),
                'profile_type': 'property_care_owner',
                'payment_purpose': 'payable_items',
                'payment_for': 'property_care_monthly_plan_fee',
            },
        )

        if not checkout_session.url:
            messages.error(
                request,
                "Stripe checkout session was created, but no checkout URL was returned."
            )
            return redirect('property_manager_app:property_care_payment_center')

        payment_transaction.gateway_session_id = checkout_session.id
        payment_transaction.status = 'processing'
        payment_transaction.save(update_fields=[
            'gateway_session_id',
            'status',
            'updated_at',
        ])

        return redirect(checkout_session.url)

    except Exception as e:
        payment_transaction.failure_reason = str(e)
        payment_transaction.save(update_fields=[
            'failure_reason',
            'updated_at',
        ])

        messages.error(
            request,
            "Stripe checkout could not be started. Please try again."
        )

        return redirect('property_manager_app:property_care_payment_center')

@login_required
def property_care_stripe_payment_success(request, transaction_id):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    session_id = request.GET.get('session_id')

    if not session_id:
        messages.error(request, "Stripe payment session not found.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=transaction_id
        )

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
    )

    # Prevent duplicate wallet deduction / duplicate recurring payable processing.
    if payment_transaction.status == 'paid':
        messages.success(request, "This payment has already been completed.")
        return redirect(
            'property_manager_app:property_care_payment_success',
            transaction_id=payment_transaction.id
        )

    if payment_transaction.payment_method not in ['stripe', 'mixed_stripe_wallet']:
        messages.error(request, "Invalid payment method for Stripe confirmation.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    if not settings.STRIPE_SECRET_KEY:
        messages.error(request, "Stripe payment is not configured.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    stripe.api_key = settings.STRIPE_SECRET_KEY

    # ---------------------------------------------------------
    # 1. Retrieve Stripe Checkout Session
    # ---------------------------------------------------------
    try:
        stripe_session = stripe.checkout.Session.retrieve(session_id)

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"Stripe session retrieve failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Could not verify Stripe payment.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # 2. Verify Stripe metadata transaction ID
    # ---------------------------------------------------------
    metadata_transaction_id = None
    stripe_metadata = getattr(stripe_session, 'metadata', None)

    if stripe_metadata:
        try:
            stripe_metadata = dict(stripe_metadata)
            metadata_transaction_id = stripe_metadata.get('payment_transaction_id')
        except Exception:
            metadata_transaction_id = getattr(
                stripe_metadata,
                'payment_transaction_id',
                None
            )

    if str(metadata_transaction_id) != str(payment_transaction.id):
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = "Stripe metadata transaction ID mismatch."
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Stripe payment verification failed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # 3. Verify Stripe payment status
    # ---------------------------------------------------------
    stripe_payment_status = getattr(stripe_session, 'payment_status', None)

    if stripe_payment_status != 'paid':
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = (
            f"Stripe payment status was not paid: {stripe_payment_status}"
        )
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Stripe payment was not completed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # 4. Verify Stripe amount
    # ---------------------------------------------------------
    expected_amount_cents = int(
        (payment_transaction.gateway_amount_usd * Decimal('100')).quantize(Decimal('1'))
    )

    stripe_amount_total = getattr(stripe_session, 'amount_total', None)

    if stripe_amount_total is not None and int(stripe_amount_total) != expected_amount_cents:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = (
            f"Stripe amount mismatch. Expected {expected_amount_cents}, "
            f"received {stripe_amount_total}."
        )
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Stripe payment amount verification failed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # 5. Payment verified. Safely complete database updates.
    # ---------------------------------------------------------
    try:
        with transaction.atomic():
            locked_transaction = PaymentTransaction.objects.select_for_update().get(
                id=payment_transaction.id,
                user=request.user,
                profile_type='property_care_owner',
            )

            if locked_transaction.status == 'paid':
                messages.success(request, "This payment has already been completed.")
                return redirect(
                    'property_manager_app:property_care_payment_success',
                    transaction_id=locked_transaction.id
                )

            wallet_deduction = locked_transaction.wallet_deduction_usd or Decimal('0.00')

            # Deduct wallet only for mixed Stripe + wallet payments.
            if wallet_deduction > Decimal('0.00'):
                wallet = WalletAccount.objects.select_for_update().get(
                    user=request.user,
                    profile_type='property_care_owner',
                )

                if wallet.balance_usd < wallet_deduction:
                    locked_transaction.status = 'failed'
                    locked_transaction.failure_reason = (
                        "Wallet balance was insufficient during Stripe success confirmation."
                    )
                    locked_transaction.failed_at = timezone.now()
                    locked_transaction.save(update_fields=[
                        'status',
                        'failure_reason',
                        'failed_at',
                        'updated_at',
                    ])

                    messages.error(
                        request,
                        "Payment was received by Stripe, but wallet deduction failed. "
                        "Please contact support."
                    )
                    return redirect(
                        'property_manager_app:property_care_payment_failed',
                        transaction_id=locked_transaction.id
                    )

                wallet.balance_usd -= wallet_deduction
                wallet.save(update_fields=['balance_usd'])

            # Mark selected Property Care payable items as paid.
            # For monthly plan fee, this should also deactivate current cycle
            # and create the next monthly payable item.
            mark_result = mark_property_care_owner_transaction_items_paid(
                payment_transaction=locked_transaction,
                notes=f"Paid by Stripe. Transaction #{locked_transaction.id}",
            )

            if not mark_result["success"]:
                locked_transaction.status = 'failed'
                locked_transaction.failure_reason = "\n".join(mark_result["failed"])
                locked_transaction.failed_at = timezone.now()
                locked_transaction.save(update_fields=[
                    'status',
                    'failure_reason',
                    'failed_at',
                    'updated_at',
                ])

                messages.error(
                    request,
                    "Stripe payment was received, but the system could not complete all Property Care payment actions. "
                    "Please contact support."
                )

                return redirect(
                    'property_manager_app:property_care_payment_failed',
                    transaction_id=locked_transaction.id
                )

            locked_transaction.gateway_session_id = stripe_session.id
            locked_transaction.gateway_transaction_id = (
                getattr(stripe_session, 'payment_intent', '') or stripe_session.id
            )
            locked_transaction.status = 'paid'
            locked_transaction.paid_at = timezone.now()
            locked_transaction.failure_reason = ''
            locked_transaction.save(update_fields=[
                'gateway_session_id',
                'gateway_transaction_id',
                'status',
                'paid_at',
                'failure_reason',
                'updated_at',
            ])

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"Database payment completion failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(
            request,
            "Payment was verified, but the system could not complete the payment record. "
            "Please contact support."
        )

        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    # ---------------------------------------------------------
    # 6. Clear checkout session data
    # ---------------------------------------------------------
    request.session.pop('property_care_payment_transaction_id', None)
    request.session.pop('property_care_checkout_selected_ids', None)
    request.session.pop('property_care_checkout_total', None)
    request.session.pop('property_care_checkout_profile_type', None)
    request.session.pop('property_care_use_wallet', None)
    request.session.pop('property_care_wallet_deduction', None)

    messages.success(request, "Stripe payment completed successfully.")

    return redirect(
        'property_manager_app:property_care_payment_success',
        transaction_id=payment_transaction.id
    )

@login_required
def property_care_payment_success(request, transaction_id):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
    )

    return render(request, 'property_manager_app/property_care_payment_success.html', {
        'owner_profile': owner_profile,
        'payment_transaction': payment_transaction,
        'page_title': 'Payment Successful',
    })

@login_required
def property_care_payment_cancelled(request, transaction_id):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
    )

    if payment_transaction.status in ['pending', 'processing']:
        payment_transaction.status = 'cancelled'
        payment_transaction.cancelled_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'cancelled_at',
            'updated_at',
        ])

    messages.warning(
        request,
        "Payment was cancelled. Your selected Property Care payment items are still due."
    )

    return render(request, 'property_manager_app/property_care_payment_cancelled.html', {
        'owner_profile': owner_profile,
        'payment_transaction': payment_transaction,
        'page_title': 'Payment Cancelled',
    })

@login_required
def property_care_payment_failed(request, transaction_id):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
    )

    return render(request, 'property_manager_app/property_care_payment_failed.html', {
        'owner_profile': owner_profile,
        'payment_transaction': payment_transaction,
        'page_title': 'Payment Failed',
    })



@login_required
def start_property_care_paypal_checkout(request):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    transaction_id = request.session.get('property_care_payment_transaction_id')

    if not transaction_id:
        messages.error(request, "No checkout transaction found.")
        return redirect('property_manager_app:property_care_payment_center')

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        status='pending',
    )

    if payment_transaction.payment_method not in ['paypal', 'mixed_paypal_wallet']:
        messages.error(request, "Invalid payment method for PayPal checkout.")
        return redirect('property_manager_app:property_care_payment_center')

    if payment_transaction.gateway_amount_usd <= Decimal('0.00'):
        messages.error(request, "Invalid PayPal payment amount.")
        return redirect('property_manager_app:property_care_payment_center')

    payable_items = list(
        payment_transaction.items.select_related('payable_item').all()
    )

    if not payable_items:
        messages.error(request, "No payment items found for this transaction.")
        return redirect('property_manager_app:property_care_payment_center')

    try:
        order_data = create_property_care_paypal_order(
            payment_transaction=payment_transaction,
            request=request,
        )

        approval_url = get_paypal_approval_url(order_data)

        if not approval_url:
            raise Exception("PayPal approval URL not found.")

        payment_transaction.gateway_session_id = order_data.get('id', '')
        payment_transaction.status = 'processing'
        payment_transaction.save(update_fields=[
            'gateway_session_id',
            'status',
            'updated_at',
        ])

        return redirect(approval_url)

    except Exception as e:
        payment_transaction.failure_reason = str(e)
        payment_transaction.save(update_fields=[
            'failure_reason',
            'updated_at',
        ])

        messages.error(
            request,
            "PayPal checkout could not be started. Please try again."
        )

        return redirect('property_manager_app:property_care_payment_center')
    
@login_required
def property_care_paypal_payment_success(request, transaction_id):
    owner_profile = getattr(request.user, 'property_manager_client_profile', None)

    if not owner_profile:
        messages.error(request, "Property Care owner profile not found.")
        return redirect('property_manager_app:home_property_care')

    paypal_order_id = request.GET.get('token')

    if not paypal_order_id:
        messages.error(request, "PayPal payment order not found.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=transaction_id
        )

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
    )

    if payment_transaction.status == 'paid':
        messages.success(request, "This payment has already been completed.")
        return redirect(
            'property_manager_app:property_care_payment_success',
            transaction_id=payment_transaction.id
        )

    if payment_transaction.payment_method not in ['paypal', 'mixed_paypal_wallet']:
        messages.error(request, "Invalid payment method for PayPal confirmation.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    if payment_transaction.gateway_session_id and payment_transaction.gateway_session_id != paypal_order_id:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = "PayPal order ID mismatch."
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "PayPal payment verification failed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    try:
        capture_data = capture_paypal_order(paypal_order_id)

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"PayPal capture failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Could not capture PayPal payment.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    paypal_status = capture_data.get('status')

    if paypal_status != 'COMPLETED':
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = (
            f"PayPal payment status was not completed: {paypal_status}"
        )
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "PayPal payment was not completed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    try:
        purchase_units = capture_data.get('purchase_units', [])
        first_purchase_unit = purchase_units[0] if purchase_units else {}
        payments = first_purchase_unit.get('payments', {})
        captures = payments.get('captures', [])
        first_capture = captures[0] if captures else {}

        captured_amount = first_capture.get('amount', {})
        captured_value = Decimal(
            str(captured_amount.get('value', '0.00'))
        ).quantize(Decimal('0.01'))
        captured_currency = captured_amount.get('currency_code')

        expected_amount = payment_transaction.gateway_amount_usd.quantize(
            Decimal('0.01')
        )

        if captured_currency != 'USD' or captured_value != expected_amount:
            payment_transaction.status = 'failed'
            payment_transaction.failure_reason = (
                f"PayPal amount mismatch. Expected USD {expected_amount}, "
                f"received {captured_currency} {captured_value}."
            )
            payment_transaction.failed_at = timezone.now()
            payment_transaction.save(update_fields=[
                'status',
                'failure_reason',
                'failed_at',
                'updated_at',
            ])

            messages.error(request, "PayPal payment amount verification failed.")
            return redirect(
                'property_manager_app:property_care_payment_failed',
                transaction_id=payment_transaction.id
            )

        paypal_capture_id = first_capture.get('id', paypal_order_id)

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"PayPal capture response parsing failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "PayPal payment verification failed.")
        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    try:
        with transaction.atomic():
            locked_transaction = PaymentTransaction.objects.select_for_update().get(
                id=payment_transaction.id,
                user=request.user,
                profile_type='property_care_owner',
            )

            if locked_transaction.status == 'paid':
                messages.success(request, "This payment has already been completed.")
                return redirect(
                    'property_manager_app:property_care_payment_success',
                    transaction_id=locked_transaction.id
                )

            wallet_deduction = locked_transaction.wallet_deduction_usd or Decimal('0.00')

            if wallet_deduction > Decimal('0.00'):
                wallet = WalletAccount.objects.select_for_update().get(
                    user=request.user,
                    profile_type='property_care_owner',
                )

                if wallet.balance_usd < wallet_deduction:
                    locked_transaction.status = 'failed'
                    locked_transaction.failure_reason = (
                        "Wallet balance was insufficient during PayPal success confirmation."
                    )
                    locked_transaction.failed_at = timezone.now()
                    locked_transaction.save(update_fields=[
                        'status',
                        'failure_reason',
                        'failed_at',
                        'updated_at',
                    ])

                    messages.error(
                        request,
                        "Payment was received by PayPal, but wallet deduction failed. "
                        "Please contact support."
                    )

                    return redirect(
                        'property_manager_app:property_care_payment_failed',
                        transaction_id=locked_transaction.id
                    )

                wallet.balance_usd -= wallet_deduction
                wallet.save(update_fields=['balance_usd'])

            mark_result = mark_property_care_owner_transaction_items_paid(
                payment_transaction=locked_transaction,
                notes=f"Paid by PayPal. Transaction #{locked_transaction.id}",
            )

            if not mark_result["success"]:
                locked_transaction.status = 'failed'
                locked_transaction.failure_reason = "\n".join(mark_result["failed"])
                locked_transaction.failed_at = timezone.now()
                locked_transaction.save(update_fields=[
                    'status',
                    'failure_reason',
                    'failed_at',
                    'updated_at',
                ])

                messages.error(
                    request,
                    "PayPal payment was received, but the system could not complete all Property Care payment actions. "
                    "Please contact support."
                )

                return redirect(
                    'property_manager_app:property_care_payment_failed',
                    transaction_id=locked_transaction.id
                )

            locked_transaction.gateway_session_id = paypal_order_id
            locked_transaction.gateway_transaction_id = paypal_capture_id
            locked_transaction.status = 'paid'
            locked_transaction.paid_at = timezone.now()
            locked_transaction.failure_reason = ''
            locked_transaction.save(update_fields=[
                'gateway_session_id',
                'gateway_transaction_id',
                'status',
                'paid_at',
                'failure_reason',
                'updated_at',
            ])

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"Database payment completion failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(
            request,
            "Payment was verified, but the system could not complete the payment record. "
            "Please contact support."
        )

        return redirect(
            'property_manager_app:property_care_payment_failed',
            transaction_id=payment_transaction.id
        )

    request.session.pop('property_care_payment_transaction_id', None)
    request.session.pop('property_care_checkout_selected_ids', None)
    request.session.pop('property_care_checkout_total', None)
    request.session.pop('property_care_checkout_profile_type', None)
    request.session.pop('property_care_use_wallet', None)
    request.session.pop('property_care_wallet_deduction', None)

    messages.success(request, "PayPal payment completed successfully.")

    return redirect(
        'property_manager_app:property_care_payment_success',
        transaction_id=payment_transaction.id
    )

def create_property_care_paypal_order(payment_transaction, request):
    """
    Creates a PayPal order for Property Care owner payable item checkout.
    """

    access_token = get_paypal_access_token()
    base_url = get_paypal_base_url()

    create_order_url = f'{base_url}/v2/checkout/orders'

    return_url = request.build_absolute_uri(
        reverse(
            'property_manager_app:property_care_paypal_payment_success',
            kwargs={'transaction_id': payment_transaction.id}
        )
    )

    cancel_url = request.build_absolute_uri(
        reverse(
            'property_manager_app:property_care_payment_cancelled',
            kwargs={'transaction_id': payment_transaction.id}
        )
    )

    amount_value = payment_transaction.gateway_amount_usd.quantize(Decimal('0.01'))

    payload = {
        'intent': 'CAPTURE',
        'purchase_units': [
            {
                'reference_id': str(payment_transaction.id),
                'description': f'Property Care Payment - Transaction #{payment_transaction.id}',
                'custom_id': str(payment_transaction.id),
                'amount': {
                    'currency_code': 'USD',
                    'value': str(amount_value),
                },
            }
        ],
        'application_context': {
            'brand_name': 'Ensolanka Home in Sri Lanka',
            'landing_page': 'BILLING',
            'user_action': 'PAY_NOW',
            'return_url': return_url,
            'cancel_url': cancel_url,
        },
    }

    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {access_token}',
        'PayPal-Request-Id': f'property-care-payable-{payment_transaction.id}',
    }

    response = requests.post(
        create_order_url,
        json=payload,
        headers=headers,
        timeout=30,
    )

    if response.status_code not in [200, 201]:
        raise Exception(f"PayPal order creation failed: {response.text}")

    return response.json()

#=================================================================

@login_required
def managed_property_document_upload(request, pk):
    """
    Allows a Property Care owner to upload supporting documents
    for one of their managed properties.
    """

    owner = getattr(request.user, 'property_manager_client_profile', None)

    if not owner:
        messages.error(
            request,
            "Property Care owner profile not found."
        )
        return redirect('property_manager_app:home_property_manager')

    managed_property = get_object_or_404(
        ManagedProperty,
        pk=pk,
        owner=owner
    )

    if request.method == 'POST':
        form = ManagedPropertyDocumentForm(
            request.POST,
            request.FILES
        )

        if form.is_valid():
            document = form.save(commit=False)

            document.property = managed_property
            document.uploaded_by_owner = True
            document.uploaded_by_staff = None
            document.save()

            messages.success(
                request,
                "Supporting property document uploaded successfully."
            )

            return redirect(
                'property_manager_app:managed_property_detail',
                pk=managed_property.pk
            )

        messages.error(
            request,
            "Please correct the document upload errors below."
        )

    else:
        form = ManagedPropertyDocumentForm()

    context = {
        'property': managed_property,
        'form': form,
    }

    return render(
        request,
        'property_manager_app/managed_property_document_upload.html',
        context
    )

@login_required
@require_POST
def managed_property_document_delete(request, pk, document_id):
    """
    Deletes a supporting document uploaded for a managed property,
    only if the property belongs to the logged-in Property Care owner.
    """

    owner = getattr(request.user, 'property_manager_client_profile', None)

    if not owner:
        messages.error(
            request,
            "Property Care owner profile not found."
        )
        return redirect('property_manager_app:home_property_manager')

    managed_property = get_object_or_404(
        ManagedProperty,
        pk=pk,
        owner=owner
    )

    document = get_object_or_404(
        ManagedPropertyDocument,
        pk=document_id,
        property=managed_property
    )

    document.delete()

    messages.success(
        request,
        "Supporting property document deleted successfully."
    )

    return redirect(
        'property_manager_app:managed_property_detail',
        pk=managed_property.pk
    )

@login_required
@require_POST
@transaction.atomic
def activate_rental_owner_from_property_manager(request):
    """
    Creates a Rental Owner profile for the currently logged-in
    Property Care client, using available profile details,
    then redirects to the Rental Owner Dashboard.

    If the Rental Owner profile already exists,
    simply redirect to the dashboard.
    """

    user = request.user

    # ---------------------------------------------------------
    # 1. Confirm this user has a Property Care client profile
    # ---------------------------------------------------------
    property_manager_profile = getattr(
        user,
        'property_manager_client_profile',
        None
    )

    if property_manager_profile is None:
        messages.warning(
            request,
            "Please activate Property Care access first before creating a Rental Owner profile."
        )
        return redirect(
            'property_manager_app:property_manager_dashboard'
        )

    # ---------------------------------------------------------
    # 2. If rental owner profile already exists, just redirect
    # ---------------------------------------------------------
    existing_rental_owner = getattr(
        user,
        'rental_owner_profile',
        None
    )

    if existing_rental_owner is not None:
        messages.info(
            request,
            "Your Rental Owner access is already active."
        )
        return redirect(
            'rental_app:rental_owner_dashboard'
        )

    # ---------------------------------------------------------
    # 3. Decide basic owner type from Property Care client type
    # ---------------------------------------------------------
    if property_manager_profile.client_type == 'company':
        owner_type = 'company'
    else:
        owner_type = 'individual'

    # ---------------------------------------------------------
    # 4. Create Rental Owner profile using available details
    # ---------------------------------------------------------
    RentalOwner.objects.create(
        user=user,
        owner_type=owner_type,
        full_name=property_manager_profile.full_name,
        email=property_manager_profile.email or user.email,
        whatsapp_number=property_manager_profile.whatsapp_number,
        phone_number=property_manager_profile.phone_number,
        city='',
        address='',
        business_name='',
        is_identity_verified=property_manager_profile.is_identity_verified,
        is_approved=True,
        is_featured=False,
        can_submit_listings=True,
    )

    # ---------------------------------------------------------
    # 5. Success and redirect
    # ---------------------------------------------------------
    messages.success(
        request,
        "Rental Owner access has been activated successfully. You can now manage rental listings."
    )

    return redirect(
        'rental_app:rental_owner_dashboard'
    )

@login_required
@require_POST
@transaction.atomic
def activate_affiliate_from_property_manager(request):
    """
    Creates an Affiliate Profile for the currently logged-in
    Property Care client using available existing profile details,
    then redirects to the Affiliate Dashboard.

    If an Affiliate Profile already exists,
    simply redirect to the dashboard.
    """

    user = request.user

    # ---------------------------------------------------------
    # 1. Confirm this user has a Property Care client profile
    # ---------------------------------------------------------
    property_manager_profile = getattr(
        user,
        'property_manager_client_profile',
        None
    )

    if property_manager_profile is None:
        messages.warning(
            request,
            "Please activate Property Care access first before creating an Affiliate profile."
        )
        return redirect(
            'property_manager_app:property_manager_dashboard'
        )

    # ---------------------------------------------------------
    # 2. If Affiliate profile already exists, just redirect
    # ---------------------------------------------------------
    existing_affiliate_profile = getattr(
        user,
        'affiliate_profile',
        None
    )

    if existing_affiliate_profile is not None:
        messages.info(
            request,
            "Your Affiliate access is already active."
        )
        return redirect(
            'affiliates_app:affiliate_dashboard'
        )

    # ---------------------------------------------------------
    # 3. Create Affiliate Profile
    #    affiliate_code will auto-generate in model.save()
    # ---------------------------------------------------------
    AffiliateProfile.objects.create(
        user=user,
        full_name=property_manager_profile.full_name,
        phone=property_manager_profile.phone_number,
        whatsapp=property_manager_profile.whatsapp_number,
        country=property_manager_profile.current_country,
        status='approved',
        is_active=True,
    )

    # ---------------------------------------------------------
    # 4. Success and redirect
    # ---------------------------------------------------------
    messages.success(
        request,
        "Affiliate access has been activated successfully. You can now open your Affiliate Dashboard and start using your Global Ambassador resources."
    )

    return redirect(
        'affiliates_app:affiliate_dashboard'
    )

def property_care_services_page(request):
    """
    Public Property Care services information page.
    """
    return render(
        request,
        'property_manager_app/property_care_services_page.html'
    )

def property_care_calculator_page(request):
    """
    Public calculator guidance page.

    The actual property-specific monthly estimate is calculated
    after a property is registered and services are selected.
    """

    profile = None

    if request.user.is_authenticated:
        profile = getattr(
            request.user,
            'property_manager_client_profile',
            None
        )

    return render(
        request,
        'property_manager_app/property_care_calculator_page.html',
        {
            'profile': profile,
        }
    )

@login_required
def property_care_estimate_center(request):
    """
    Owner-facing central estimate page.

    This lists the owner’s registered properties and gives
    access to each property-specific estimate page.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please activate Property Care access before viewing estimates."
        )
        return redirect(
            'property_manager_app:property_manager_signup'
        )

    properties = ManagedProperty.objects.filter(
        owner=profile
    ).order_by('-created_at')

    return render(
        request,
        'property_manager_app/property_care_estimate_center.html',
        {
            'profile': profile,
            'properties': properties,
            'total_properties': properties.count(),
        }
    )

@login_required
def property_care_report_center(request):
    """
    Owner-facing report center shell.

    This page will later be connected to:
    - Maintenance visits
    - Inspection reports
    - Work orders
    - Owner communication records

    For now, it provides a real menu destination and a clean
    overview page for the upcoming report center.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if profile is None:
        messages.warning(
            request,
            "Please activate Property Care access before viewing reports."
        )
        return redirect(
            'property_manager_app:property_manager_signup'
        )

    properties = ManagedProperty.objects.filter(
        owner=profile
    ).order_by('-created_at')

    return render(
        request,
        'property_manager_app/property_care_report_center.html',
        {
            'profile': profile,
            'properties': properties,
            'total_properties': properties.count(),
        }
    )

# top up

@login_required
def property_care_wallet_topup(request):
    """
    Property Care Owner direct wallet top-up page.

    No UserPayableItem is created here.
    User enters amount and selects Stripe or PayPal.
    """

    profile = getattr(
        request.user,
        'property_manager_client_profile',
        None
    )

    if not profile:
        messages.error(
            request,
            "Property Care wallet top-up is available only for registered Property Care Owner accounts."
        )
        return redirect('property_manager_app:home_property_care')

    profile_type = 'property_care_owner'
    wallet = get_or_create_wallet_account(request.user, profile_type)

    if request.method == 'POST':
        form = WalletTopUpForm(request.POST)

        if form.is_valid():
            amount_usd = form.cleaned_data['amount_usd']
            payment_method = form.cleaned_data['payment_method']

            request.session['property_care_wallet_topup_profile_type'] = profile_type
            request.session['property_care_wallet_topup_amount_usd'] = str(amount_usd)
            request.session['property_care_wallet_topup_return_url'] = 'property_manager_app:property_care_wallet_topup'

            if payment_method == 'stripe':
                return redirect(
                    'property_manager_app:start_property_care_wallet_topup_stripe_checkout'
                )

            if payment_method == 'paypal':
                return redirect(
                    'property_manager_app:start_property_care_wallet_topup_paypal_checkout'
                )

            messages.error(request, "Please select a valid payment method.")
    else:
        form = WalletTopUpForm(initial={
            'payment_method': 'stripe',
        })

    return render(request, 'property_manager_app/property_care_wallet_topup.html', {
        'form': form,
        'wallet': wallet,
        'profile': profile,
        'profile_type': profile_type,
        'page_title': 'Property Care Wallet Top-up',
        'account_label': 'Property Care Wallet',
    })

@login_required
def start_property_care_wallet_topup_stripe_checkout(request):
    profile_type = request.session.get('property_care_wallet_topup_profile_type')
    amount_usd = request.session.get('property_care_wallet_topup_amount_usd')
    return_url = request.session.get(
        'property_care_wallet_topup_return_url',
        'property_manager_app:property_care_wallet_topup'
    )

    if not profile_type or not amount_usd:
        messages.error(request, "No wallet top-up checkout details found.")
        return redirect(return_url)

    if profile_type != 'property_care_owner':
        messages.error(request, "Invalid wallet top-up profile type.")
        return redirect(return_url)

    if not user_can_topup_wallet(request.user, profile_type):
        messages.error(
            request,
            "Wallet top-up is not available for this Property Care account."
        )
        return redirect(return_url)

    try:
        amount_usd = Decimal(str(amount_usd)).quantize(Decimal('0.01'))
    except (InvalidOperation, TypeError, ValueError):
        messages.error(request, "Invalid wallet top-up amount.")
        return redirect(return_url)

    if amount_usd <= Decimal('0.00'):
        messages.error(request, "Wallet top-up amount must be greater than zero.")
        return redirect(return_url)

    if not settings.STRIPE_SECRET_KEY:
        messages.error(request, "Stripe payment is not configured.")
        return redirect(return_url)

    stripe.api_key = settings.STRIPE_SECRET_KEY

    try:
        payment_transaction = PaymentTransaction.objects.create(
            user=request.user,
            profile_type='property_care_owner',
            payment_purpose='wallet_topup',
            payment_method='stripe',
            total_amount_usd=amount_usd,
            wallet_deduction_usd=Decimal('0.00'),
            gateway_amount_usd=amount_usd,
            status='pending',
        )

        success_url = request.build_absolute_uri(
            reverse(
                'property_manager_app:property_care_wallet_topup_stripe_success',
                kwargs={'transaction_id': payment_transaction.id}
            )
        ) + '?session_id={CHECKOUT_SESSION_ID}'

        cancel_url = request.build_absolute_uri(
            reverse(
                'property_manager_app:property_care_wallet_topup_cancelled',
                kwargs={'transaction_id': payment_transaction.id}
            )
        )

        stripe_amount_cents = int(
            (amount_usd * Decimal('100')).quantize(Decimal('1'))
        )

        checkout_session = stripe.checkout.Session.create(
            payment_method_types=['card'],
            mode='payment',
            line_items=[
                {
                    'price_data': {
                        'currency': 'usd',
                        'product_data': {
                            'name': 'Property Care Wallet Top-up',
                            'description': (
                                f'Wallet top-up for Property Care Owner account. '
                                f'Transaction #{payment_transaction.id}'
                            ),
                        },
                        'unit_amount': stripe_amount_cents,
                    },
                    'quantity': 1,
                }
            ],
            success_url=success_url,
            cancel_url=cancel_url,
            customer_email=request.user.email or None,
            client_reference_id=str(payment_transaction.id),
            metadata={
                'payment_transaction_id': str(payment_transaction.id),
                'user_id': str(request.user.id),
                'profile_type': 'property_care_owner',
                'payment_purpose': 'wallet_topup',
                'amount_usd': str(amount_usd),
            },
        )

        if not checkout_session.url:
            raise Exception("Stripe checkout URL was not returned.")

        payment_transaction.gateway_session_id = checkout_session.id
        payment_transaction.status = 'processing'
        payment_transaction.save(update_fields=[
            'gateway_session_id',
            'status',
            'updated_at',
        ])

        request.session['property_care_wallet_topup_transaction_id'] = payment_transaction.id

        return redirect(checkout_session.url)

    except Exception as e:
        messages.error(
            request,
            f"Stripe wallet top-up checkout could not be started. {str(e)}"
        )
        return redirect(return_url)

@login_required
def property_care_wallet_topup_stripe_success(request, transaction_id):
    session_id = request.GET.get('session_id')

    if not session_id:
        messages.error(request, "Stripe wallet top-up session not found.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=transaction_id
        )

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        payment_purpose='wallet_topup',
    )

    if payment_transaction.status == 'paid':
        messages.success(request, "This wallet top-up has already been completed.")
        return redirect('property_manager_app:property_care_wallet_topup')

    if payment_transaction.payment_method != 'stripe':
        messages.error(request, "Invalid payment method for Stripe wallet top-up.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    if not settings.STRIPE_SECRET_KEY:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = "Stripe payment is not configured."
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Stripe payment is not configured.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    stripe.api_key = settings.STRIPE_SECRET_KEY

    try:
        stripe_session = stripe.checkout.Session.retrieve(session_id)

        metadata_transaction_id = None
        stripe_metadata = getattr(stripe_session, 'metadata', None)

        if stripe_metadata:
            try:
                stripe_metadata = dict(stripe_metadata)
                metadata_transaction_id = stripe_metadata.get('payment_transaction_id')
            except Exception:
                metadata_transaction_id = getattr(
                    stripe_metadata,
                    'payment_transaction_id',
                    None
                )

        if str(metadata_transaction_id) != str(payment_transaction.id):
            raise Exception("Stripe metadata transaction ID mismatch.")

        stripe_payment_status = getattr(stripe_session, 'payment_status', None)

        if stripe_payment_status != 'paid':
            raise Exception(
                f"Stripe payment status was not paid: {stripe_payment_status}"
            )

        expected_amount_cents = int(
            (payment_transaction.gateway_amount_usd * Decimal('100')).quantize(
                Decimal('1')
            )
        )

        stripe_amount_total = getattr(stripe_session, 'amount_total', None)

        if stripe_amount_total is not None and int(stripe_amount_total) != expected_amount_cents:
            raise Exception(
                f"Stripe amount mismatch. Expected {expected_amount_cents}, "
                f"received {stripe_amount_total}."
            )

        gateway_transaction_id = (
            getattr(stripe_session, 'payment_intent', '') or stripe_session.id
        )

        complete_wallet_topup_transaction(
            payment_transaction=payment_transaction,
            gateway_session_id=stripe_session.id,
            gateway_transaction_id=gateway_transaction_id,
        )

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"Stripe wallet top-up failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "Stripe wallet top-up could not be completed.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    request.session.pop('property_care_wallet_topup_profile_type', None)
    request.session.pop('property_care_wallet_topup_amount_usd', None)
    request.session.pop('property_care_wallet_topup_return_url', None)
    request.session.pop('property_care_wallet_topup_transaction_id', None)

    messages.success(
        request,
        "Your Property Care wallet has been topped up successfully."
    )

    return redirect('property_manager_app:property_care_wallet_topup')

def create_property_care_wallet_topup_paypal_order(payment_transaction, request):
    """
    Creates a PayPal order for Property Care wallet top-up.
    """

    access_token = get_paypal_access_token()
    base_url = get_paypal_base_url()

    create_order_url = f'{base_url}/v2/checkout/orders'

    return_url = request.build_absolute_uri(
        reverse(
            'property_manager_app:property_care_wallet_topup_paypal_success',
            kwargs={'transaction_id': payment_transaction.id}
        )
    )

    cancel_url = request.build_absolute_uri(
        reverse(
            'property_manager_app:property_care_wallet_topup_cancelled',
            kwargs={'transaction_id': payment_transaction.id}
        )
    )

    amount_value = payment_transaction.gateway_amount_usd.quantize(Decimal('0.01'))

    payload = {
        'intent': 'CAPTURE',
        'purchase_units': [
            {
                'reference_id': str(payment_transaction.id),
                'description': (
                    f'Property Care Wallet Top-up - Transaction '
                    f'#{payment_transaction.id}'
                ),
                'custom_id': str(payment_transaction.id),
                'amount': {
                    'currency_code': 'USD',
                    'value': str(amount_value),
                },
            }
        ],
        'application_context': {
            'brand_name': 'Ensolanka Home in Sri Lanka',
            'landing_page': 'BILLING',
            'user_action': 'PAY_NOW',
            'return_url': return_url,
            'cancel_url': cancel_url,
        },
    }

    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {access_token}',
        'PayPal-Request-Id': f'property-care-wallet-topup-{payment_transaction.id}',
    }

    response = requests.post(
        create_order_url,
        json=payload,
        headers=headers,
        timeout=30,
    )

    if response.status_code not in [200, 201]:
        raise Exception(f"PayPal wallet top-up order creation failed: {response.text}")

    return response.json()

@login_required
def start_property_care_wallet_topup_paypal_checkout(request):
    profile_type = request.session.get('property_care_wallet_topup_profile_type')
    amount_usd = request.session.get('property_care_wallet_topup_amount_usd')
    return_url = request.session.get(
        'property_care_wallet_topup_return_url',
        'property_manager_app:property_care_wallet_topup'
    )

    if not profile_type or not amount_usd:
        messages.error(request, "No wallet top-up checkout details found.")
        return redirect(return_url)

    if profile_type != 'property_care_owner':
        messages.error(request, "Invalid wallet top-up profile type.")
        return redirect(return_url)

    if not user_can_topup_wallet(request.user, profile_type):
        messages.error(
            request,
            "Wallet top-up is not available for this Property Care account."
        )
        return redirect(return_url)

    try:
        amount_usd = Decimal(str(amount_usd)).quantize(Decimal('0.01'))
    except (InvalidOperation, TypeError, ValueError):
        messages.error(request, "Invalid wallet top-up amount.")
        return redirect(return_url)

    if amount_usd <= Decimal('0.00'):
        messages.error(request, "Wallet top-up amount must be greater than zero.")
        return redirect(return_url)

    try:
        payment_transaction = PaymentTransaction.objects.create(
            user=request.user,
            profile_type='property_care_owner',
            payment_purpose='wallet_topup',
            payment_method='paypal',
            total_amount_usd=amount_usd,
            wallet_deduction_usd=Decimal('0.00'),
            gateway_amount_usd=amount_usd,
            status='pending',
        )

        order_data = create_property_care_wallet_topup_paypal_order(
            payment_transaction=payment_transaction,
            request=request,
        )

        approval_url = get_paypal_approval_url(order_data)

        if not approval_url:
            raise Exception("PayPal approval URL not found.")

        payment_transaction.gateway_session_id = order_data.get('id', '')
        payment_transaction.status = 'processing'
        payment_transaction.save(update_fields=[
            'gateway_session_id',
            'status',
            'updated_at',
        ])

        request.session['property_care_wallet_topup_transaction_id'] = payment_transaction.id

        return redirect(approval_url)

    except Exception as e:
        messages.error(
            request,
            f"PayPal wallet top-up checkout could not be started. {str(e)}"
        )
        return redirect(return_url)
    
@login_required
def property_care_wallet_topup_paypal_success(request, transaction_id):
    paypal_order_id = request.GET.get('token')

    if not paypal_order_id:
        messages.error(request, "PayPal wallet top-up order not found.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=transaction_id
        )

    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        payment_purpose='wallet_topup',
    )

    if payment_transaction.status == 'paid':
        messages.success(request, "This wallet top-up has already been completed.")
        return redirect('property_manager_app:property_care_wallet_topup')

    if payment_transaction.payment_method != 'paypal':
        messages.error(request, "Invalid payment method for PayPal wallet top-up.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    if (
        payment_transaction.gateway_session_id and
        payment_transaction.gateway_session_id != paypal_order_id
    ):
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = "PayPal wallet top-up order ID mismatch."
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "PayPal wallet top-up verification failed.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    try:
        capture_data = capture_paypal_order(paypal_order_id)

        paypal_status = capture_data.get('status')

        if paypal_status != 'COMPLETED':
            raise Exception(
                f"PayPal payment status was not completed: {paypal_status}"
            )

        purchase_units = capture_data.get('purchase_units', [])
        first_purchase_unit = purchase_units[0] if purchase_units else {}
        payments = first_purchase_unit.get('payments', {})
        captures = payments.get('captures', [])
        first_capture = captures[0] if captures else {}

        captured_amount = first_capture.get('amount', {})
        captured_value = Decimal(
            str(captured_amount.get('value', '0.00'))
        ).quantize(Decimal('0.01'))
        captured_currency = captured_amount.get('currency_code')

        expected_amount = payment_transaction.gateway_amount_usd.quantize(
            Decimal('0.01')
        )

        if captured_currency != 'USD' or captured_value != expected_amount:
            raise Exception(
                f"PayPal amount mismatch. Expected USD {expected_amount}, "
                f"received {captured_currency} {captured_value}."
            )

        paypal_capture_id = first_capture.get('id', paypal_order_id)

        complete_wallet_topup_transaction(
            payment_transaction=payment_transaction,
            gateway_session_id=paypal_order_id,
            gateway_transaction_id=paypal_capture_id,
        )

    except Exception as e:
        payment_transaction.status = 'failed'
        payment_transaction.failure_reason = f"PayPal wallet top-up failed: {str(e)}"
        payment_transaction.failed_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'failure_reason',
            'failed_at',
            'updated_at',
        ])

        messages.error(request, "PayPal wallet top-up could not be completed.")
        return redirect(
            'property_manager_app:property_care_wallet_topup_failed',
            transaction_id=payment_transaction.id
        )

    request.session.pop('property_care_wallet_topup_profile_type', None)
    request.session.pop('property_care_wallet_topup_amount_usd', None)
    request.session.pop('property_care_wallet_topup_return_url', None)
    request.session.pop('property_care_wallet_topup_transaction_id', None)

    messages.success(
        request,
        "Your Property Care wallet has been topped up successfully."
    )

    return redirect('property_manager_app:property_care_wallet_topup')

@login_required
def property_care_wallet_topup_cancelled(request, transaction_id):
    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        payment_purpose='wallet_topup',
    )

    if payment_transaction.status in ['pending', 'processing']:
        payment_transaction.status = 'cancelled'
        payment_transaction.cancelled_at = timezone.now()
        payment_transaction.save(update_fields=[
            'status',
            'cancelled_at',
            'updated_at',
        ])

    request.session.pop('property_care_wallet_topup_profile_type', None)
    request.session.pop('property_care_wallet_topup_amount_usd', None)
    request.session.pop('property_care_wallet_topup_return_url', None)
    request.session.pop('property_care_wallet_topup_transaction_id', None)

    messages.warning(
        request,
        "Wallet top-up was cancelled. No amount was added to your Property Care wallet."
    )

    return redirect('property_manager_app:property_care_wallet_topup')


@login_required
def property_care_wallet_topup_failed(request, transaction_id):
    payment_transaction = get_object_or_404(
        PaymentTransaction,
        id=transaction_id,
        user=request.user,
        profile_type='property_care_owner',
        payment_purpose='wallet_topup',
    )

    messages.error(
        request,
        "Wallet top-up failed. Please try again or contact support if money was deducted."
    )

    return redirect('property_manager_app:property_care_wallet_topup')



def property_care_information_center(request):
    selected_language = request.GET.get('lang', 'en')

    allowed_languages = ['en', 'si', 'ta']

    if selected_language not in allowed_languages:
        selected_language = 'en'

    guidance_resources = PropertyCareGuidanceResource.objects.filter(
        is_active=True
    ).order_by('language')

    available_languages = list(
        guidance_resources.values_list('language', flat=True)
    )

    # If requested language is not available, fallback safely.
    if selected_language not in available_languages and available_languages:
        if 'en' in available_languages:
            selected_language = 'en'
        else:
            selected_language = available_languages[0]

    selected_resource = guidance_resources.filter(
        language=selected_language
    ).first()

    context = {
        'guidance_resources': guidance_resources,
        'selected_resource': selected_resource,
        'selected_language': selected_language,
    }

    return render(
        request,
        'property_manager_app/property_care_information_center.html',
        context
    )