from contextlib import suppress
from datetime import date

from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from pandas._libs.tslibs.offsets import BDay
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from wbcompliance.viewsets.risk_management.mixins import RiskCheckViewSetMixin
from wbcore import viewsets
from wbcore.metadata.configs.display.instance_display import (
    Display,
    create_simple_display,
)
from wbcore.permissions.permissions import InternalUserPermissionMixin
from wbcore.utils.views import CloneMixin

from wbportfolio.models import AssetPosition, TradeProposal
from wbportfolio.models.transactions.trade_proposals import (
    replay_as_task,
)
from wbportfolio.serializers import (
    ReadOnlyTradeProposalModelSerializer,
    TradeProposalModelSerializer,
    TradeProposalRepresentationSerializer,
)

from ..configs import (
    TradeProposalButtonConfig,
    TradeProposalDisplayConfig,
    TradeProposalEndpointConfig,
    TradeProposalPortfolioEndpointConfig,
)
from ..mixins import UserPortfolioRequestPermissionMixin


class TradeProposalRepresentationViewSet(InternalUserPermissionMixin, viewsets.RepresentationViewSet):
    IDENTIFIER = "wbportfolio:trade"
    queryset = TradeProposal.objects.all()
    serializer_class = TradeProposalRepresentationSerializer


class TradeProposalModelViewSet(CloneMixin, RiskCheckViewSetMixin, InternalUserPermissionMixin, viewsets.ModelViewSet):
    ordering_fields = ("trade_date",)
    ordering = ("-trade_date",)
    search_fields = ("comment",)
    filterset_fields = {"trade_date": ["exact", "gte", "lte"], "status": ["exact"]}

    queryset = TradeProposal.objects.select_related("rebalancing_model", "portfolio")
    serializer_class = TradeProposalModelSerializer
    display_config_class = TradeProposalDisplayConfig
    button_config_class = TradeProposalButtonConfig
    endpoint_config_class = TradeProposalEndpointConfig

    def get_serializer_class(self):
        if self.new_mode or (
            "pk" in self.kwargs and (obj := self.get_object()) and obj.status == TradeProposal.Status.DRAFT
        ):
            return TradeProposalModelSerializer
        return ReadOnlyTradeProposalModelSerializer

    @classmethod
    def get_clone_instance_display(cls) -> Display:
        return create_simple_display(
            [
                ["comment"],
                ["trade_date"],
            ]
        )

    @classmethod
    def _get_risk_checks_button_title(cls) -> str:
        return "Pre-Trade Checks"

    @action(detail=True, methods=["PATCH"])
    def reset(self, request, pk=None):
        trade_proposal = get_object_or_404(TradeProposal, pk=pk)
        if trade_proposal.status == TradeProposal.Status.DRAFT:
            trade_proposal.reset_trades()
            return Response({"send": True})
        return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["PATCH"])
    def normalize(self, request, pk=None):
        trade_proposal = get_object_or_404(TradeProposal, pk=pk)
        if trade_proposal.status == TradeProposal.Status.DRAFT:
            trade_proposal.normalize_trades()
            return Response({"send": True})
        return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["PATCH"])
    def replay(self, request, pk=None):
        trade_proposal = get_object_or_404(TradeProposal, pk=pk)
        if trade_proposal.portfolio.is_manageable:
            replay_as_task.delay(trade_proposal.id)
            return Response({"send": True})
        return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["PATCH"])
    def deleteall(self, request, pk=None):
        trade_proposal = get_object_or_404(TradeProposal, pk=pk)
        if trade_proposal.status == TradeProposal.Status.DRAFT:
            trade_proposal.trades.all().delete()
            return Response({"send": True})
        return Response({"status": "Trade proposal is not Draft"}, status=status.HTTP_400_BAD_REQUEST)


class TradeProposalPortfolioModelViewSet(UserPortfolioRequestPermissionMixin, TradeProposalModelViewSet):
    endpoint_config_class = TradeProposalPortfolioEndpointConfig

    @cached_property
    def default_trade_date(self) -> date | None:
        with suppress(AssetPosition.DoesNotExist):
            return (self.portfolio.assets.latest("date").date + BDay(1)).date()

    def get_queryset(self):
        return TradeProposal.objects.filter(portfolio=self.kwargs["portfolio_id"])
