from decimal import Decimal
import json
from datetime import timedelta
import uuid
from django.urls import reverse
from django.utils import timezone
from django.contrib import messages
from django.db.models import Avg, Count, Q, Exists, OuterRef, Value, Sum
from django.contrib.auth import get_user_model
from django.contrib.admin.views.decorators import staff_member_required
from django.http import HttpResponse, JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render


from .forms import (
    BookingRequestForm,
    CreatorMediaForm,
    MessageReplyForm,
    NewMessageThreadForm,
    PaymentSelectionForm,
    ProfileSearchForm,
    WalletDepositForm,
    WithdrawalRequestForm,
)
from .models import (
    ConnectionRequest,
    EntertainmentBrand,
    MediaBookmark,
    MediaComment,
    MediaReaction,
    Message,
    MessageThread,
    PaidContent,
    CreatorMedia,
    PaymentGateway,
    ProfileFollow,
    MediaLike,
    BookingRequest,
    PaymentTransaction,
    SubscriptionPlan,
    UserSubscription,
    InvestmentPlan,
    Investment,
    UserWallet,
    WalletTransaction,
    WithdrawalRequest,
    ReferralBonus,
    ChatMessage,
    create_notification,
)
from .payment_providers import CryptoProvider, PaymentProviderError, PaymentProviderFactory
from users.models import Profile
from django.core.paginator import Paginator
from django.views.decorators.csrf import csrf_exempt

from django.core.mail import send_mail
from django.conf import settings

def home(request):
    User = get_user_model()
    active_users = User.objects.filter(is_active=True).count()
    celebrity_count = User.objects.filter(is_celebrity=True, is_active=True).count()
    plan_count = SubscriptionPlan.objects.filter(is_active=True).count()
    content_count = PaidContent.objects.filter(is_published=True).count()
    investment_plans = InvestmentPlan.objects.all()
    trusted_brands = EntertainmentBrand.objects.filter(
        is_active=True,
        status=EntertainmentBrand.Status.TRUSTED,
        is_verified=True
    ).order_by("display_order", "name")[:10]  # limit if needed7
    
    featured_brands = EntertainmentBrand.objects.filter(
    is_active=True,
    status=EntertainmentBrand.Status.TRUSTED,
    is_verified=True,
    is_featured=True
).order_by("display_order")
    
    payment_gateways = PaymentGateway.objects.filter(
        is_active=True,
        status=PaymentGateway.Status.ACTIVE
    ).order_by("display_order")
    
    
    return render(
        request,
        "home.html",
        {
            "stats": {
                "active_users": active_users,
                "celebrities": celebrity_count,
                "plans": plan_count,
                "premium_posts": content_count,
            },
            "feature_rows": [
                {
                    "title": "Discover verified fan-worthy celebrities",
                    "description": "Browse curated profiles, connect with creators, and join exclusive fan communities.",
                    "icon": "fa-user-check",
                },
                {
                    "title": "Private chat and premium content",
                    "description": "Send direct messages, unlock premium posts, and support celebrity creators with ease.",
                    "icon": "fa-comments",
                },
                {
                    "title": "Flexible subscription payments",
                    "description": "Choose from Stripe, Paystack or Flutterwave for secure recurring membership plans.",
                    "icon": "fa-credit-card",
                },
            ],
            "trusted_brands": trusted_brands,
            "featured_brands": featured_brands,
            "payment_gateways": payment_gateways,
            "investment_plans": investment_plans,
        },
    )


@login_required
def discovery(request):
    form = ProfileSearchForm(request.GET or None)
    profiles = Profile.objects.filter(
        visibility__in=[Profile.Visibility.PUBLIC, Profile.Visibility.DISCOVERABLE],
        user__is_active=True,
    ).select_related("user").order_by("-updated_at")

    profiles = profiles.annotate(
        active_media_count=Count("media_items", filter=Q(media_items__is_active=True)),
        follower_count_stat=Count("followers"),
        is_followed=Exists(
            ProfileFollow.objects.filter(
                follower=request.user,
                profile=OuterRef('pk')
            )
        ) if request.user.is_authenticated else Value(False)
    ).order_by("-active_media_count", "-follower_count_stat")
    
    
    if form.is_valid():
        keyword = form.cleaned_data.get("keyword")
        city = form.cleaned_data.get("city")
        country = form.cleaned_data.get("country")
        interest = form.cleaned_data.get("interest")
        verified_only = form.cleaned_data.get("verified_only")
        category = form.cleaned_data.get("category")

        if keyword:
            profiles = profiles.filter(
                Q(display_name__icontains=keyword)
                | Q(user__username__icontains=keyword)
                | Q(headline__icontains=keyword)
                | Q(bio__icontains=keyword)
            )
        if city:
            profiles = profiles.filter(city__icontains=city)
        if country:
            profiles = profiles.filter(country__icontains=country)
        if interest:
            profiles = profiles.filter(interests__icontains=interest)
        if verified_only:
            profiles = profiles.filter(user__is_verified=True)
        if category:
            profiles = profiles.filter(media_items__category=category)
        profiles = profiles.distinct()

    return render(request, "core/discover_list.html", {"profiles": profiles, "form": form})

@login_required
def marketplace(request):
    """Marketplace view with better annotations"""
    form = ProfileSearchForm(request.GET or None)
    profiles = Profile.objects.filter(
        visibility__in=[Profile.Visibility.PUBLIC, Profile.Visibility.DISCOVERABLE],
        user__is_active=True,
        user__is_celebrity=True,
    ).select_related("user")

    # ... existing filtering logic ...

    profiles = profiles.annotate(
        active_media_count=Count("media_items", filter=Q(media_items__is_active=True)),
        follower_count_stat=Count("followers"),
        is_followed=Exists(
            ProfileFollow.objects.filter(
                follower=request.user,
                profile_id=OuterRef('pk')
            )
        ) if request.user.is_authenticated else Value(False)
    ).order_by("-active_media_count", "-follower_count_stat")

    paginator = Paginator(profiles, 12)
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)

    # Attach preview media
    for profile in page_obj.object_list:
        profile.preview_media = profile.media_items.filter(is_active=True)[:3]

    return render(request, "core/marketplace.html", {"page_obj": page_obj, "form": form})


@login_required
def discovery_detail(request, username):
    profile = get_object_or_404(Profile, user__username=username)

    if profile.visibility == Profile.Visibility.PRIVATE and profile.user != request.user:
        messages.warning(request, "This profile is private.")
        return redirect("core:discover")

    can_send_request = request.user != profile.user
    # Fans can only message celebrities (updated rule)
    can_send_message = (
        request.user != profile.user 
        and profile.can_receive_messages
        and can_message(request.user, profile.user)
    )

    is_following = (
        ProfileFollow.objects.filter(follower=request.user, profile=profile).exists()
        if request.user.is_authenticated and request.user != profile.user
        else False
    )

    request_status = None

    if request.method == "POST" and can_send_request:
        request_obj, created = ConnectionRequest.objects.get_or_create(
            from_user=request.user,
            to_user=profile.user,
            defaults={"message": "Connection request from discovery."},
        )

        if created:
            create_notification(
                user=profile.user,
                actor=request.user,
                verb=f"{request.user.username} sent you a connection request.",
                target_url=request.build_absolute_uri(request.path),
            )
            messages.success(request, "Connection request sent.")
        else:
            messages.info(request, f"Existing request status: {request_obj.status}")

        request_status = request_obj.status

    active_media_items = profile.media_items.filter(is_active=True).select_related()

    return render(request, "core/discover_detail.html", {
        "profile": profile,
        "can_send_request": can_send_request,
        "can_send_message": can_send_message,
        "request_status": request_status,
        "is_following": is_following,
        "active_media_items": active_media_items,
    })
    
    
# AJAX LIKE ENDPOINT (NEW)
@require_POST
@login_required
def like_media(request, username, media_id):
    """Legacy redirect-style like (used in URL)"""
    profile = get_object_or_404(Profile, user__username=username)
    media_item = get_object_or_404(CreatorMedia, pk=media_id, creator=profile)
    MediaLike.objects.get_or_create(user=request.user, media_item=media_item)
    create_notification(
        user=profile.user,
        actor=request.user,
        verb=f"{request.user.username} liked your media.",
        target_url=request.build_absolute_uri(request.path),
    )
    messages.success(request, "Media liked.")
    return redirect("core:discover_detail", username=username)

@login_required
def follow_profile(request, username):
    profile = get_object_or_404(Profile, user__username=username)
    if request.user == profile.user:
        messages.warning(request, "You cannot follow your own profile.")
        return redirect("core:discover_detail", username=username)

    ProfileFollow.objects.get_or_create(follower=request.user, profile=profile)
    create_notification(
        user=profile.user,
        actor=request.user,
        verb=f"{request.user.username} started following your profile.",
        target_url=request.build_absolute_uri(request.path),
    )
    messages.success(request, f"You are now following {profile.display_name or profile.user.username}.")
    return redirect("core:discover_detail", username=username)


@login_required
def book_profile(request, username):
    profile = get_object_or_404(Profile, user__username=username)

    if request.user == profile.user:
        messages.warning(request, "You cannot request a booking for your own profile.")
        return redirect("core:discover_detail", username=username)

    wallet, _ = UserWallet.objects.get_or_create(user=request.user)

    booking_fee = getattr(profile, "booking_fee", Decimal("0.00")) or Decimal("0.00")


    # ===== Only redirect if balance is insufficient =====
    if booking_fee > 0 and wallet.balance < booking_fee:
        messages.warning(
            request,
            f"You need at least ${booking_fee:.2f} in your wallet to book this celebrity. "
            "Please fund your wallet to continue."
        )
        # Pass next as a relative path to avoid absolute URL issues
        next_path = reverse("core:book_profile", args=[username])
        deposit_url = reverse("core:wallet_deposit")
        return redirect(f"{deposit_url}?next={next_path}&amount={booking_fee}")

    # ===== Normal form handling =====
    if request.method == "POST":
        form = BookingRequestForm(request.POST)
        if form.is_valid():
            if booking_fee > 0:
                try:
                    wallet.withdraw(booking_fee)
                except ValueError:
                    messages.error(request, "Insufficient balance. Please fund your wallet.")
                    return redirect("core:wallet_deposit")

            BookingRequest.objects.create(
                requester=request.user,
                profile=profile,
                preferred_date=form.cleaned_data.get("preferred_date"),
                message=form.cleaned_data.get("message"),
            )

            create_notification(
                user=profile.user,
                actor=request.user,
                verb=f"{request.user.username} requested a meeting for your profile.",
                target_url=request.build_absolute_uri(request.path),
            )

            messages.success(request, "Booking request submitted successfully!")
            return redirect("core:discover_detail", username=username)
    else:
        form = BookingRequestForm()

    return render(request, "core/book_profile.html", {
        "profile": profile,
        "form": form,
        "booking_fee": booking_fee,
        "wallet_balance": wallet.balance,
        "has_enough_balance": wallet.balance >= booking_fee,
    })
    
    
@login_required
def upload_profile_media(request, username):
    profile = get_object_or_404(Profile, user__username=username)
    if request.user != profile.user:
        messages.warning(request, "Only the profile owner can upload media.")
        return redirect("core:discover_detail", username=username)

    if request.method == "POST":
        form = CreatorMediaForm(request.POST, request.FILES)
        if form.is_valid():
            media = form.save(commit=False)
            media.creator = profile
            media.save()
            messages.success(request, "Media uploaded successfully.")
            return redirect("core:discover_detail", username=username)
    else:
        form = CreatorMediaForm()

    return render(request, "core/upload_media.html", {"profile": profile, "form": form})


def can_message(user, recipient):
    """Fans can only message celebrities. Celebrities can message anyone."""
    if user.is_celebrity or recipient.is_celebrity:
        return True
    return False


def get_or_create_thread(user, recipient, title=None):
    """Only allow fan-celebrity or celebrity-celebrity messaging"""
    if not can_message(user, recipient):
        return None  # or raise error in caller
    
    thread = (
        MessageThread.objects.filter(participants=user)
        .filter(participants=recipient)
        .annotate(participant_count=Count("participants"))
        .filter(participant_count=2)
        .first()
    )
    if thread:
        return thread
    thread = MessageThread.objects.create(title=title or "")
    thread.participants.add(user, recipient)
    return thread

@login_required
def message_list(request):
    threads = (
        request.user.threads.all()
        .prefetch_related("participants")
        .order_by("-last_message_at", "-updated_at")
    )
    return render(request, "core/message_list.html", {"threads": threads})


def get_or_create_thread(user, recipient, title=None):
    thread = (
        MessageThread.objects.filter(participants=user)
        .filter(participants=recipient)
        .annotate(participant_count=Count("participants"))
        .filter(participant_count=2)
        .first()
    )
    if thread:
        return thread
    thread = MessageThread.objects.create(title=title or "")
    thread.participants.add(user, recipient)
    return thread


def activate_user_subscription(user, plan, provider, transaction=None):
    expires_at = timezone.now() + timezone.timedelta(days=plan.duration_days)
    subscription, created = UserSubscription.objects.get_or_create(
        user=user,
        plan=plan,
        defaults={
            "started_at": timezone.now(),
            "expires_at": expires_at,
            "is_active": True,
            "auto_renew": False,
            "payment_provider": provider,
            "provider_subscription_id": transaction.provider_payment_id if transaction else "",
            "metadata": transaction.metadata if transaction else {},
        },
    )
    if not created:
        subscription.expires_at = max(subscription.expires_at, timezone.now()) + timezone.timedelta(days=plan.duration_days)
        subscription.is_active = True
        subscription.payment_provider = provider
        subscription.provider_subscription_id = transaction.provider_payment_id if transaction else subscription.provider_subscription_id
        subscription.metadata = transaction.metadata if transaction else subscription.metadata
        subscription.save(update_fields=["expires_at", "is_active", "payment_provider", "provider_subscription_id", "metadata"])
    return subscription


@login_required
def message_thread(request, thread_id):
    thread = get_object_or_404(MessageThread, pk=thread_id, participants=request.user)
    
    # Check if this thread is valid under new rules (for existing threads)
    other_participant = thread.participants.exclude(pk=request.user.pk).first()
    if other_participant and not can_message(request.user, other_participant):
        messages.warning(request, "This conversation is no longer available.")
        return redirect("core:message_list")
    
    reply_form = MessageReplyForm(request.POST or None)

    if request.method == "POST" and reply_form.is_valid():
        message_instance = reply_form.save(commit=False)
        message_instance.thread = thread
        message_instance.sender = request.user
        message_instance.save()
        thread.update_last_message()

        recipient = thread.participants.exclude(pk=request.user.pk).first()
        if recipient:
            create_notification(
                user=recipient,
                actor=request.user,
                verb=f"{request.user.username} replied to your thread.",
                target_url=request.build_absolute_uri(request.path),
            )

        messages.success(request, "Reply sent.")
        return redirect("core:message_thread", thread_id=thread.pk)

    return render(request, "core/message_thread.html", {"thread": thread, "reply_form": reply_form})


@login_required
def message_new(request, username=None):
    initial = {"recipient": username} if username else {}
    form = NewMessageThreadForm(request.POST or None, initial=initial)

    if request.method == "POST" and form.is_valid():
        recipient_username = form.cleaned_data["recipient"]
        User = get_user_model()
        
        try:
            recipient = User.objects.get(username__iexact=recipient_username)
        except User.DoesNotExist:
            form.add_error("recipient", "Recipient not found.")
            return render(request, "core/message_new.html", {"form": form})
        
        if recipient == request.user:
            form.add_error("recipient", "You cannot send a message to yourself.")
        elif not can_message(request.user, recipient):
            form.add_error("recipient", "Fans can only message celebrities.")
        else:
            thread = get_or_create_thread(request.user, recipient, title=form.cleaned_data.get("title"))
            if thread is None:
                form.add_error("recipient", "Unable to create message thread.")
                return render(request, "core/message_new.html", {"form": form})
            
            Message.objects.create(
                thread=thread,
                sender=request.user,
                body=form.cleaned_data["body"],
            )
            thread.update_last_message()
            create_notification(
                user=recipient,
                actor=request.user,
                verb=f"{request.user.username} sent you a message.",
                target_url=request.build_absolute_uri(request.path),
            )
            messages.success(request, "Message sent.")
            return redirect("core:message_thread", thread_id=thread.pk)

    return render(request, "core/message_new.html", {"form": form})


@login_required
def notification_list(request):
    notifications = request.user.notifications.all()
    return render(request, "core/notifications.html", {"notifications": notifications})


@login_required
def notification_mark_read(request, notification_id):
    notification = get_object_or_404(request.user.notifications, pk=notification_id)
    notification.unread = False
    notification.save(update_fields=["unread"])
    return redirect("core:notification_list")

@login_required
def react_media(request, media_id):
    media = get_object_or_404(CreatorMedia, id=media_id)
    reaction_type = request.POST.get("type", "like")

    obj, created = MediaReaction.objects.get_or_create(
        user=request.user,
        media_item=media,
        defaults={"type": reaction_type}
    )

    if not created:
        if obj.type == reaction_type:
            obj.delete()  # toggle off
        else:
            obj.type = reaction_type
            obj.save()

    context = {
        "media": media,
        "user_reaction": MediaReaction.objects.filter(user=request.user, media_item=media).first(),
        "counts": media.reactions.values("type").order_by("type"),
    }

    return render(request, "core/partials/reactions.html", context)


@login_required
def toggle_bookmark(request, media_id):
    media = get_object_or_404(CreatorMedia, id=media_id)

    obj, created = MediaBookmark.objects.get_or_create(
        user=request.user,
        media_item=media
    )

    if not created:
        obj.delete()
        saved = False
    else:
        saved = True

    return render(request, "core/partials/bookmark.html", {
        "media": media,
        "saved": saved
    })
    
    
@login_required
def media_comments(request, media_id):
    media = get_object_or_404(CreatorMedia, id=media_id)

    return render(request, "core/partials/comments.html", {
        "media": media,
        "comments": media.comments.select_related("user")
    })
    
@login_required
def add_comment(request, media_id):
    media = get_object_or_404(CreatorMedia, id=media_id)

    body = request.POST.get("body", "").strip()
    if body:
        MediaComment.objects.create(
            user=request.user,
            media_item=media,
            body=body
        )

    return render(request, "core/partials/comments.html", {
        "media": media,
        "comments": media.comments.select_related("user")
    })
    
@login_required
def toggle_follow(request, username):
    profile = get_object_or_404(Profile, user__username=username)

    obj, created = ProfileFollow.objects.get_or_create(
        follower=request.user,
        profile=profile
    )

    if not created:
        obj.delete()
        following = False
    else:
        following = True

    follower_count = profile.follower_count

    button_html = render(request, "core/partials/follow_button.html", {
        "profile": profile,
        "following": following,
    }).content.decode('utf-8')
    
    response = HttpResponse(button_html)
    response['HX-Trigger'] = 'followerUpdated'
    response['X-Follower-Count'] = str(follower_count)
    return response

@login_required
def support_chat_view(request):
    """Live Support Chat View"""
    context = {
        'title': 'Live Support',
        'user': request.user,
    }
    return render(request, 'chat/support_chat.html', context)


# Optional: API endpoint for sending/receiving messages (for HTMX + JS)
@csrf_exempt
@login_required
def send_chat_message(request):
    if request.method == 'POST':
        try:
            data = json.loads(request.body)
            message_text = data.get('message')
            
            if not message_text:
                return JsonResponse({'error': 'Message cannot be empty'}, status=400)
            
            message = ChatMessage.objects.create(
                user=request.user,
                message=message_text,
                is_from_support=False
            )
            
            return JsonResponse({
                'success': True,
                'message': {
                    'id': message.id,
                    'text': message.message,
                    'timestamp': message.created_at.strftime('%H:%M'),
                    'is_from_support': message.is_from_support
                }
            })
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=400)
    
    return JsonResponse({'error': 'Invalid request'}, status=405)


# Get chat history
@login_required
def get_chat_history(request):
    messages = ChatMessage.objects.filter(user=request.user).order_by('created_at')[:50]
    data = [{
        'id': msg.id,
        'text': msg.message,
        'timestamp': msg.created_at.strftime('%H:%M'),
        'is_from_support': msg.is_from_support
    } for msg in messages]
    
    return JsonResponse({'messages': data})


def send_transaction_email(user, subject, message):
    send_mail(
        subject=subject,
        message=message,
        from_email=settings.DEFAULT_FROM_EMAIL,
        recipient_list=[user.email],
        fail_silently=True,
    )
    
    
    
    

