class YFException(Exception):
    def __init__(self, description=""):
        super().__init__(description)


class YFDataException(YFException):
    pass


class YFNotImplementedError(NotImplementedError):
    def __init__(self, method_name):
        super().__init__(f"Have not implemented fetching '{method_name}' from Yahoo API")


class YFTickerMissingError(YFException):
    """Expected ticker data is missing.

    By default the message speculates the ticker is delisted, because Yahoo
    rarely explains the absence. Pass possibly_delisted=False when the real
    cause is known, so the message does not mislead.
    """
    def __init__(self, ticker, rationale, possibly_delisted=True):
        prefix = "possibly delisted; " if possibly_delisted else ""
        super().__init__(f"${ticker}: {prefix}{rationale}")
        self.rationale = rationale
        self.ticker = ticker


class YFTzMissingError(YFTickerMissingError):
    def __init__(self, ticker):
        super().__init__(ticker, "no timezone found")


class YFPricesMissingError(YFTickerMissingError):
    """Yahoo returned no price data for the requested range/interval.

    When Yahoo states an explicit reason, pass it as yahoo_reason: that
    reason becomes the whole message and the speculative 'possibly delisted'
    prefix and generic 'no price data found' text are dropped, because Yahoo
    has already explained the absence. Otherwise the message reports the
    request context (debug_info) and keeps the delisting speculation.
    """
    def __init__(self, ticker, debug_info, yahoo_reason=None):
        self.debug_info = debug_info
        self.yahoo_reason = yahoo_reason
        if yahoo_reason is not None:
            super().__init__(ticker, yahoo_reason, possibly_delisted=False)
        elif debug_info != '':
            super().__init__(ticker, f"no price data found {debug_info}")
        else:
            super().__init__(ticker, "no price data found")


class YFEarningsDateMissing(YFTickerMissingError):
    # note that this does not get raised. Added in case of raising it in the future
    def __init__(self, ticker):
        super().__init__(ticker, "no earnings dates found")


class YFInvalidPeriodError(YFException):
    def __init__(self, ticker, invalid_period, valid_ranges):
        self.ticker = ticker
        self.invalid_period = invalid_period
        self.valid_ranges = valid_ranges
        super().__init__(f"{self.ticker}: Period '{invalid_period}' is invalid, "
                         f"must be one of: {valid_ranges}")


class YFRateLimitError(YFException):
    def __init__(self):
        super().__init__("Too Many Requests. Rate limited. Try after a while.")
