"""
    The MIT License (MIT)

    Copyright (c) 2023 pkjmesra

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

"""
import datetime
import json
import re

import pandas as pd
import requests
from bs4 import BeautifulSoup

from PKNSETools.morningstartools.error import no_site_error, not_200_response
from PKNSETools.morningstartools.search import search_funds, token_investment_strategy
from PKNSETools.morningstartools.security import Security
from PKDevTools.classes.Utils import random_user_agent


class Funds(Security):
    """
    Main class to access data about funds and etf, inherit from Security class

    Args:
        term (str): text to find a fund, can be a name, part of a name or the isin of the funds
        country (str) : text for code ISO 3166-1 alpha-2 of country, should be '' for etf
        pageSize (int): number of funds to return
        itemRange (int) : index of funds to return (must be inferior to PageSize)
        proxies = (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"}

    Examples:
        >>> Funds('0P0000712R', "ca", 9, 0)
        >>> Funds('bond', "uk", 25, 2)

    Raises:
        TypeError: raised whenever the parameter type is not the type expected
        ValueError : raised whenever the parameter is not valid or no fund found

    """

    def __init__(self, term = None, country: str = "", pageSize : int =1, itemRange: int = 0, filters: dict = {}, proxies: dict = {}):
        
        super().__init__(term=term,asset_type='fund',country=country,pageSize=pageSize,itemRange=itemRange,filters=filters,proxies=proxies)
        
        

    def allocationMap(self):
        """    
        This function retrieves the asset allocation of the funds, index and category.

        Returns:
            dict with allocation map

        Examples:
            >>> Funds("myria", "fr").allocationMap()

        """
        return self.GetData("process/asset/v2")

    def allocationWeighting(self):
        """
        This function retrieves the Growth/Blend/Value and market capitalizaton allocation size of the funds.

        Returns:
            dict with allocation

        Examples:
            >>> Funds("myria", "fr").allocationWeighting()
        
        """
        return self.GetData("process/weighting")

    def analystRating(self):
        """
        This function retrieves the rating of the funds

        Returns:
            list of dict with ratings

            >>> Funds("RMAGX", "us").analystRating()

        """
        

        return self.GetData("parent/analystRating")

    def analystRatingTopFunds(self):
        """
        This function retrieves the rating Top funds

        Returns:
            dict with ratings

            >>> Funds("RMAGX", "us").analystRatingTopFunds()
            
        """
        
        return self.GetData("parent/analystRating/topfunds")


    def analystRatingTopFundsUpDown(self):
        """
        This function retrieves the rating funds Up Down

        Returns:
            dict with ratings

            >>> Funds("RMAGX", "us").analystRatingTopFundsUpDown()
            
        """

        return self.GetData("parent/analystRating/topfundsUpDown")


    def AnnualPerformance(self, cat):
        """
        This function retrieves the annual performance of the funds, index, category or the annual rank of the funds.

        Args:
            cat (str) : possible values are category, funds, index, rank    
        Returns:
            dict annual performance or rank

        Raises:
            ValueError : raised whenever parameter cat is not category, funds, index, or rank

        Examples:
            >>> Funds("myria", "fr").AnnualPerformance("category")
            >>> Funds("myria", "fr").AnnualPerformance("funds")
            >>> Funds("myria", "fr").AnnualPerformance("index")
            >>> Funds("myria", "fr").AnnualPerformance("rank")

        """
        no_site_error(self.code,self.name,self.country,self.site)

        cat_row = {'funds' : 0,'category' : 1, 'index' : 2, 'rank' : 3}
        if cat not in cat_row:
            raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}")

        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        #label are dates
        regex = re.compile('.*heading number')
        label_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex})
        #funds performance, category performance, index performance, rank in category
        regex = re.compile('.*value number')
        #values
        value_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex})

        regex = re.compile('-|\/')
        #first col is nothing
        for i in range(1, len(label_list)):
            label = label_list[i].text
            #if today

            if regex.search(label):
                label = 'current'
            #add category to label
            if label:
                
                result[f'{cat}_annual_performance_{label}'] = value_list[i+(cat_row[cat])*(len(label_list)-1)-1].text

        return result

    def benchmark(self):
        """
        This function retrieves the benchmark name of the funds.

        Returns:
            str benchmark name

        Examples:
            >>> Funds("myria", "fr").benchmark()

        """
        return self.referenceIndex("benchmark")

    def carbonMetrics(self):
        """
        This function retrieves the carbon metrics of the funds.

        Returns:
            dict with carbon metrics

        Examples:
            >>> Funds("myria", "fr").carbonMetrics()

        """

        return self.GetData("esg/carbonMetrics")

    def category(self):
        """
        This function retrieves the category name of the funds.

        Returns:
            str category name

        Examples:
            >>> Funds("myria", "fr").category()

        """
        return self.referenceIndex("category")

    def categoryAnnualPerformance(self):
        """
        This function retrieves the annual performance of the category.
  
        Returns:
            dict annual performance of the category


        Examples:
            >>> Funds("myria", "fr").categoryAnnualPerformance()

        """
        return self.AnnualPerformance('category')

    def categoryCumulativePerformance(self):
        """
        This function retrieves the cumulative performance of the category.
  
        Returns:
            dict cumulative performance of the category

        Examples:
            >>> Funds("myria", "fr").categoryCumulativePerformance()

        """
        
        return self.CumulativePerformance('category')

    def contact(self):
        """
        This function retrieves information about the asset manager.
  
        Returns:
            dict contact

        Examples:
            >>> Funds("myria", "fr").contact()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        #page 4 - info about found
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=4"
        response = requests.get(url, headers=headers,proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        #label
        label_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col1 label"})
        #value
        value_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col2 value number"})
        for i in range(0, len(value_list)):
            label = label_list[i].text
            
            result[label] = value_list[i].text

        return result

    def costIllustration(self):
        """
        This function retrieves the cost of the funds.
  
        Returns:
            dict cost of funds

        Examples:
            >>> Funds("FOUSA00E5P", "us").costIllustration()

        """
        return self.GetData("price/costIllustration")

    def couponRange(self):
        """
        This function retrieves the coupon of the funds, index and category.
  
        Returns:
            dict coupon

        Examples:
            >>> Funds("rmagx", "us").couponRange()

        """
        return self.GetData("process/couponRange")


    def creditQuality(self):
        """
        This function retrieves the credit notation of the funds, index and category.
  
        Returns:
            dict credit notation

        Examples:
            >>> Funds("rmagx", "us").creditQuality()

        """
        return self.GetData("portfolio/creditQuality")

    def dataPoint(self, field, currency ='INR'):
        """
        This function retrieves infos about funds such as name, performance, risk metrics...

        Args:
        field (str or list) : field to find
        currency (str) : currency in 3 letters

        Returns:
            list of dict funds infos

        Example:
            >>> Funds("myria", "fr").dataPoint(['largestSector', 'Name', 'ongoingCharge'])
            >>> Funds("myria", "fr").dataPoint('SharpeM36')

        """
        return search_funds(self.code, field,self.country,10,currency, proxies=self.proxies)

    def distribution(self, period = "annual"):
        """
        This function retrieves the coupon distributed by the funds.

        Args:
            period (str) : annual or latest
        
        Raises:
            ValueError whenever the pariod parameter is not annual or latest

        Returns:
            dict distribution of coupon

        Example:
            >>> Funds("rmagx", "us").distribution("annual")
            
        """


        period_choice = ["annual", "latest"]
        if period not in period_choice:
            raise ValueError(f'period parameter can only take one of the values: {", ".join(period_choice)}')

        return self.GetData(f"distribution/{period}")



    def CumulativePerformance(self, cat):
        """
        This function retrieves the cumulative performance of funds, index and category.

        Args:
            cat (str) : possible values are category, funds, index

        Returns:
            dict cumulative performance

        Examples:
            >>> Funds("myria", "fr").CumulativePerformance("funds")
            >>> Funds("myria", "fr").CumulativePerformance("index")
            >>> Funds("myria", "fr").CumulativePerformance("category")

        """
        no_site_error(self.code,self.name,self.country,self.site)
        cat_row = {'funds' : 2,'category' : 3, 'index' : 4}
        if cat not in cat_row:
            raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}")
        result = {}

        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        cumulative_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text
        result['cumulative_performance_date'] = cumulative_performance_date 
        #days
        regex = re.compile('.*label')
        label_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex})

        #cumulative performance cat
        regex = re.compile(f'.*col{str(cat_row[cat])} value number')
        value_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex})
        #loop on label
        for i in range(0, len(label_list)):
            #label
            label = label_list[i].text
            #perf funds
            result[f'{cat}_cumulative_performance_{label}'] = re.sub('[^0-9,-\.]','',value_list[i].text)
            
        return result

    def equityStyle(self):
        """
        This function retrieves the equity style of the funds and category.

        Returns:
            dict equity style

        Examples:
            >>> Funds("myria", "fr").equityStyle()

        """
        return self.GetData("process/stockStyle/v2")
    
    def equityStyleBoxHistory(self):
        """
        This function retrieves the equity style history of the funds

        Returns:
            dict equity style history

        Examples:
            >>> Funds("myria", "fr").equityStyleBoxHistory()

        """
        return self.GetData("process/equityStyleBoxHistory")

    def esgData(self):
        """
        This function retrieves ESG data of the funds and category

        Returns:
            dict ESG data

        Examples:
            >>> Funds("myria", "fr").esgData()

        """

        return self.GetData("esg/v1")

    def factorProfile(self):
        """
        This function retrieves the factor profile of the funds, index and category

        Returns:
            dict factor profile

        Examples:
            >>> Funds("myria", "fr").factorProfile()

        """
        return self.GetData("factorProfile")

    def feeLevel(self):
        """        
        This function retrieves the fees of the fund compare to its category.

        Returns:
            dict fees

        Examples:
            >>> Funds("rmagx", "us").feeLevel()

        """
        return self.GetData("price/feeLevel")
    
    def feeMifid(self,currency="EUR"):
        """        
        This function retrieves the fees of the fund.

        Returns:
            dict fees

        Examples:
            >>> Funds("myria", "fr").feeMifid()

        """
        return self.ltData("Mifid",currency=currency)


    def fees(self):
        """        
        This function retrieves the fees of the fund (by scraping pages);

        Returns:
            dict fees

        Examples:
            >>> Funds("myria", "fr").fees()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=5"
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        if soup.find(id='managementFeesDiv') == None:
            return {}
        #label
        label_list =soup.find(id='managementFeesDiv').find_all('td', {"class": "label"})
        #value
        value_list = soup.find(id='managementFeesDiv').find_all('td', {"class": "value number"}) + soup.find(id='managementFeesDiv').find_all('td', {"class": "value number jdpa"})
        for i in range(0, len(value_list)):
            label = label_list[i].text
            result[label] = re.sub('(\\n +)|(\\n)','',value_list[i].text)

        return result

    def financialMetrics(self):
        """        
        This function retrieves the final metrics of the funds and category.

        Returns:
            dict financial metrics

        Examples:
            >>> Funds("rmagx", "us").financialMetrics()

        """
        return self.GetData("process/financialMetrics")


    def fixedIncomeStyle(self):
        """        
        This function retrieves the fixed income style of the funds and category.

        Returns:
            dict fixed income style

        Examples:
            >>> Funds("rmagx", "us").fixedIncomeStyle()

        """

        return self.GetData("process/fixedIncomeStyle")

    def fixedincomeStyleBoxHistory(self):
        """        
        This function retrieves the fixed income style history of the funds.

        Returns:
            dict fixed income style

        Examples:
            >>> Funds("rmagx", "us").fixedincomeStyleBoxHistory()

        """
        return self.GetData("process/fixedincomeStyleBoxHistory")

    def fundsAnnualPerformance(self):
        """        
        This function retrieves the annual performance of the funds.

        Returns:
            dict funds annual performance 

        Examples:
            >>> Funds("myria", "fr").fundsAnnualPerformance()

        """
        return self.AnnualPerformance('funds')

    def fundsAnnualRank(self):
        """        
        This function retrieves the annual rank of the funds in percentile.

        Returns:
            dict funds annual rank

        Examples:
            >>> Funds("myria", "fr").fundsAnnualRank()

        """
        return self.AnnualPerformance('rank')

    def fundsCumulativePerformance(self):
        """        
        This function retrieves the cumulative performance of the funds.

        Returns:
            dict funds cumulative performance

        Examples:
            >>> Funds("myria", "fr").fundsCumulativePerformance()

        """
        return self.CumulativePerformance('funds')

    def fundsQuarterlyPerformance(self):
        """        
        This function retrieves the quarterly performance of the funds.

        Returns:
            dict funds quarterly performance

        Examples:
            >>> Funds("myria", "fr").fundsCumulativePerformance()

        """
        no_site_error(self.code,self.name,self.country,self.site)
        result = {}
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #page 1 - performance
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1"
        
        response = requests.get(url, headers=headers, proxies=self.proxies)
        not_200_response(url,response)
        soup = BeautifulSoup(response.text, 'html.parser')
        quarterly_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text
        result['quarterly_performance_date'] = quarterly_performance_date 

        #quarter label
        regex = re.compile('.*heading number')
        quarter_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #year label
        regex = re.compile('.*label')
        year_list =soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #1st Quarter 
        regex = re.compile('.*col2 value number')
        quarter_1_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #2nd Quarter
        regex = re.compile('.*col3 value number')
        quarter_2_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #3rd Quarter
        regex = re.compile('.*col4 value number')
        quarter_3_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #4th Quarter
        regex = re.compile('.*col5 value number')
        quarter_4_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex})
        #loop on year
        for i in range(0, len(year_list)):
            label = 'performance_%s_' % (year_list[i].text)
            result[label + 'quarter_1'] = quarter_1_list[i].text
            result[label + 'quarter_2'] = quarter_2_list[i].text
            result[label + 'quarter_3'] = quarter_3_list[i].text
            result[label + 'quarter_4'] = quarter_4_list[i].text
        return result

    def graphData(self):
        """        
        This function retrieves historical data of the funds.

        Returns:
            dict historical data

        Examples:
            >>> Funds("myria", "fr").graphData()
   
        """

        return self.GetData("parent/graphData")

    def historicalData(self):
        """
        This function retrieves the historical price of the funds, index and category

        Returns:
            dict with historical data

        Examples:
            >>> Funds("myria", "fr").historicalData()

        """
        return self.GetData("performance/v3", url_suffixe='')



    def historicalExpenses(self):
        """        
        This function retrieves historical expenses of the funds.

        Returns:
            dict historical expenses

        Examples:
            >>> Funds("rmagx", "us").historicalExpenses()

        """
        if self.asset_type == 'etf':
            return {}
        return self.GetData("price/historicalExpenses")

    def holdings(self, holdingType: str = 'all'):
        """        
        This function retrieves holdings of the funds.

        Args:
            holdingType (str) : paramater to select the kind of holdings; all, bond, equity or other

        Returns:
            pandas DataFrame holdings

        Raises:
            ValueError whenever the parameter is not all, bond, equity or other

        Examples:
            >>> Funds("myria", "fr").holdings("all")
            >>> Funds("myria", "fr").holdings("bond")
            >>> Funds("myria", "fr").holdings("equity")
            >>> Funds("myria", "fr").holdings("other")

        """
        holdingType_to_holdingPage = {"all" : "all", "bond" : "boldHoldingPage","equity" : "equityHoldingPage", "other": "otherHoldingPage"}
        if holdingType not in holdingType_to_holdingPage:
            raise ValueError(f'parameter holdingType must take one of the following value : {", ".join(holdingType_to_holdingPage.keys())}  ')
    
        if holdingType == "all":
            return pd.DataFrame(self.position()["equityHoldingPage"]["holdingList"] + 
            self.position()["boldHoldingPage"]["holdingList"] + self.position()["otherHoldingPage"]["holdingList"] )
        else:
           return pd.DataFrame(self.position()[holdingType_to_holdingPage[holdingType]]["holdingList"])

    def indexAnnualPerformance(self):
        """
        This function retrieves the annual performance of the index.
  
        Returns:
            dict annual performance of the index


        Examples:
            >>> Funds("myria", "fr").indexAnnualPerformance()

        """
        return self.AnnualPerformance('index')

    def indexCumulativePerformance(self):
        """
        This function retrieves the cumulative performance of the index.
  
        Returns:
            dict cumulative performance of the index


        Examples:
            >>> Funds("myria", "fr").indexCumulativePerformance()

        """
        
        return self.CumulativePerformance('index')

    def investmentStrategy(self):
        """
        This function retrieves the investment strategy.
  
        Returns:
            dict investment strategy

        Examples:
            >>> Funds("LU0823421689").investmentStrategy()

        """
        bearer_token = token_investment_strategy()
        #header with bearer token
        headers = {
                    'authorization': f'Bearer {bearer_token}',
                    }
        return self.GetData("morningstarTake/investmentStrategy",headers=headers)
        
    def investmentLookup(self,currency="EUR"):
        """        
        This function gives details about fund investment.

        Returns:
            dict fund investment

        Examples:
            >>> Funds("myria", "fr").investmentLookup()

        """
        return self.ltData("investmentTypeLookup",currency=currency)
    
    def marketCapitalization(self):
        """
        This function retrieves the marketCapitalization breakdown of the funds, category and index.
  
        Returns:
            dict market capitalization

        Examples:
            >>> Funds("myria", "fr").marketCapitalization()

        """
        return self.GetData("process/marketCap")


    def maturitySchedule(self):
        """
        This function retrieves the maturity breakdown of the funds and category.
  
        Returns:
            dict maturity

        Examples:
            >>> Funds("rmagx", "us").maturitySchedule()

        """
        return self.GetData("process/maturitySchedule")


    def maxDrawDown(self, year = 3):
        """
        This function retrieves the max drawdown of the funds, index and category.
  
        Args:
            year (int) : period of calculation in year

        Returns:
            dict max drawdown

        Raises:
            TypeError whenever the parameter year is not an integer

        Examples:
            >>> Funds("myria", "fr").maxDrawDown()

        """

        if not isinstance(year, int):
            raise TypeError('year parameter should be an integer')


        return self.GetData("performance/marketVolatilityMeasure", params = {"year": year})

    def morningstarAnalyst(self):
        """
        This function retrieves the raiting of MorningStar analyst.
  
        Returns:
            dict rating

        Examples:
            >>> Funds("rmagx", "us").morningstarAnalyst()
   
        """

        return self.GetData("morningstarAnalyst")

    def multiLevelFixedIncomeData(self, primary = "superEffectiveDuration", secondary = "superSector.weight"):
        """
        This function retrieves the exposures of fixed income

        Args:
            primary (str) : can be superEffectiveDuration, superYieldToWorst, creditQuality
            secondary (str) : can be superSector.weight, region.weight, creditQuality.weight

        Returns:
            dict exposures

        Raises:
            ValueError whenever the primary and seconday paramater is not a value expected

        Examples:
            >>> Funds("rmagx", "us").multiLevelFixedIncomeData()

        """

        primary_choice = ["superEffectiveDuration","superYieldToWorst", "creditQuality"]
        secondary_choice = ["superSector.weight", "region.weight", "creditQuality.weight"]

        if primary not in primary_choice:
            raise ValueError(f'primary parameter can only take one of the values: {", ".join(primary_choice)}')
        
        if secondary not in secondary_choice:
            raise ValueError(f'secondary parameter can only take one of the values: {", ".join(secondary_choice)}')

        if primary == "creditQuality" and secondary == "creditQuality.weight":
            raise ValueError(f'primary and secondary parameters cannot be both credit quality')

        return self.GetData("multiLevelFixedIncomeData", params = {"primary": primary,"secondary": secondary})

    def nav(self,start_date,end_date,frequency="daily"):
        """
        This function retrieves the NAV of the funds

        Args:
            start_date (datetime) : start date to get nav
            end_date (datetime) : end date to get nav
            frequency (str) : can be daily, weekly, monthly

        Returns:
            list of dict with nav

            >>> Funds("RMAGX", "us").nav(datetime.datetime.today()- datetime.timedelta(30),datetime.datetime.today())

        Raises:
            TypeError: raised whenever the parameter type is not the type expected
            ValueError : raised whenever the parameter is not valid or no funds found

        """

        return self.TimeSeries(["nav","totalReturn"],
                                start_date=start_date,end_date=end_date,frequency=frequency)
            
        



    def objectiveInvestment(self):
        """        
        This function retrieves the objective of investment of the fund (by scraping pages);

        Returns:
            str objective investment

        Examples:
            >>> Funds("myria", "fr").objectiveInvestment()

        """
        
        no_site_error(self.code,self.name,self.country,self.site)
        #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #Page 1 - overview
        #url page overview
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}"
        #get HTML page overview
        response = requests.get(url, headers=headers, proxies=self.proxies)
        #if page not found

        not_200_response(url,response)
        #html page as soup
        soup = BeautifulSoup(response.text, 'html.parser')
        #investment objective funds
        return soup.find(id='overviewObjectiveDiv').find('td', {"class": "value text"}).text

    def otherFee(self):
        """
        This function retrieves the other fee of the etf

        Returns:
            dict fees

        Examples:
            >>> Funds("American Century Foc Dynmc Gr ETF").otherFee()
            
            {'expenseWaiver': False, 'expenseReimbursement': None, 'expirationDate': None, 'expenseWaivers': None}
                    
        """
        return self.GetData("price/otherFee")


    def ownershipZone(self):
        """        
        This function retrieves ownershipZone of the funds, index and category.

        Returns:
            dict ownershipZone

        Examples:
            >>> Funds("myria", "fr").ownershipZone()

        """

        return self.GetData("process/ownershipZone")

    def parentMstarRating(self):

        """
        This function retrieves the raiting of parent by MorningStar analyst.
  
        Returns:
            list of dict rating

        Examples:
            >>> Funds("rmagx", "us").parentMstarRating()

        """

        return self.GetData("parent/parentMstarRating")

    def parentSummary(self):
        """
        This function retrieves info about the parent.
  
        Returns:
            dict parent info

        Examples:
            >>> Funds("rmagx", "us").parentSummary()

        """
        return self.GetData("parent/parentSummary")

    def people(self):
        """
        This function retrieves info about people who works in the company.
  
        Returns:
            dict people info

        Examples:
            >>> Funds("rmagx", "us").people()

        """
        return self.GetData("people")

    def position(self):
        """
        This function retrieves the hodings of the funds.
  
        Returns:
            dict holdings

        Examples:
            >>> Funds("myria", "fr").position()
  
        """

        return self.GetData("portfolio/holding/v2", params = {"premiumNum" : 10000, "freeNum" : 10000})

    def proxyVotingManagement(self):
        """
        This function retrieves the vote of management.
  
        Returns:
            dict vote

        Examples:
            >>> Funds("rmagx", "us").proxyVotingManagement()

        """
        return self.GetData("people/proxyVoting/management")
    

    def proxyVotingShareHolder(self):
        """
        This function retrieves the vote of shareholders.
  
        Returns:
            dict vote

        Examples:
            >>> Funds("rmagx", "us").proxyVotingShareHolder()

        """
        return self.GetData("people/proxyVoting/shareHolder")

    def productInvolvement(self):
        """
        This function retrieves the involvement of the funds
  
        Returns:
            dict involvement

        Examples:
            >>> Funds("myria", "fr").proxyVotingShareHolder()

        """

        return self.GetData("esg/productInvolvement")


    def referenceIndex(self, index):
        """
        This function retrieves the name of the category or the benchmark

        Args:
            index (str) : possible values are benchmark, category     

        Returns:
            str category or benchmark

        Raises:
            ValueErrror whenever the index parameter is not category of benchmark

        Examples:
            >>> Funds("myria", "fr").referenceIndex("category")
            >>> Funds("myria", "fr").referenceIndex("benchmark")

        """
        no_site_error(self.code,self.name,self.country,self.site)

        index_row = {'benchmark' : 0,'category' : 1}
        if index not in index_row:
            raise ValueError(f"index parameter must take one of the following value : { ', '.join(index_row.keys())}")

                #headers random agent
        headers = {'user-agent' : random_user_agent()}
        #Page 1 - overview
        #url page overview
        url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}"
        #get HTML page overview
        response = requests.get(url, headers=headers, proxies=self.proxies)
        #if page not found
        
        not_200_response(url,response)

        #html page as soup
        soup = BeautifulSoup(response.text, 'html.parser')
        benchmark_soup = soup.find(id='overviewBenchmarkDiv2Cols').find_all('td', {"class": "value text"})
        return benchmark_soup[index_row[index]].text

    def regionalSector(self):
        """
        This function retrieves the breakdown of the funds, category and index by region
  
        Returns:
            dict regional breakdown

        Examples:
            >>> Funds("myria", "fr").regionalSector()

        """
        return self.GetData("portfolio/regionalSector")

    def regionalSectorIncludeCountries(self):
        """
        This function retrieves the breakdown of the funds, category and index by region and country
  
        Returns:
            dict regional breakdown

        Examples:
            >>> Funds("myria", "fr").regionalSectorIncludeCountries()

        """
        return self.GetData("portfolio/regionalSectorIncludeCountries")



    def riskReturnScatterplot(self):
        """
        This function retrieves the return and standard deviation of the funds and category
  
        Returns:
            dict risk return

        Examples:
            >>> Funds("rmagx", "us").riskReturnScatterplot()

        """
        return self.GetData("performance/riskReturnScatterplot")

    def riskReturnSummary(self):
        """
        This function retrieves the return and risk summary of the funds compare to the category
  
        Returns:
            dict risk return

        Examples:
            >>> Funds("rmagx", "us").riskReturnSummary()

        """

        return self.GetData("performance/riskReturnSummary")

    def riskVolatility(self):
        """
        This function retrieves the alpha, beta, R², volatility and Sharpe ratio of the funds, category and index.

        Returns:
            dict econometrics

        Examples:
            >>> Funds("rmagx", "us").riskVolatility()

        """
        return self.GetData("performance/riskVolatility")

    def salesFees(self):
        """
        This function retrieves the sales fees of the funds

        Returns:
            dict fees

        Examples:
            >>> Funds("myria", "fr").salesFees()
        
        """
        if self.asset_type == 'etf':
            return {}
        return self.GetData("price/salesFees")

    def sector(self):
        """
        This function retrieves the sector breakdown of the funds, category and index
  
        Returns:
            dict sector breakdown

        Examples:
            >>> Funds("myria", "fr").sector()

        """
        return self.GetData("portfolio/v2/sector")
    
    def snapshot(self,currency="EUR"):
        """        
        This function returns a snapshot of the fund and asset manager.

        Returns:
            dict snapshot fund and asset manager

        Examples:
            >>> Funds("myria", "fr").snapshot()

        """
        return self.ltData("MFsnapshot",currency=currency)

    def starRatingFundAsc(self):
        """
        This function retrieves the MorningStar rating of the funds of the company by ascending order
  
        Returns:
            dict rating

        Examples:
            >>> Funds("myria", "fr").starRatingFundAsc()
 
        """
        
        return self.GetData("parent/mstarRating/StarRatingFundAsc")

    def starRatingFundDesc(self):
        """
        This function retrieves the MorningStar rating of the funds of the company by descending order
  
        Returns:
            dict rating

        Examples:
            >>> Funds("myria", "fr").starRatingFundDesc()

        """
        
        return self.GetData("parent/mstarRating/StarRatingFundDesc")

    def sustainability(self,currency="EUR"):
        """        
        This function retrieves the sustainability data of the fund.

        Returns:
            dict sustanability data

        Examples:
            >>> Funds("myria", "fr").sustainability()

        """
        return self.ltData("sustainability",currency=currency)
    
    def taxes(self):
        """
        This function retrieves the other fee of the etf

        Returns:
            dict taxes

        Examples:
            >>> Funds("American Century Foc Dynmc Gr ETF").taxes()

        """
        return self.GetData("price/taxes")

    def trailingReturn(self, duration ='daily'):
        """
        This function retrieves the trailing return of the funds of the company.

        Args:
        duration (str) : frequency of return can be daily, monthly or quarterly
        
        Returns:
            dict trailing return

        Raises:
            ValueError whenever the parameter duration is not daily, monthly or quarterly

        Example:
            >>> Funds("myria", "fr").trailingReturn("daily")

        """

        duration_choice = ["daily", "monthly", "quarterly"]
        if duration not in duration_choice:
            raise ValueError(f'duration parameter can only take one of the values: {", ".join(duration_choice)}')


        return self.GetData("trailingReturn/v2",{"duration" : duration})