from decimal import Decimal

from django.db.models import Q
from django.utils.dateparse import parse_date

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from accounts.models import Users
from chats.models import Chat
from contracts.serializers import ContractDetailsSerializer
from works.models import Works, Work_Sub_Categories
from sub_categories.models import Sub_Categories
from .models import Work_Contract

from utils.helpers import send_contract_notification, verify_token


class CreateContractView(APIView):

    def post(self, request):

        user_id = verify_token(request)

        if not user_id:
            return Response(
                {"message": "Unauthorized"},
                status=401
            )

        try:

            login_user = Users.objects.get(id=user_id)

            work = Works.objects.get(
                id=request.data.get("work_id")
            )

            sub_category = Sub_Categories.objects.get(
                id=request.data.get("sub_category_id")
            )

            created_for = Users.objects.get(
                id=request.data.get("created_for")
            )

            contract = Work_Contract.objects.create(

                work=work,
                sub_category=sub_category,

                created_by=login_user,
                created_for=created_for,

                last_modified_by=login_user,

                pending_with=created_for,

                title=request.data.get("title"),

                description=request.data.get(
                    "description",
                    ""
                ),

                amount=Decimal(
                    request.data.get("amount") or 0
                ),

                contract_type=int(
                    request.data.get("contract_type") or 0
                ),

                escrow_amount=Decimal(
                    request.data.get("escrow_amount") or 0
                ),

                start_date=parse_date(
                    request.data.get("start_date")
                ),

                end_date=parse_date(
                    request.data.get("end_date")
                ),

                terms_conditions=request.data.get(
                    "terms_conditions",
                    ""
                ),

                message=request.data.get(
                    "message",
                    ""
                ),

                escrow_status=1 if int(
                    request.data.get("contract_type") or 0
                ) == 1 else 0,

                flow_status=1,  # SENT
                status=0
            )

            send_contract_notification(
                user=created_for,
                title="New Contract Received",
                body=f"{login_user.name} sent you a contract.",
                notification_type="contract",
                reference_id=contract.id,
                data={
                    "contract_id": contract.id,
                    "action": "created"
                }
            )

            return Response({
                "message": "Contract created successfully",
                "contract_id": contract.id,
                "status" : 200
            }, status=201)

        except Users.DoesNotExist:
            return Response({
                "message": "User not found"
            }, status=404)

        except Works.DoesNotExist:
            return Response({
                "message": "Work not found"
            }, status=404)

        except Sub_Categories.DoesNotExist:
            return Response({
                "message": "Sub category not found"
            }, status=404)

        except Exception as e:
            return Response({
                "message": str(e)
            }, status=500)


    def get(self, request):

        user_id = verify_token(request)

        if not user_id:

            return Response(
                {
                    "message": "Unauthorized"
                },
                status=401
            )

        try:

            contract_id = request.GET.get(
                "contract_id"
            )

            if not contract_id:

                return Response(
                    {
                        "message": "contract_id is required"
                    },
                    status=400
                )

            contract = Work_Contract.objects.get(
                id=contract_id
            )

            # ============================================
            # PERMISSION CHECK
            # ============================================

            if user_id not in [
                contract.created_by.id,
                contract.created_for.id
            ]:

                return Response(
                    {
                        "message": "Permission denied"
                    },
                    status=403
                )

            serializer = ContractDetailsSerializer(
                contract,
                context={
                    'request': request
                }
            )

            return Response(
                {
                    "message": "Contract details fetched successfully",
                    "contract": serializer.data,
                    "user_id" : user_id,
                    "status" : 200
                },
                status=200
            )

        except Work_Contract.DoesNotExist:

            return Response(
                {
                    "message": "Contract not found"
                },
                status=404
            )

        except Exception as e:

            return Response(
                {
                    "message": str(e)
                },
                status=500
            )

# =========================================================
# CHECK CONTRACT
# =========================================================

class CheckContractView(APIView):

    def post(self, request):

        user_id = verify_token(request)

        if not user_id:

            return Response(
                {
                    "message": "Unauthorized"
                },
                status=status.HTTP_401_UNAUTHORIZED
            )

        try:

            work_id = request.data.get(
                "work_id"
            )

            created_for = request.data.get(
                "created_for"
            )

            sub_category_id = request.data.get(
                "sub_category_id"
            )

            # =================================================
            # CHECK EXISTING CONTRACT
            # =================================================

            contract = Work_Contract.objects.filter(

                Q(
                    created_by_id=user_id,
                    created_for_id=created_for
                ) |

                Q(
                    created_by_id=created_for,
                    created_for_id=user_id
                ),

                work_id=work_id,
                sub_category_id=sub_category_id

            ).order_by(
                "-id"
            ).first()

            if contract:

                return Response(
                    {
                        "status": 200,
                        "contract_id": contract.id,
                        "message": "Contract exists"
                    },
                    status=status.HTTP_200_OK
                )

            return Response(
                {
                    "status": 404,
                    "message": "No contract found"
                },
                status=status.HTTP_404_NOT_FOUND
            )

        except Exception as e:

            return Response(
                {
                    "message": str(e)
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


# =========================================================
# UPDATE CONTRACT
# =========================================================

class UpdateContractView(APIView):

    def post(self, request):

        user_id = verify_token(request)

        if not user_id:
            return Response(
                {"message": "Unauthorized"},
                status=status.HTTP_401_UNAUTHORIZED
            )

        try:

            contract_id = request.data.get("contract_id")
            action = request.data.get("action")

            contract = Work_Contract.objects.get(id=contract_id)
            login_user = Users.objects.get(id=user_id)

            # =================================================
            # PERMISSION CHECK
            # =================================================
            if login_user.id not in [
                contract.created_by.id,
                contract.created_for.id
            ]:
                return Response(
                    {"message": "Forbidden"},
                    status=status.HTTP_403_FORBIDDEN
                )

            # =================================================
            # MODIFY CONTRACT
            # =================================================
            if action == "modify":

                contract.title = request.data.get("title", contract.title)
                contract.description = request.data.get("description", contract.description)

                contract.amount = Decimal(request.data.get("amount") or 0)
                contract.contract_type = int(request.data.get("contract_type") or 0)
                contract.escrow_amount = Decimal(request.data.get("escrow_amount") or 0)

                contract.start_date = parse_date(request.data.get("start_date"))
                contract.end_date = parse_date(request.data.get("end_date"))

                contract.terms_conditions = request.data.get(
                    "terms_conditions",
                    contract.terms_conditions
                )

                contract.message = request.data.get(
                    "message",
                    contract.message
                )

                # escrow reset
                contract.escrow_status = 1 if contract.contract_type == 1 else 0

                # reset approvals
                contract.customer_approved = False
                contract.contractor_approved = False

                # send to opposite user
                contract.pending_with = (
                    contract.created_for
                    if login_user.id == contract.created_by.id
                    else contract.created_by
                )

                contract.flow_status = 2
                contract.status = 0
                contract.last_modified_by = login_user

                contract.save()

                other_user = (
                    contract.created_for
                    if login_user.id == contract.created_by.id
                    else contract.created_by
                )

                send_contract_notification(
                    user=other_user,
                    title="Contract Updated",
                    body=f"{login_user.name} modified the contract.",
                    notification_type="contract",
                    reference_id=contract.id,
                    data={
                        "contract_id": contract.id,
                        "action": "modified"
                    }
                )
                

                return Response(
                    {"message": "Contract modified successfully","status" : 200},
                    status=status.HTTP_200_OK
                )

            # =================================================
            # ACCEPT CONTRACT
            # =================================================
            if action == "accept":

                if login_user.id == contract.created_by.id:
                    contract.customer_approved = True

                elif login_user.id == contract.created_for.id:
                    contract.contractor_approved = True

                # BOTH ACCEPTED
                if contract.customer_approved and contract.contractor_approved:

                    contract.flow_status = 3
                    contract.status = 1
                    contract.pending_with = None


                    contractor_user = None

                    if contract.created_by.user_type == 3:
                        contractor_user = contract.created_by

                    elif contract.created_for.user_type == 3:
                        contractor_user = contract.created_for

                    Work_Sub_Categories.objects.filter(
                        work=contract.work,
                        sub_category=contract.sub_category
                    ).update(
                        contractor=contractor_user,
                        status=1
                    )

                    # =====================================================
                    # DELETE OTHER CONTRACTS FOR SAME WORK + SUBCATEGORY
                    # (keep only this accepted contract)
                    # =====================================================
                    Work_Contract.objects.filter(
                        work=contract.work,
                        sub_category=contract.sub_category
                    ).exclude(
                        id=contract.id
                    ).delete()

                    # =====================================================
                    # DELETE OTHER CONTRACT-RELATED CHATS
                    # =====================================================
                    Chat.objects.filter(
                        work=contract.work,
                        work_sub_category=contract.sub_category
                    ).exclude(
                        sender_id__in=[contract.created_by_id, contract.created_for_id],
                        receiver_id__in=[contract.created_by_id, contract.created_for_id]
                    ).delete()
                
                else:
                    # wait for other side
                    contract.pending_with = (
                        contract.created_for
                        if login_user.id == contract.created_by.id
                        else contract.created_by
                    )

                contract.last_modified_by = login_user
                contract.save()

                other_user = (
                    contract.created_for
                    if login_user.id == contract.created_by.id
                    else contract.created_by
                )

                send_contract_notification(
                    user=other_user,
                    title="Contract Accepted",
                    body=f"{login_user.name} accepted the contract.",
                    notification_type="contract",
                    reference_id=contract.id,
                    data={
                        "contract_id": contract.id,
                        "action": "accepted"
                    }
                )

                return Response(
                    {"message": "Contract accepted", "status" : 200},
                    status=status.HTTP_200_OK
                )

            # =================================================
            # REJECT CONTRACT
            # =================================================
            if action == "reject":

                contract.flow_status = 4
                contract.status = 2

                contract.customer_approved = False
                contract.contractor_approved = False
                contract.pending_with = None

                contract.last_modified_by = login_user
                contract.save()

                other_user = (
                    contract.created_for
                    if login_user.id == contract.created_by.id
                    else contract.created_by
                )

                send_contract_notification(
                    user=other_user,
                    title="Contract Rejected",
                    body=f"{login_user.name} rejected the contract.",
                    notification_type="contract",
                    reference_id=contract.id,
                    data={
                        "contract_id": contract.id,
                        "action": "rejected"
                    }
                )

                return Response(
                    {"message": "Contract rejected","status" : 200},
                    status=status.HTTP_200_OK
                )

            return Response(
                {"message": "Invalid action"},
                status=status.HTTP_400_BAD_REQUEST
            )

        except Work_Contract.DoesNotExist:
            return Response(
                {"message": "Contract not found"},
                status=status.HTTP_404_NOT_FOUND
            )

        except Exception as e:
            return Response(
                {"message": str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

# =========================================================
# GET ALL CONTRACTS
# =========================================================

class ContractsView(APIView):

    def get(self, request):

        user_id = verify_token(request)

        if not user_id:

            return Response(
                {
                    "message": "Unauthorized"
                },
                status=401
            )

        try:

            contracts = Work_Contract.objects.filter(

                Q(created_by_id=user_id) |
                Q(created_for_id=user_id)

            ).select_related(

                "work",
                "sub_category",
                "created_by",
                "created_for",
                "last_modified_by",
                "pending_with"

            ).order_by("-id")

            print(contracts)

            serializer = ContractDetailsSerializer(
                contracts,
                many=True,
                context={
                    "request": request
                }
            )

            return Response(
                {
                    "message": "Contracts fetched successfully",
                    "count": contracts.count(),
                    "contracts": serializer.data,
                    'status' : 200
                },
                status=200
            )

        except Exception as e:

            return Response(
                {
                    "message": str(e)
                },
                status=500
            )