from uuid import uuid4

from django.core.paginator import EmptyPage, Paginator
from django.utils import timezone
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from core.views import _get_authenticated_user
from disputes.models import Dispute

from .models import Deal, Transaction
from .serializers import DealCreateSerializer, DealSerializer


def _get_deal_for_user_by_code(code, user):
	deal = Deal.objects.filter(code=code).select_related("seller", "buyer").first()
	if deal is None:
		return None, Response({"detail": "Deal not found."}, status=status.HTTP_404_NOT_FOUND)
	if deal.seller_id != user.id and deal.buyer_id != user.id:
		return None, Response({"detail": "You are not allowed to access this deal."}, status=status.HTTP_403_FORBIDDEN)
	return deal, None


def _get_deal_for_user_by_id(deal_id, user):
	deal = Deal.objects.filter(id=deal_id).select_related("seller", "buyer").first()
	if deal is None:
		return None, Response({"detail": "Deal not found."}, status=status.HTTP_404_NOT_FOUND)
	if deal.seller_id != user.id and deal.buyer_id != user.id:
		return None, Response({"detail": "You are not allowed to access this deal."}, status=status.HTTP_403_FORBIDDEN)
	return deal, None


def _serialize_deal(deal, user):
	return DealSerializer(deal, context={"user": user}).data


def _create_transaction(deal, user, amount, event_type, metadata=None):
	Transaction.objects.create(
		deal=deal,
		user=user,
		amount=amount,
		event_type=event_type,
		reference=f"TXN-{uuid4().hex[:12].upper()}",
		metadata=metadata or {},
	)


@api_view(["GET", "POST"])
def list_create_deals(request):
	user, auth_error = _get_authenticated_user(request)
	if auth_error:
		return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)

	if request.method == "POST":
		payload = dict(request.data)
		if "inspection_window" in payload and "inspection_window_hours" not in payload:
			payload["inspection_window_hours"] = payload["inspection_window"]
		serializer = DealCreateSerializer(data=payload, context={"seller": user})
		serializer.is_valid(raise_exception=True)
		deal = serializer.save()
		_create_transaction(
			deal=deal,
			user=user,
			amount=deal.amount,
			event_type=Transaction.EventType.DEAL_CREATED,
			metadata={"inspection_window_hours": deal.inspection_window_hours},
		)
		return Response(_serialize_deal(deal, user), status=status.HTTP_201_CREATED)

	queryset = Deal.objects.filter(seller=user) | Deal.objects.filter(buyer=user)
	queryset = queryset.select_related("seller", "buyer").distinct().order_by("-id")

	status_filter = (request.query_params.get("status") or "all").lower().strip()
	if status_filter == "active":
		queryset = queryset.filter(
			status__in=[
				Deal.Status.DRAFT,
				Deal.Status.AWAITING_PAYMENT,
				Deal.Status.FUNDED,
				Deal.Status.IN_TRANSIT,
				Deal.Status.CONFIRMING,
			]
		)
	elif status_filter == "completed":
		queryset = queryset.filter(status=Deal.Status.COMPLETED)
	elif status_filter == "disputed":
		queryset = queryset.filter(status=Deal.Status.DISPUTED)
	elif status_filter != "all":
		queryset = queryset.filter(status=status_filter)

	try:
		page = max(1, int(request.query_params.get("page", 1)))
	except (TypeError, ValueError):
		page = 1
	try:
		page_size = int(request.query_params.get("page_size", 20))
	except (TypeError, ValueError):
		page_size = 20
	page_size = max(1, min(page_size, 100))

	paginator = Paginator(queryset, page_size)
	try:
		page_obj = paginator.page(page)
	except EmptyPage:
		page_obj = paginator.page(paginator.num_pages) if paginator.num_pages > 0 else []

	results = []
	for deal in page_obj:
		results.append(_serialize_deal(deal, user))

	return Response(
		{
			"count": paginator.count,
			"page": page if paginator.num_pages > 0 else 1,
			"page_size": page_size,
			"num_pages": paginator.num_pages,
			"results": results,
		}
	)


@api_view(["GET"])
def get_deal_by_code(request, code):
	user, auth_error = _get_authenticated_user(request)
	if auth_error:
		return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)

	deal, error_response = _get_deal_for_user_by_code(code=code, user=user)
	if error_response:
		return error_response

	return Response(_serialize_deal(deal, user), status=status.HTTP_200_OK)


@api_view(["POST"])
def join_deal_by_code(request, code):
	user, auth_error = _get_authenticated_user(request)
	if auth_error:
		return Response({"detail": auth_error}, status=status.HTTP_401_UNAUTHORIZED)

	deal = Deal.objects.filter(code=code).select_related("seller", "buyer").first()
	if deal is None:
		return Response({"detail": "Deal not found."}, status=status.HTTP_404_NOT_FOUND)
	if deal.seller_id == user.id:
		return Response({"detail": "Seller cannot join own deal as buyer."}, status=status.HTTP_400_BAD_REQUEST)
	if deal.buyer_id and deal.buyer_id != user.id:
		return Response({"detail": "Deal is already joined by another buyer."}, status=status.HTTP_409_CONFLICT)
	if deal.status not in {Deal.Status.DRAFT, Deal.Status.AWAITING_PAYMENT}:
		return Response({"detail": "Deal can no longer be joined."}, status=status.HTTP_400_BAD_REQUEST)

	if deal.buyer_id is None:
		deal.buyer = user
		deal.status = Deal.Status.AWAITING_PAYMENT
		deal.save(update_fields=["buyer", "status"])

	return Response(_serialize_deal(deal, user), status=status.HTTP_200_OK)


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

	deal, error_response = _get_deal_for_user_by_id(deal_id=deal_id, user=user)
	if error_response:
		return error_response
	if deal.buyer_id != user.id:
		return Response({"detail": "Only the buyer can fund this deal."}, status=status.HTTP_403_FORBIDDEN)
	if deal.status != Deal.Status.AWAITING_PAYMENT:
		return Response({"detail": "Deal is not awaiting payment."}, status=status.HTTP_400_BAD_REQUEST)

	now = timezone.now()
	deal.status = Deal.Status.FUNDED
	deal.funded_at = now
	deal.save(update_fields=["status", "funded_at"])
	_create_transaction(
		deal=deal,
		user=user,
		amount=deal.amount,
		event_type=Transaction.EventType.PAYMENT_RECEIVED,
	)

	return Response(_serialize_deal(deal, user), status=status.HTTP_200_OK)


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

	deal, error_response = _get_deal_for_user_by_id(deal_id=deal_id, user=user)
	if error_response:
		return error_response
	if deal.seller_id != user.id:
		return Response({"detail": "Only the seller can mark this deal as shipped."}, status=status.HTTP_403_FORBIDDEN)
	if deal.status != Deal.Status.FUNDED:
		return Response({"detail": "Deal must be funded before shipping."}, status=status.HTTP_400_BAD_REQUEST)

	now = timezone.now()
	tracking_info = request.data.get("tracking_info")
	deal.status = Deal.Status.IN_TRANSIT
	deal.shipped_at = now
	if tracking_info is not None:
		deal.tracking_info = str(tracking_info).strip() or None
	deal.save(update_fields=["status", "shipped_at", "tracking_info"])

	return Response(_serialize_deal(deal, user), status=status.HTTP_200_OK)


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

	deal, error_response = _get_deal_for_user_by_id(deal_id=deal_id, user=user)
	if error_response:
		return error_response
	if deal.buyer_id != user.id:
		return Response({"detail": "Only the buyer can confirm this deal."}, status=status.HTTP_403_FORBIDDEN)
	if deal.status not in {Deal.Status.IN_TRANSIT, Deal.Status.CONFIRMING}:
		return Response({"detail": "Deal is not ready for confirmation."}, status=status.HTTP_400_BAD_REQUEST)

	now = timezone.now()
	deal.status = Deal.Status.COMPLETED
	deal.confirmed_at = now
	deal.save(update_fields=["status", "confirmed_at"])
	_create_transaction(
		deal=deal,
		user=deal.seller,
		amount=deal.net_amount,
		event_type=Transaction.EventType.PAYOUT_RELEASED,
	)

	return Response(_serialize_deal(deal, user), status=status.HTTP_200_OK)


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

	deal, error_response = _get_deal_for_user_by_id(deal_id=deal_id, user=user)
	if error_response:
		return error_response
	if deal.status in {Deal.Status.COMPLETED, Deal.Status.CANCELLED, Deal.Status.REFUNDED}:
		return Response({"detail": "Cannot raise dispute for this deal status."}, status=status.HTTP_400_BAD_REQUEST)

	reason_category = str(request.data.get("reason_category") or "general").strip()
	reason_detail = str(request.data.get("reason_detail") or request.data.get("message") or "").strip()
	if not reason_detail:
		return Response({"detail": "reason_detail is required."}, status=status.HTTP_400_BAD_REQUEST)

	existing_dispute = Dispute.objects.filter(deal=deal, status__in=[Dispute.Status.OPEN, Dispute.Status.UNDER_REVIEW]).first()
	if existing_dispute:
		return Response(
			{
				"detail": "A dispute is already open for this deal.",
				"dispute_id": str(existing_dispute.id),
			},
			status=status.HTTP_409_CONFLICT,
		)

	dispute = Dispute.objects.create(
		deal=deal,
		raised_by=user,
		reason_category=reason_category,
		reason_detail=reason_detail,
		status=Dispute.Status.OPEN,
	)

	deal.status = Deal.Status.DISPUTED
	deal.has_dispute = True
	deal.save(update_fields=["status", "has_dispute"])

	return Response(
		{
			"detail": "Dispute raised successfully.",
			"dispute_id": str(dispute.id),
			"deal": _serialize_deal(deal, user),
		},
		status=status.HTTP_201_CREATED,
	)
