"""
Module to make API calls to User Portfolio service.
"""
import json
import logging

from bw_essentials.constants.services import Services
from bw_essentials.services.api_client import ApiClient

logger = logging.getLogger(__name__)


class UserPortfolio(ApiClient):
    """
    Class for making API calls to the User Portfolio Service.

    Args:
    user (str): The user for whom the API calls are being made.
    """

    def __init__(self, service_user: str):
        logger.info(f"Initializing UserPortfolio client for user: {service_user}")
        super().__init__(user=service_user)
        self.base_url = self.get_base_url(Services.USER_PORTFOLIO.value)
        self.name = Services.USER_PORTFOLIO.value
        self.urls = {
            "holding_transaction": "holding/transaction",
            "portfolio_rebalance": "userportfolio/portfolio/rebalance",
            "rebalance_transaction": "userportfolio/portfolio/rebalance/transaction",
            "user_instructions": "userportfolio/portfolio/rebalance/transaction/user-instruction",
            "user_inputs": "userportfolio/portfolio/rebalance",
            "user_portfolio_holdings": "holding/holdings",
            "user_holdings": "holding/user/holdings",
            "user_portfolios": "userportfolio/portfolio",
            "orders": "userportfolio/portfolio/rebalance/orders",
            "update_user_portfolio_rebalance": "userportfolio/portfolio/rebalance",
            "update_portfolio_transaction": "userportfolio/portfolio/rebalance/transactions",
            "complete_rebalance_transaction": "userportfolio/portfolio/rebalance/transactions/complete",
            "get_portfolio_rebalances": "userportfolio/portfolio/rebalance",
            "create_basket": "userportfolio/basket",
            "order_instructions": "userportfolio/order/instructions/",
            "retry": "userportfolio/order/retry/basket",
            "skip": "userportfolio/order/skip/basket",
            "create_order_instructions": "userportfolio/order/order-instruction",
            "basket_details": "userportfolio/basket",
            "broker_users_holdings": "holding/{}/users",
            "user_portfolio_thresholds": "alerts/portfolio/thresholds",
            "holding_thresholds": "alerts/holding/thresholds",
            "user_portfolio_summary": "userportfolio/user-portfolio/summary",
            "create_user_portfolio": "userportfolio/portfolio",
            "get_user_portfolio": "userportfolio/portfolio",
            "finn_user_order_place": "userorder/place-order/",
            "finn_user_order_update": "userorder/order",
            "finn_user_order_status": "userorder/order/status",
            "finn_user_order_list": "userorder/orders",
            "finn_user_order_portfolio": "userorder/portfolio",
            "finn_user_order_reconcile": "userorder/reconcile/",
            "user_thresholds": "alerts/user/thresholds",
            "finn_user_order_move_to_draft": "userorder/orders/status/draft/",
            "get_stop_loss_constituents": "alerts/holding/stop_loss_hit_constituents"
        }

    def create_holding_transaction(self, payload):
        """
        Make a holding transaction.

        Args:
        payload (str): The payload for the holding transaction.
        Returns:
        dict: Holding transaction data.
        """
        logger.info(f"In - holding_transaction {payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get("holding_transaction"),
                          data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def create_portfolio_rebalance(self, payload):
        """
        Perform a portfolio rebalance.

        Args:
        payload (str): The payload for the portfolio rebalance.

        Returns:
        dict: Portfolio rebalance data.
        """
        logger.info(f"In - portfolio_rebalance {payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get("portfolio_rebalance"),
                          data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def create_rebalance_transaction(self, payload):
        """
        Perform a rebalance transaction.

        Args:
        payload (str): The payload for the rebalance transaction.

        Returns:
        dict: Rebalance transaction data.
        """
        logger.info(f"In - rebalance_transaction {payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get("rebalance_transaction"),
                          data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def update_user_instructions(self, payload):
        """
        Provide user instructions.

        Args:
        payload (str): The payload for user instructions.

        """
        logger.info(f"In - user_instructions {payload =}")
        payload['filled_quantity'] = payload['quantity']
        payload = json.dumps(payload)
        data = self._put(url=self.base_url,
                         endpoint=self.urls.get("user_instructions"),
                         data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_inputs(self, params, user_portfolio_id):
        """
        Get user inputs for a specific user portfolio.

        Args:
        params (dict): Additional parameters for the request.
        user_portfolio_id (str): The ID of the user portfolio.

        Returns:
        dict: User inputs data.
        """
        logger.info(f"In - user_inputs {params =}, {user_portfolio_id =}")
        data = self._get(url=self.base_url,
                         endpoint=f'{self.urls.get("user_inputs")}/{user_portfolio_id}',
                         params=params)
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolio_holdings(self, user_portfolio_id):
        """
        Get user holdings for a specific user portfolio.

        Args:
        user_portfolio_id (str): The ID of the user portfolio.
        Returns:
        dict: User holdings data.
        """
        logger.info(f"In - user_holdings {user_portfolio_id =}")
        data = self._get(url=self.base_url,
                         endpoint=f'{self.urls.get("user_portfolio_holdings")}/{user_portfolio_id}')
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_holdings(self, user_id, broker):
        """
        Get user holdings for a all user portfolios.

        Args:
        user_id (str): The ID of the user portfolio.
        broker (str): Broker of user

        Returns:
        dict: User holdings data.
        """
        logger.info(f"In - user_holdings {user_id = }, {broker = }")
        data = self._get(url=self.base_url,
                         endpoint=f'{self.urls.get("user_holdings")}/{user_id}',
                         params={"broker": broker})
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolio_by_id(self, user_portfolio_id):
        """
        Retrieves a user's portfolio data by the provided ID.

        Args:
        - user_portfolio_id (str): The ID of the user's portfolio.

        Returns:
        - dict: The data associated with the user's portfolio.
        """
        logger.info(f"In - user_holdings {user_portfolio_id =}")
        data = self._get(url=self.base_url,
                         endpoint=f'{self.urls.get("user_portfolios")}/{user_portfolio_id}')
        logger.info(f"{data =}")
        return data.get("data")

    def get_rebalance_orders(self, rebalance_type, user_portfolio_rebalance_id):
        """
        Fetches rebalance orders for a specific user portfolio rebalance.

        Args:
        - rebalance_type: Type of rebalance.
        - user_portfolio_rebalance_id: ID of the user's portfolio rebalance.

        Returns:
        - dict: Data related to rebalance orders.
        """
        logger.info(f"In - rebalance_orders {rebalance_type =}, {user_portfolio_rebalance_id =}")
        data = self._get(url=self.base_url,
                         endpoint=f'{self.urls.get("orders")}',
                         params={"type": rebalance_type,
                                 "user_portfolio_rebalance_id": user_portfolio_rebalance_id
                                 })
        logger.info(f"{data =}")
        return data.get("data")

    def update_user_portfolio_rebalance(self, payload, user_portfolio_rebalance_id):
        """
        Update a user's portfolio rebalance.

        Args:
        - payload (dict): The data payload to update the user's portfolio rebalance.
        - user_portfolio_rebalance_id (int): The ID of the user's portfolio rebalance to be updated.

        Returns:
        - dict: The updated data of the user's portfolio rebalance.
        """
        logger.info(f"In - update_user_portfolio_rebalance {payload =}, {user_portfolio_rebalance_id =}")
        data = self._put(url=self.base_url,
                         endpoint=f"{self.urls.get('update_user_portfolio_rebalance')}/{user_portfolio_rebalance_id}/",
                         data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def update_portfolio_transaction(self, payload, portfolio_rebalance_transaction_id):
        """
        Updates the portfolio transaction with the provided payload.

        Parameters:
        - payload (str): JSON-formatted payload containing information to update the portfolio transaction.
        - portfolio_rebalance_transaction_id (str): The ID of the portfolio rebalance transaction to be updated.

        Returns:
        str: The updated data from the portfolio transaction.

        Note:
        This method sends a PUT request to the specified endpoint to update the portfolio transaction.
        """
        logger.info(f"In - update_portfolio_transaction {payload =}, {portfolio_rebalance_transaction_id =}")
        data = self._put(url=self.base_url,
                         endpoint=f"{self.urls.get('update_portfolio_transaction')}/{portfolio_rebalance_transaction_id}",
                         data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def complete_rebalance_transaction(self, portfolio_rebalance_transaction_id, status=None):
        """
        Marks a portfolio rebalance as complete based on the provided data.

        Parameters:
        - item (RebalanceComplete): An instance of the RebalanceComplete class containing relevant information.
        - request (Request): An instance of the Request class representing the incoming request.

        Returns:
        dict:
        """
        logger.info(f"In - update_portfolio_transaction, {portfolio_rebalance_transaction_id =}")
        payload = {}
        if status:
            payload = json.dumps({
                "status": status
            })
        data = self._put(url=self.base_url,
                         endpoint=f"{self.urls.get('complete_rebalance_transaction')}/{portfolio_rebalance_transaction_id}",
                         data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def get_portfolio_rebalances(self, user_portfolio_id, current_state):
        """
        Retrieve portfolio rebalances data.

        This method retrieves portfolio rebalances data for a specific user portfolio and current state.

        Args:
            user_portfolio_id (int): The ID of the user portfolio.
            current_state (list): The current state of the portfolio.

        Returns:
            dict: Portfolio rebalances data.
        """
        logger.info(f"In - update_portfolio_transaction, {user_portfolio_id =}, {current_state =}")
        params = {
            'current_state': ','.join(current_state)
        }
        data = self._get(url=self.base_url,
                         endpoint=f"{self.urls.get('get_portfolio_rebalances')}/{user_portfolio_id}",
                         params=params)
        logger.info(f"{data =}")
        return data.get("data")

    def create_basket(self, basket_payload):
        """
        Creates a new basket by sending a POST request with the provided basket payload.

        This method sends a POST request to create a basket using the specified `basket_payload`.
        The response is logged and the data is returned.

        Args:
            basket_payload (json()): A dictionary containing the details for creating a new basket.
                It should include all necessary fields to create the basket, such as user ID, model ID,
                basket type, product type, and other relevant data.

        Returns:
            dict: A dictionary containing the response data, including the created basket details.
                It returns the value of the 'data' field from the response.

        Logs:
            Logs the request payload and the response data for debugging and traceability.
        """
        logger.info(f"In create_basket {basket_payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get('create_basket'),
                          data=basket_payload)
        logger.info(f"{data =}")
        return data.get('data')

    def update_order_instructions(self, payload):
        """
        Sends order instructions by updating the order details through a PUT request.

        This method takes the given `payload`, updates the filled quantity, converts it to a JSON string,
        and sends it via a PUT request to update the order instructions. The response is logged and returned.

        Args:
            payload (dict): A dictionary containing the order details, including the symbol,
                quantity, and other relevant order information. The 'filled_quantity' field is automatically
                set to the value of 'quantity' in the payload.

        Returns:
            dict: A dictionary containing the response data, which includes the updated order instructions.
                It returns the value of the 'data' field from the response.

        Logs:
            Logs the request payload and the response data for debugging and traceability.
        """
        logger.info(f"In - user_instructions {payload =}")
        payload['filled_quantity'] = payload['quantity']
        payload = json.dumps(payload)
        data = self._put(url=self.base_url,
                         endpoint=self.urls.get("order_instructions"),
                         data=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def create_basket_orders(self, basket_id, action: str):
        """
        Processes basket orders based on the specified action (retry or skip).

        Args:
            basket_id (int): The ID of the user's basket.
            action (str): The action to perform, either 'retry' or 'skip'.

        Returns:
            dict: The response data containing information about the processed orders.
        """
        logger.info(f"Processing basket orders for {basket_id =} with action '{action}'")
        endpoint = f"{self.urls.get(action)}/{basket_id}"
        data = self._post(url=self.base_url, endpoint=endpoint, data={})

        logger.info(f"Response data: {data =}")
        return data.get('data')

    def create_instructions(self, payload: str) -> list:
        """
        Sends a request to create order instructions based on the provided payload.

        Args:
            payload (str): The JSON string payload containing the instructions data.

        Returns:
            list: A list of newly created instructions data from the response.
        """
        logger.info(f"In create_instructions: {payload =}")

        endpoint = self.urls.get('create_order_instructions')
        response = self._post(
            url=self.base_url,
            endpoint=endpoint,
            data=payload)
        return response.get('data')

    def get_basket_details(self, user_id, current_state):
        """
        Fetches basket details for a given user based on the current state.

        Parameters:
            user_id (str): The user identifier.
            current_state (str): The current state of the basket (e.g., 'uninvested', 'invested').

        Returns:
            Optional[Dict]: The basket details if available, otherwise None.
        """
        logger.info(f"In basket_details {user_id =}, {current_state =}")
        endpoint = self.urls.get('basket_details')
        params = {
            'user_id': user_id,
            'current_state': current_state
        }
        response = self._get(url=self.base_url,
                             endpoint=endpoint,
                             params=params)

        return response.get('data')

    def get_broker_users_holdings(self, broker):
        logger.info(f"In broker_user_holdings {broker =}")
        endpoint = self.urls.get('broker_users_holdings').format(broker)
        response = self._get(url=self.base_url, endpoint=endpoint)
        return response.get('data')

    def create_user_portfolio_threshold(self, payload):
        """Create a *User Portfolio* threshold.

        Parameters
        ----------
        payload : dict | str
            JSON-serialisable dictionary (or raw JSON string) containing the
            following **required** keys.

            * **portfolio_type** (str) – One of the portfolio types accepted by
              the User-Portfolio service (e.g. ``USER_PORTFOLIO`` or ``BASKET``).
            * **portfolio_id** (str) – Unique identifier of the portfolio
              entity.
            * **side** (str) – Either ``LONG`` or ``SHORT``.
            * **threshold_type** (str) – Threshold category, e.g. ``PT`` or
              ``SL``.
            * **status** (str) – Initial status, typically ``ACTIVE``.

            **Optional** keys:

            * **target_pct** (Decimal | float) – Percent based trigger level.
            * **target_value** (Decimal | float) – Absolute money value trigger.
            * **source** (str) – Origin of the instruction (``user``, ``admin`` …).

        Returns
        -------
        dict
            A dictionary representing the newly-created threshold (same shape
            as the backend response ``data`` field).
        """
        logger.info(f"In create_user_portfolio_threshold {payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get("user_portfolio_thresholds"),
                          json=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolio_thresholds(self, params=None):
        """Retrieve *User Portfolio* thresholds.

        Parameters
        ----------
        params : dict | None, optional
            Query-string parameters used as filters; accepted keys mirror the
            columns of ``UserPortfolioThreshold`` (e.g. ``portfolio_type``,
            ``portfolio_id``, ``status`` …).  Passing ``None`` performs an
            unfiltered list retrieval.

        Returns
        -------
        list[dict]
            List of serialised thresholds ordered by ``-effective_from``.
        """
        logger.info(f"In get_user_portfolio_thresholds {params =}")
        data = self._get(url=self.base_url,
                         endpoint=self.urls.get("user_portfolio_thresholds"),
                         params=params)
        logger.info(f"{data =}")
        return data.get("data")

    def update_user_portfolio_threshold(self, payload):
        """Update an existing *User Portfolio* threshold.

        The request is forwarded verbatim to ``PUT /portfolio/thresholds``.

        Parameters
        ----------
        payload : dict | str
            Dictionary (or JSON string) that **must** include:

            * **id** (int) – Primary key of the threshold to update.

            Any of the following *optional* fields may also be supplied; only
            those present will be updated:

            * **target_pct** (Decimal | float)
            * **status** (str)
            * **source** (str)

        Returns
        -------
        dict
            The updated threshold as returned by the backend (``data`` field).
        """
        logger.info(f"In update_user_portfolio_threshold {payload =}")
        data = self._put(url=self.base_url,
                         endpoint=self.urls.get("user_portfolio_thresholds"),
                         json=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def create_holding_threshold(self, payload):
        """Create a *Holding* threshold.

        Parameters
        ----------
        payload : dict | str
            Data required by ``POST /holding/thresholds``. Mandatory keys:

            * **holding_type** (str)
            * **holding_id** (str)
            * **side** (str)
            * **threshold** (Decimal | float)
            * **effective_from** (datetime-iso-str)

            Optional: **source** (str)

        Returns
        -------
        dict
            Newly created Holding-threshold representation.
        """
        logger.info(f"In create_holding_threshold {payload =}")
        data = self._post(url=self.base_url,
                          endpoint=self.urls.get("holding_thresholds"),
                          json=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def get_holding_thresholds(self, params=None):
        """Retrieve *Holding* thresholds with optional filters.

        Parameters
        ----------
        params : dict | None, optional
            Filter parameters (``holding_type``, ``holding_id`` …). ``None``
            results in an unfiltered list.

        Returns
        -------
        list[dict]
            Serialised Holding-thresholds ordered by ``-effective_from``.
        """
        logger.info(f"In get_holding_thresholds {params =}")
        data = self._get(url=self.base_url,
                         endpoint=self.urls.get("holding_thresholds"),
                         params=params)
        logger.info(f"{data =}")
        return data.get("data")

    def update_holding_threshold(self, payload):
        """Update an existing *Holding* threshold.

        Parameters
        ----------
        payload : dict | str
            Must include the primary key ``id`` and any fields to be changed
            (e.g. ``threshold``, ``status`` or ``effective_to``).

        Returns
        -------
        dict
            Updated Holding-threshold object as returned by the backend.
        """
        logger.info(f"In update_holding_threshold {payload =}")
        data = self._put(url=self.base_url,
                         endpoint=self.urls.get("holding_thresholds"),
                         json=payload)
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolio_summary(self, user_id: str, broker: str, product_type: str):
        """
        Retrieves the summary of a user's portfolio for a given broker and product type.

        Args:
            user_id (str): ID of the user.
            broker (str): Broker of the user.
            product_type (str): Product type (e.g., 'mtf').

        Returns:
            dict: Portfolio summary data.
        """
        logger.info(f"In - user_portfolio_summary {user_id = }, {broker = }, {product_type = }")
        params = {
            "user_id": user_id,
            "broker": broker,
            "product_type": product_type
        }
        data = self._get(url=self.base_url, endpoint=self.urls.get("user_portfolio_summary"), params=params)
        logger.info(f"{data =}")
        return data.get("data")

    def create_user_portfolio(self, payload):
        """
        Create a User Portfolio.

        This method sends a POST request to create a new user portfolio
        using the provided payload. The payload must contain:

        - user_id (str): Unique user identifier.
        - name (str): Name of the user.
        - portfolio_id (str): Portfolio identifier.
        - status (str): Portfolio status (e.g., "active").
        - subscription_id (str): Subscription reference.
        - product_type (str): Product type (e.g., "fno").
        - broker (str): Broker associated with the user.
        - strategy (str): Strategy identifier (one_time/rebalance. Default: rebalance)

        Parameters
        ----------
        payload : dict
            JSON-serialisable dictionary for creating the user portfolio.

        Returns
        -------
        dict
            The newly created user portfolio (value of the 'data' field).
        """
        logger.info(f"In - create_user_portfolio {payload =}")

        data = self._post(
            url=self.base_url,
            endpoint=self.urls.get("create_user_portfolio"),
            json=payload
        )

        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolio(
        self,
        user_id=None,
        portfolio_id=None,
        broker=None,
        status=None,
        investment_status=None,
        subscription_id=None,
        product_type=None,
        strategy=None,
    ):
        """
        Fetch User Portfolio with optional filters.

        All parameters are optional and sent as query params.
        Any field left as None will be ignored.

        Parameters
        ----------
        user_id : str, optional
        portfolio_id : str, optional
        broker : str, optional
        status : str, optional
        investment_status : str, optional
        subscription_id : str, optional
        product_type : str, optional
        strategy : str, optional

        Returns
        -------
        dict or list
            Value of `data` in API response.
        """
        params = {
            "user_id": user_id,
            "portfolio_id": portfolio_id,
            "broker": broker,
            "status": status,
            "investment_status": investment_status,
            "subscription_id": subscription_id,
            "product_type": product_type,
            "strategy": strategy,
        }
        params = {k: v for k, v in params.items() if v is not None}

        logger.info(f"In - get_user_portfolio {params =}")

        data = self._get(
            url=self.base_url,
            endpoint=self.urls.get("get_user_portfolio"),
            params=params,
        )

        logger.info(f"{data =}")
        return data.get("data")

    def place_user_order(self, payload: dict):
        """
        Place a user order.

        This method maps to:
        POST /user-portfolio/userorder/place-order/

        Parameters
        ----------
        payload : dict
            Order placement payload containing:
            - user_id
            - broker_user_id
            - broker
            - product_type
            - symbol
            - broker_symbol
            - category
            - strategy
            - option_type
            - side
            - order_type
            - variety
            - quantity
            - price
            - recommendation_id
            - user_inputs

        Returns
        -------
        dict
            Placed order response data.
        """
        logger.info(f"In - place_user_order {payload =}")

        data = self._post(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_place"),
            json=payload
        )

        logger.info(f"{data =}")
        return data.get("data")

    def update_user_order(self, order_tag: str, payload: dict):
        """
        Update a user order.

        Maps to:
        PUT /user-portfolio/userorder/order/{id}/update/

        Parameters
        ----------
        order_tag : str
            Internal Order Tag (path param).
        payload : dict
            Order update payload containing:
            - order_id
            - status
            - filled_quantity
            - pending_quantity
            - cancelled_quantity
            - average_price
            - amount

        Returns
        -------
        dict
            Updated order data.
        """
        logger.info(
            f"In - update_user_order {order_tag =}, {payload =}"
        )

        endpoint = f"{self.urls.get('finn_user_order_update')}/{order_tag}/update/"

        data = self._put(
            url=self.base_url,
            endpoint=endpoint,
            json=payload
        )

        logger.info(f"{data =}")
        return data.get("data")

    def get_user_order_status(self, order_id):
        """
        Fetch the current status of one or more user orders.

        Backend always returns a list.
        Client normalises response shape:

        - int  -> dict
        - list -> list

        Maps to:
        GET /user-portfolio/userorder/order/status?order_id=1,2,3
        """
        is_single = not isinstance(order_id, (list, tuple, set))

        if is_single:
            order_id_param = str(order_id)
        else:
            order_id_param = ",".join(str(i) for i in order_id)

        logger.info(f"In - get_user_order_status {order_id_param =}")

        response = self._get(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_status"),
            params={"order_id": order_id_param},
        )
        data = response.get("data") or []
        logger.info(f"{data =}")
        if is_single:
            return data[0] if data else None
        return data

    def get_user_orders(
            self,
            product_type: str,
            user_id: str,
            from_date: str = None,
            to_date: str = None,
            status: str = None,
            symbol: str = None,
    ):
        """
        Fetch user orders with filters.

        Maps to:
        GET /user-portfolio/userorder/orders/

        Mandatory query params:
        - product_type
        - user_id

        Optional query params:
        - from_date (YYYY-MM-DD)
        - to_date (YYYY-MM-DD)
        - status
        - symbol

        Parameters
        ----------
        product_type : str
        user_id : str
        from_date : str, optional
        to_date : str, optional
        status : str, optional
        symbol : str, optional

        Returns
        -------
        list[dict]
            List of user orders.
        """
        params = {
            "product_type": product_type,
            "user_id": user_id,
            "from_date": from_date,
            "to_date": to_date,
            "status": status,
            "symbol": symbol,
        }
        params = {k: v for k, v in params.items() if v is not None}
        logger.info(f"In - get_user_orders {params =}")
        data = self._get(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_list"),
            params=params,
        )
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_portfolios(
            self,
            user_id: str,
            broker_user_id: str,
            broker: str,
            product_type: str,
            status: str = None,
            limit: int = None,
            offset: int = None,
    ):
        """
        Fetch user order portfolios with filters.

        Maps to:
        GET /user-portfolio/userorder/portfolio/

        Mandatory query params:
        - user_id
        - product_type
        - broker_user_id
        - broker

        Optional query params:
        - status
        - limit
        - offset

        Parameters
        ----------
        user_id : str
        product_type : str
        broker_user_id : str
        broker : str
        status : str, optional
        limit : int, optional
        offset : int, optional

        Returns
        -------
        dict
            List of user order portfolios.
        """
        params = {
            "user_id": user_id,
            "product_type": product_type,
            "broker_user_id": broker_user_id,
            "broker": broker,
            "status": status,
            "limit": limit,
            "offset": offset,
        }
        params = {k: v for k, v in params.items() if v is not None}
        logger.info(f"In - get_user_portfolios {params =}")
        data = self._get(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_portfolio"),
            params=params,
        )
        logger.info(f"{data =}")
        return data.get("data")

    def get_stop_loss_constituents(self, user_portfolio_id: int):
        """
        Fetch holdings with active stop loss thresholds and check if they have hit stop loss.

        Maps to:
        GET /finn/user-order-details?user_portfolio_id={user_portfolio_id}

        Parameters
        ----------
        user_portfolio_id : int
            The user portfolio ID to check

        Returns
        -------
        List[Dict]
            List of holdings that hit stop loss with format:
            {
                "symbol": str,
                "target_pct": float,
                "avg_buy_price": float,
                "stop_loss_price": float,
                "live_price": float
            }

        """
        logger.info(f"In - get_stop_loss {user_portfolio_id = }")
        details = self._get(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_details"),
            params={"user_portfolio_id": user_portfolio_id},
        )
        logger.info(f"{details = }")
        return details.get('data')


    def reconcile_user_order(self, payload: dict):
        """
        Reconcile a user order.

        Maps to:
        POST /user-portfolio/userorder/reconcile/

        Parameters
        ----------
        payload : dict
            Reconciliation payload containing:
            - user_id
            - broker_user_id
            - broker
            - product_type
            - symbol
            - broker_symbol
            - category
            - strategy
            - option_type
            - order_type
            - variety
            - quantity

        Returns
        -------
        dict
            Reconciled order data.
        """
        logger.info(f"In - reconcile_user_order {payload =}")
        data = self._post(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_reconcile"),
            json=payload,
        )
        logger.info(f"{data =}")
        return data.get("data")

    def get_user_thresholds(
            self,
            portfolio_type: str,
            status: str,
            source_id: str,
            portfolio_id: str = None,
            side: str = None,
            threshold_type: str = None,
    ):
        """
        Retrieve User thresholds with mandatory and optional filters.

        Maps to:
        GET /user-portfolio/alerts/portfolio/thresholds

        Required query params:
        - portfolio_type
        - status
        - source_id

        Optional query params:
        - portfolio_id
        - side
        - threshold_type

        Parameters
        ----------
        portfolio_type : str
            Portfolio entity type (e.g. 'finn_portfolio')
        status : str
            Threshold status (e.g. 'active')
        source_id : str
            Source identifier (user/admin/dealer ID)
        portfolio_id : str, optional
            Portfolio identifier
        side : str, optional
            Position side (long/short)
        threshold_type : str, optional
            Threshold type (profit_target/stop_loss)

        Returns
        -------
        list[dict]
            List of matching portfolio thresholds.
        """
        params = {
            "portfolio_type": portfolio_type,
            "status": status,
            "source_id": source_id,
            "portfolio_id": portfolio_id,
            "side": side,
            "threshold_type": threshold_type,
        }
        params = {k: v for k, v in params.items() if v is not None}
        logger.info(f"In - get_user_portfolio_thresholds_filtered {params =}")
        data = self._get(
            url=self.base_url,
            endpoint=self.urls.get("user_thresholds"),
            params=params,
        )
        logger.info(f"{data =}")
        return data.get("data")

    def move_orders_to_draft(self, order_ids):
        """
        Move eligible orders to DRAFT status.

        Only orders currently in WAITING state are updated.
        Others are ignored by the backend.

        Maps to:
        PUT /user-portfolio/userorder/orders/status/draft/?order_id=1,2,3

        Parameters
        ----------
        order_ids : list[str]
            Single order ID or list of order IDs.

        Returns
        -------
        dict
            Response data containing update summary.
            Example:
            {
                "success": true,
                "message": "Order status update processed",
                "data": {
                    "updated_to_draft": 3
                }
            }
        """
        if isinstance(order_ids, (list, tuple, set)):
            order_id_param = ",".join(str(i) for i in order_ids)
        else:
            order_id_param = str(order_ids)
        logger.info(f"In - move_orders_to_draft {order_id_param =}")
        response = self._put(
            url=self.base_url,
            endpoint=self.urls.get("finn_user_order_move_to_draft"),
            params={"order_id": order_id_param},
        )
        logger.info(f"{response =}")
        return response.get("data")

