"""

    AUTHOR:

        Khushal P Soonderji

    DATE:

        Thursday, 28th Nov., 2024

    OBJECTIVE:

        To provide a way to retrieve dates of important events like financial-results, stock-splits, fund-raising, etc.
        from NSE's portal.

        NOTE: This method involves web scraping. It is good for proof-of-concept development, but it is recommended that
        more professional data-sources be used when the product starts becoming mature.

    REFERENCES:

        N/A

    DOWNLOADS:

        N/A

"""


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   IMPORT                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")

# System-level activities:
import io

# My utils:
from utils_v2.string import json
from utils_v2.date_time import date_time

# NSE-related utils:
from utils_v2.nse.controllers.base import AsyncNSEBase
from utils_v2.nse.models.api_call import NSEApiResponse

# To make REST-ful API calls:
import httpx

# To work with date and time:
import datetime
import pytz

# To work with datatypes:
from typing import Any, List

# For asynchronous activities:
import asyncio


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                           MACROS / ONE-TIME INIT                                              ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                VARIABLES                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                  FUNCTIONS                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


class NSECorporateActions(AsyncNSEBase):

    # The types of corporate actions/entities:
    TYPE_EQUITIES = "equities"
    TYPE_MUTUAL_FUNDS = "mf"
    TYPE_DEBT = "debt"
    TYPE_SME = "sme"

    def __init__(
            self,
            http_client: httpx.AsyncClient,
            debug = True,
            debug_prefix = "NSE (C.Act.) | ",
            debug_only_errors = True
    ):

        # Pass on the initialization to the parent:
        super().__init__(
            base_url = r"https://www.nseindia.com/companies-listing/corporate-filings-actions",
            data_url = r"https://www.nseindia.com/api/corporates-corporateActions",
            http_client = http_client,
            debug = debug,
            debug_prefix = debug_prefix,
            debug_only_errors = debug_only_errors
        )

    async def get_data(
            self,
            action_type: str,
            from_date: datetime.datetime,
            to_date: datetime.datetime,
            return_raw: bool = False,
    ) -> NSEApiResponse:

        """
        To get the data of the corporate event calendar.
        :param action_type: The option between 'equities", "mf", "debt", and "sme".
        :param from_date: The starting date (inclusive) from which the events must be fetched.
        :param to_date: The ending date (inclusive) till which the events must be fetched.
        :param return_raw: Whether you want the raw JSON from NSE or you want it formatted.
        :return: The raw or formatted event calendar data in the 'data' field of the response model.
        """

        # Make the API call:
        api_response = await self.get(
            params = {
                "index": action_type,
                "from_date": from_date.strftime("%d-%m-%Y"),
                "to_date": to_date.strftime("%d-%m-%Y"),
            }
        )

        # If the API call was successful:
        if api_response.httpCode in [200]:
            api_response.success = True
            if return_raw: api_response.data = await api_response.get_json()
            else:
                try: api_response.data = self.format_data(
                    raw_json = await api_response.get_json(),
                    timestamp = date_time.get_current_utc_date_time(as_string = True),
                    raise_exception = True
                )
                except Exception as exception:
                    api_response.exception = exception
                    api_response.success = False

        # Done here:
        return api_response

    def format_data(
            self,
            raw_json: List[dict],
            timestamp: datetime.datetime = None,
            raise_exception: bool = False
    ) -> List[dict] | None:

        """
        We format the data here to be able to retrieve it properly later.
        :param raw_json: The raw data as scraped from NSE.
        :param timestamp: The timestamp at which the data was scraped. This shall be useful for data retrieval from the
            database, later.
        :param raise_exception: If set to True, any exception will be propagated. If set to False, any exception will be
            suppressed internally.
        :return: The formatted data if successful, else None.
        """

        # Can't do anything if the chain itself is null:
        if raw_json is None: return raw_json

        # Start by assuming failure:
        formatted_data = None

        # Ensure that we've got a proper timestamp:
        if timestamp is None: timestamp = date_time.get_current_utc_date_time(as_string = False)

        try:

            # Format the data:
            formatted_data = []
            for action in raw_json:
                formatted_data.append({
                    "scrapeTs": timestamp,
                    "symbol": action["symbol"],
                    "company": action["comp"],
                    "isin": action["isin"],
                    "segment": action["series"],
                    "ind": action["ind"],
                    "faceVal": float(action["faceVal"]),
                    "action": action["subject"],
                    "exDate": self.parse_datetime_string(action["exDate"], ["%d-%b-%Y"]),
                    "recDate": self.parse_datetime_string(action["recDate"]),
                    "bcStartDate": self.parse_datetime_string(action["bcStartDate"]),
                    "bcEndDate": self.parse_datetime_string(action["bcEndDate"]),
                    "ndStartDate": self.parse_datetime_string(action["ndStartDate"]),
                    "ndEndDate": self.parse_datetime_string(action["ndEndDate"]),
                    "caBroadcastDate": self.parse_datetime_string(action["caBroadcastDate"])
                })

        # If something goes wrong:
        except Exception as exception:
            formatted_data = None
            if raise_exception: raise

        # Done here:
        return formatted_data


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                               MAIN PROGRAM                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


if __name__ == "__main__":

    async def main():

        # Create an HTTP client:
        test_client = httpx.AsyncClient(
            limits = httpx.Limits(
                max_connections = 100,  # ............ Maximum number of connections allowed in the pool.
                max_keepalive_connections = 50,  # ... Maximum number of connections that can be kept alive.
            ),
            timeout = httpx.Timeout(
                pool = 120.0,  # .... Time to wait for a free connection from the pool.
                connect = 2.5,  # ... Time to wait for establishing a connection to the server.
                write = 10.0,  # .... Time to wait for sending data.
                read = 2.5  # ....... Time to wait for receiving data.
            )
        )

        # Create an instance of the scraper, and refresh its cookies:
        my_nse = NSECorporateActions(http_client = test_client)

        # Get and show the data:
        to_date = date_time.get_current_ist_date_time()
        from_date = to_date - datetime.timedelta(days = 365)
        print("FROM :", from_date)
        print("TO   :", to_date)
        api_response = await my_nse.get_data(
            action_type = NSECorporateActions.TYPE_EQUITIES,
            from_date = from_date,
            to_date = to_date,
            return_raw = False
        )
        print("SUMMARY:", api_response.to_markdown(), "\n---\n\n")
        if api_response.success: print("CORPORATE ACTIONS:", json.to_string(api_response.data, default = str))

    asyncio.run(main())
