# MIT License
#
# Copyright (c) 2020 Brett Graves
#
# 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.

from enum import Enum

from ..utils import option_format


class OType(Enum):
    Order = 1
    Modify = 2
    Cancel = 3


class Side(Enum):
    Buy = 1
    Sell = 2
    BuyCover = 3
    SellShort = 4


class SubmitStatus(Enum):
    NotSubmitted = 1
    Submitted = 2
    Pending = 3
    Open = 4
    Paritial = 5
    Filled = 6


class TimeInForce(Enum):
    Day = 0
    GTC = 1
    OnClose = 7


class Instrument:
    """Handle all the bullshit around an instrument"""

    def __str__(self):
        return self.symbol


### Pricing information


class PriceType(Enum):
    Market = 1
    Limit = 2
    Stop = 3
    StopLimit = 4
    StopLoss = 5
    TrailingStop = 6


class Pricing:
    def __init__(self):
        self._tag = {}

    @property
    def attributes(self):
        return self._data

    @property
    def fixml(self):
        return self._tag

    def __eq__(self, other):
        if isinstance(other, Pricing):
            return (
                (other.type_ == self.type_)
                and (other._data == self._data)
                and (other._tag == self._tag)
            )
        return False


class Market(Pricing):
    def __init__(self):
        super().__init__()
        self.type_ = PriceType.Market
        self._data = {"Typ": "1"}

    def __str__(self):
        """Creates market price object."""
        return "Market"


class Limit(Pricing):
    def __init__(self, limpx):
        """Creates a limit price object.

        Args:

                limpx: the stop price

        """
        super().__init__()
        self.px = round(float(limpx), 2)
        self.type_ = PriceType.Limit
        self._data = {"Typ": "2", "Px": str(self.px)}

    def __str__(self):
        return "Limit ${:.3f}".format(self.px)


class Stop(Pricing):
    def __init__(self, stoppx):
        """Creates a stop price object.

        Args:

                stoppx: the stop price

        """
        super().__init__()
        self.stoppx = round(float(stoppx), 2)
        self.type_ = PriceType.Stop
        self._data = {"Typ": "3", "StopPx": str(self.stoppx)}

    def __str__(self):
        return "Stop ${:.3f}".format(self.stoppx)


class StopLimit(Pricing):
    def __init__(self, limpx, stoppx):
        """Stop-limit price object.

        Args:

                limpx: limit price, to be used once stop price was reached
                stoppx: stop price, to trigger limit price
        """
        super().__init__()
        self.px = round(float(limpx), 2)
        self.stoppx = round(float(stoppx), 2)
        self.type = PriceType.StopLimit
        self._data = {"Typ": "4", "StopPx": str(self.stoppx), "Px": str(self.px)}

    def __str__(self):
        return "StopLimit (Stop ${0:.3f}, Limit ${1:.3f})".format(self.px, self.stoppx)


class TrailingStop(Pricing):
    def __init__(self, use_pct, offset):
        """Trailing stop price object.

        Args:

                use_pct: if true, treat offset as percent. Otherwise, treat it as a dollar quantity
                offset: the trailing stop offset

        """
        self.use_pct = use_pct
        self.offset = offset
        self._tag = {
            "PegInstr": {
                "OfstTyp": 1 if self.use_pct else 0,
                "PegPxTyp": 1,
                "OfstVal": self.offset,
            }
        }
        self.type_ = PriceType.TrailingStop
        self._data = {"Typ": "P"}

    def __str__(self):
        return "Trailing Stop {0}{1}{2}".format(
            "$" if not self.use_pct else "",
            ("{:.1f}" if self.use_pct else "{:.3f}").format(self.offset),
            "%" if self.use_pct else "",
        )


class Option(Instrument):
    def __init__(self, underlying, exp_date, strike, direction):
        self.type = "OPTION"
        self.underlying = underlying.upper()
        self.exp_date = exp_date
        self.strike = strike
        self.direction = "CALL" if "c" in direction.lower() else "PUT"

        # Also get the option
        self.symbol = option_format(
            symbol=self.underlying,
            exp_date=self.exp_date,
            strike=self.strike,
            direction=self.direction,
        )

    @property
    def fixml(self):
        return {
            "Instrmt": {
                "CFI": "O" + self.direction[0],
                "SecTyp": "OPT",
                "MatDt": self.exp_date + "T00:00:00.000-05:00",
                "StrkPx": self.strike,
                "Sym": self.underlying,
            }
        }


class Stock(Instrument):
    def __init__(self, symbol):
        self.type = "STOCK"
        self.symbol = symbol.upper()

    @property
    def fixml(self):
        return {"Instrmt": {"SecTyp": "CS", "Sym": self.symbol}}
