"""Deal chat: buyer<->seller messaging about the product, an optional
"call support" escalation, and a staff view/reply path (core.admin_chat_api).

Delivery is plain request/response — clients poll GET .../chat?after=<iso> every
few seconds (cheap on any hosting) and push notifications wake the app for new
messages; a WebSocket layer can replace the polling later without changing
these payloads."""

from django.utils import timezone
from django.utils.dateparse import parse_datetime
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from core.notify import push_notification
from core.views import _get_authenticated_user

from .models import Deal, DealChatRead, DealChatState, DealMessage

MAX_BODY = 2000
MAX_IMAGE_BYTES = 5 * 1024 * 1024
ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/jpg", "image/png", "image/webp"}


def serialize_message(msg, request, viewer=None):
	url = None
	if msg.image:
		url = request.build_absolute_uri(msg.image.url)
	sender = None
	if msg.sender_id:
		sender = {"id": str(msg.sender_id), "name": msg.sender.name or msg.sender.phone}
	elif msg.sender_role == DealMessage.SenderRole.SUPPORT:
		sender = {"id": None, "name": msg.staff_name or "Kitonga Support"}
	return {
		"id": str(msg.id),
		"sender_role": msg.sender_role,
		"sender": sender,
		"body": msg.body,
		"image_url": url,
		"created_at": msg.created_at,
		"mine": bool(viewer is not None and msg.sender_id == viewer.id),
	}


def chat_state_payload(deal):
	state = DealChatState.objects.filter(deal=deal).first()
	return {
		"support_requested": bool(state and state.support_requested_at),
		"support_joined": bool(state and state.support_joined_at),
		"support_joined_by": state.support_joined_by if state else "",
	}


def _party_role(deal, user):
	if deal.seller_id == user.id:
		return DealMessage.SenderRole.SELLER
	if deal.buyer_id == user.id:
		return DealMessage.SenderRole.BUYER
	return None


def _other_party(deal, user):
	return deal.buyer if deal.seller_id == user.id else deal.seller


def _load(deal_id, user):
	deal = Deal.objects.filter(id=deal_id).select_related("seller", "buyer").first()
	if deal is None:
		return None, None, Response({"detail": "Deal not found."}, status=status.HTTP_404_NOT_FOUND)
	role = _party_role(deal, user)
	if role is None:
		return None, None, Response({"detail": "Only the buyer or seller can use this chat."}, status=status.HTTP_403_FORBIDDEN)
	return deal, role, None


@api_view(["GET", "POST"])
def deal_chat(request, deal_id):
	user, auth_error = _get_authenticated_user(request)
	if auth_error:
		return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)
	deal, role, err = _load(deal_id, user)
	if err:
		return err

	if request.method == "POST":
		if user.is_frozen:
			return Response({"detail": "Your account is frozen."}, status=status.HTTP_403_FORBIDDEN)
		body = str(request.data.get("body") or "").strip()
		image = request.FILES.get("image")
		if not body and image is None:
			return Response({"detail": "Write a message or attach a photo."}, status=status.HTTP_400_BAD_REQUEST)
		if len(body) > MAX_BODY:
			return Response({"detail": f"Message is too long (max {MAX_BODY} characters)."}, status=status.HTTP_400_BAD_REQUEST)
		if image is not None:
			if image.content_type not in ALLOWED_IMAGE_TYPES:
				return Response({"detail": "Only JPEG, PNG or WEBP photos are allowed."}, status=status.HTTP_400_BAD_REQUEST)
			if image.size > MAX_IMAGE_BYTES:
				return Response({"detail": "Photo must be 5MB or smaller."}, status=status.HTTP_400_BAD_REQUEST)
		msg = DealMessage.objects.create(deal=deal, sender=user, sender_role=role, body=body, image=image)
		other = _other_party(deal, user)
		if other is not None:
			push_notification(
				other, f"New message · {deal.title}",
				(body or "Sent a photo")[:140], deal=deal,
			)
		return Response(serialize_message(msg, request, user), status=status.HTTP_201_CREATED)

	# Clients poll this every ~2s while a chat is open, so the idle path must be
	# one indexed read: with an `after` cursor and nothing new we return
	# immediately and do NOT touch the read marker (no per-poll DB write).
	qs = DealMessage.objects.filter(deal=deal).select_related("sender")
	after = parse_datetime(str(request.query_params.get("after") or ""))
	if after is not None:
		qs = qs.filter(created_at__gt=after)
	messages = [serialize_message(m, request, user) for m in qs.order_by("created_at")[:200]]
	if messages or after is None:
		DealChatRead.objects.update_or_create(deal=deal, user=user, defaults={"last_read_at": timezone.now()})
	return Response({"messages": messages, "state": chat_state_payload(deal)})


@api_view(["POST"])
def deal_chat_call_support(request, deal_id):
	user, auth_error = _get_authenticated_user(request)
	if auth_error:
		return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)
	deal, role, err = _load(deal_id, user)
	if err:
		return err

	state, _ = DealChatState.objects.get_or_create(deal=deal)
	if state.support_requested_at is None:
		state.support_requested_at = timezone.now()
		state.support_requested_by = user
		state.save(update_fields=["support_requested_at", "support_requested_by"])
		DealMessage.objects.create(
			deal=deal, sender_role=DealMessage.SenderRole.SYSTEM,
			body=f"{user.name or user.phone} asked Kitonga Support to join this chat.",
		)
	return Response(chat_state_payload(deal))
