from django.conf import settings
from requests import HTTPError

from wbportfolio.api_clients.jpm import JPMNexusAPIClient
from wbportfolio.order_routing.adapters.base import BaseCustodianAdapter
from wbportfolio.order_routing.adapters.jpm.model import JPMOrder
from wbportfolio.order_routing.exception import RoutingError
from wbportfolio.order_routing.model import ExecutionStatus

STATUS_MAP = {
    "Amend Pending": ExecutionStatus.PENDING,
    "Cancel Pending": ExecutionStatus.PENDING,
    "Cancelled": ExecutionStatus.CANCELLED,
    "Complete": ExecutionStatus.COMPLETED,
    "Complete (Order Cancelled)": ExecutionStatus.COMPLETED,
    "Complete (Partial Fill)": ExecutionStatus.COMPLETED,
    "In Draft": ExecutionStatus.IN_DRAFT,
    "Pending Approval": ExecutionStatus.PENDING,
    "Pending Execution": ExecutionStatus.PENDING,
    "Rebalance Cancelled": ExecutionStatus.CANCELLED,
    "Rebalance Cancelled (Executing partially)": ExecutionStatus.CANCELLED,
    "Rejected": ExecutionStatus.REJECTED,
    "Rejection Acknowledged": ExecutionStatus.PENDING,
    "Waiting for Response": ExecutionStatus.PENDING,
}


class CustodianAdapter(BaseCustodianAdapter):
    client: JPMNexusAPIClient
    order_model = JPMOrder

    def __init__(self, *args, raise_exception: bool = False, **kwargs):
        super().__init__(*args, **kwargs)
        self.raise_exception = raise_exception
        if not self.ticker:
            raise AssertionError(
                "To use the JPM custodian adapter, we need a valid AMC identifier in the form of the ticker"
            )

    def _handle_response(self, res):
        if request_id := res.get("requestId"):
            self.logger.info(request_id)
        if error := res.get("error"):
            self.logger.warning(error)
            if self.raise_exception:
                raise RoutingError(error)

    def authenticate(self) -> bool:
        """
        Authenticate or renew tokens with the custodian API.
        Raises an exception if authentication fails.
        """
        self.client = JPMNexusAPIClient(settings.JPM_NEXUS_API_CLIENT_ID, settings.JPM_NEXUS_API_CLIENT_SECRET)
        return self.client.is_healthy()

    def is_valid(self) -> bool:
        """
        Check whether the given ticker is valid and can be rebalanced
        """

        try:
            self.client.get_rebalance_status_for_ticker(
                self.ticker, self.rebalance_date
            )  # will reaise a 403 error if user does not have access to the given ticker
            return self.authenticate()
        except (HTTPError, KeyError) as e:
            self.logger.warning(f"Couldn't validate adapter: {str(e)}")
            return False

    def submit_rebalancing(
        self, items: list[dict[str, str]], as_draft: bool = True
    ) -> tuple[list[dict[str, str]], str, dict]:
        """
        Submit a rebalance order for the certificate.
        """
        if as_draft:
            self.logger.warning("JPM adapter does not allow draft rebalancing mode")
        res = self.client.submit_rebalance(
            self.ticker,
            self.rebalance_date,
            items,
            identifier_type="BloombergTicker",
            comment="Automatic Workbench rebalancing",
        )
        self._handle_response(res)
        rebalancing_id, message, rebalancing_type = (
            res["result"]["id"],
            res["result"]["message"],
            res["result"]["type"],
        )
        res = self.client.get_current_rebalance_request(self.ticker, rebalancing_id, rebalancing_type)
        self._handle_response(res)

        return res["result"]["results"], message, {"id": rebalancing_id, "type": rebalancing_type}

    def cancel_current_rebalancing(self) -> bool:
        """
        Cancel an existing rebalance order identified by ISIN.
        """
        try:
            res = self.client.cancel_rebalance(self.ticker)
            self._handle_response(res)
            return res["status"] == JPMNexusAPIClient.SUCCESS_VALUE
        except (HTTPError, KeyError):
            return False

    def get_rebalance_status(self) -> tuple[ExecutionStatus, str]:
        res = self.client.get_rebalance_status_for_ticker(self.ticker, self.rebalance_date)
        self._handle_response(res)
        status = res.get("status", "")
        return STATUS_MAP.get(status, ExecutionStatus.UNKNOWN), status

    def get_current_rebalancing(self) -> list[dict[str, str]]:
        """
        Fetch the current rebalance request details for a certificate.
        """
        if (rebalancing_id := self.execution_kwarg.get("id")) and (
            rebalancing_type := self.execution_kwarg.get("type")
        ):
            res = self.client.get_current_rebalance_request(self.ticker, rebalancing_id, rebalancing_type)
            self._handle_response(res)
            return res["result"]["results"]["rebalanceItems"]
        return []
