"""
Chat API views — all JSON endpoints consumed by the JS widget.

Endpoints:
  POST /chat/start/           → create session, return session_id + greeting
  POST /chat/message/         → send message, get AI reply
  POST /chat/identify/        → visitor provides name + email
  GET  /chat/history/<uuid>/  → fetch message history (for reconnecting)
"""
import logging
from django.http import JsonResponse
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from django.utils.decorators import method_decorator

from .models import ChatSession, ChatMessage
from .openai_client import get_ai_response, detect_handoff_intent

logger = logging.getLogger(__name__)

# Max messages per session per hour (rate limit)
RATE_LIMIT_MESSAGES = 50
# Max characters in a single user message
MAX_MESSAGE_LENGTH = 1000


def _get_client_ip(request):
    x_forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded:
        return x_forwarded.split(',')[0].strip()
    return request.META.get('REMOTE_ADDR')


@csrf_exempt
@require_POST
def chat_start(request):
    """Create a new chat session for the current tenant."""
    tenant = getattr(request, 'tenant', None)

    if not tenant or not getattr(tenant, 'chat_enabled', False):
        return JsonResponse({'error': 'Chat not available'}, status=404)

    session = ChatSession.objects.create(
        tenant=tenant,
        visitor_ip=_get_client_ip(request),
    )

    greeting = tenant.chat_greeting or 'Hi! How can I help you today?'

    # Store the greeting as an assistant message
    ChatMessage.objects.create(
        session=session,
        role=ChatMessage.ROLE_ASSISTANT,
        content=greeting,
    )

    return JsonResponse({
        'session_id': str(session.session_id),
        'greeting': greeting,
        'whatsapp_enabled': bool(tenant.chat_whatsapp),
    })


@csrf_exempt
@require_POST
def chat_message(request):
    """Receive a user message, get AI reply, check for handoff intent."""
    import json

    tenant = getattr(request, 'tenant', None)
    if not tenant or not getattr(tenant, 'chat_enabled', False):
        return JsonResponse({'error': 'Chat not available'}, status=404)

    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Invalid JSON'}, status=400)

    session_id = data.get('session_id', '').strip()
    user_message = data.get('message', '').strip()

    if not session_id or not user_message:
        return JsonResponse({'error': 'session_id and message are required'}, status=400)

    # Sanitise input
    user_message = user_message[:MAX_MESSAGE_LENGTH]

    # Load session (must belong to this tenant)
    try:
        session = ChatSession.objects.get(session_id=session_id, tenant=tenant)
    except ChatSession.DoesNotExist:
        return JsonResponse({'error': 'Session not found'}, status=404)

    if session.status == ChatSession.STATUS_CLOSED:
        return JsonResponse({'error': 'Session is closed'}, status=400)

    # Rate limit check
    from datetime import timedelta
    one_hour_ago = timezone.now() - timedelta(hours=1)
    recent_count = session.messages.filter(
        role=ChatMessage.ROLE_USER,
        created_at__gte=one_hour_ago
    ).count()

    if recent_count >= RATE_LIMIT_MESSAGES:
        return JsonResponse({'error': 'Message limit reached. Please continue via WhatsApp or contact form.'}, status=429)

    # Store user message
    ChatMessage.objects.create(
        session=session,
        role=ChatMessage.ROLE_USER,
        content=user_message,
    )

    # Daily spend guard. Checked before the API call so an exhausted budget
    # costs nothing at all. Counts tokens billed today across the tenant.
    budget = getattr(tenant, 'chat_daily_token_budget', 0) or 0
    if budget:
        from django.db.models import Sum
        start_of_day = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
        spent = ChatMessage.objects.filter(
            session__tenant=tenant,
            created_at__gte=start_of_day,
        ).aggregate(t=Sum('tokens'))['t'] or 0

        if spent >= budget:
            logger.warning(
                'Chat daily token budget reached for tenant %s: %s of %s',
                tenant.name, spent, budget,
            )
            fallback = (
                'Our assistant is taking a short break. Please send your question '
                'through the contact form and a person will get back to you.'
            )
            ChatMessage.objects.create(
                session=session,
                role=ChatMessage.ROLE_ASSISTANT,
                content=fallback,
                tokens=0,
            )
            last = session.messages.filter(role=ChatMessage.ROLE_ASSISTANT).order_by('-pk').first()
            return JsonResponse({
                'reply': fallback,
                'last_id': last.pk if last else None,
                'budget_reached': True,
            })

    # Check for WhatsApp handoff intent BEFORE calling OpenAI
    handoff_keyword = None
    if tenant.chat_whatsapp and not session.handoff_triggered:
        handoff_keyword = detect_handoff_intent(user_message, tenant.chat_whatsapp_trigger)

    # Get AI reply
    history = session.build_openai_history()
    ai_result = get_ai_response(history, tenant)

    ai_content = ai_result['content']
    tokens     = ai_result['tokens']

    # Store assistant reply
    ChatMessage.objects.create(
        session=session,
        role=ChatMessage.ROLE_ASSISTANT,
        content=ai_content,
        tokens=tokens,
    )

    # If handoff triggered, mark the session and append a CTA
    handoff_url = None
    if handoff_keyword and tenant.chat_whatsapp:
        session.handoff_triggered = True
        session.handoff_keyword   = handoff_keyword
        session.handoff_at        = timezone.now()
        session.status            = ChatSession.STATUS_HANDOFF
        session.save(update_fields=['handoff_triggered', 'handoff_keyword', 'handoff_at', 'status'])
        handoff_url = session.whatsapp_handoff_url or tenant.whatsapp_link

    # Once the visitor has sent two messages there is enough to label the
    # chat. Done in a background thread so it never delays the reply.
    if not session.topic:
        from apps.chat.models import quick_reply_label
        chip = quick_reply_label([user_message])
        if chip:
            # A clicked option is unambiguous, so label immediately on the
            # first message. No API call, no waiting for a second reply.
            session.topic = chip[:60]
            session.save(update_fields=['topic'])
        else:
            user_msgs = session.messages.filter(role=ChatMessage.ROLE_USER).count()
            if user_msgs >= 2:
                import threading
                threading.Thread(target=session.generate_topic, daemon=True).start()

    # Get the pk of the assistant message we just stored for poll dedup
    last_msg = session.messages.filter(role=ChatMessage.ROLE_ASSISTANT).order_by('-pk').first()

    return JsonResponse({
        'reply': ai_content,
        'msg_pk': last_msg.pk if last_msg else None,
        'handoff': {
            'triggered': bool(handoff_keyword),
            'url': handoff_url,
            'keyword': handoff_keyword,
        },
    })


@csrf_exempt
@require_POST
def chat_identify(request):
    """Visitor provides their name and email mid-chat."""
    import json

    tenant = getattr(request, 'tenant', None)
    if not tenant:
        return JsonResponse({'error': 'Not found'}, status=404)

    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Invalid JSON'}, status=400)

    session_id = data.get('session_id', '').strip()
    name  = data.get('name', '').strip()[:150]
    email = data.get('email', '').strip()[:254]

    try:
        session = ChatSession.objects.get(session_id=session_id, tenant=tenant)
    except ChatSession.DoesNotExist:
        return JsonResponse({'error': 'Session not found'}, status=404)

    if email:
        from apps.tickets.models import BlockedEmail
        if BlockedEmail.is_blocked(email):
            return JsonResponse({'ok': True})  # silent — spammer thinks it worked

    if name:
        session.visitor_name = name
    if email:
        session.visitor_email = email
    session.save(update_fields=['visitor_name', 'visitor_email'])

    return JsonResponse({'ok': True})


@require_GET
def chat_history(request, session_id):
    """Return message history for a session (for widget reconnect)."""
    tenant = getattr(request, 'tenant', None)
    if not tenant:
        return JsonResponse({'error': 'Not found'}, status=404)

    try:
        session = ChatSession.objects.get(session_id=session_id, tenant=tenant)
    except ChatSession.DoesNotExist:
        return JsonResponse({'error': 'Session not found'}, status=404)

    qs = session.messages.all()
    after = request.GET.get('after')
    if after:
        try:
            qs = qs.filter(pk__gt=int(after))
        except (ValueError, TypeError):
            pass

    msgs = [
        {
            'pk': msg.pk,
            'role': msg.role,
            'content': msg.content,
            'time': msg.created_at.isoformat(),
        }
        for msg in qs
    ]

    return JsonResponse({
        'messages': msgs,
        'session_status': session.status,
        'handoff': {
            'triggered': session.handoff_triggered,
            'url': (session.whatsapp_handoff_url or tenant.whatsapp_link) if session.handoff_triggered else None,
        },
    })
