# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/dataStrcuture/03_odds.ipynb.

# %% auto 0
__all__ = ['MARKET_TYPES', 'MarketOdds']

# %% ../../nbs/dataStrcuture/03_odds.ipynb 3
import datetime
import re
import mongoengine
import pandas as pd

# %% ../../nbs/dataStrcuture/03_odds.ipynb 5
MARKET_TYPES = ("asian", "total", "1x2")

# %% ../../nbs/dataStrcuture/03_odds.ipynb 6
class MarketOdds(mongoengine.Document):
    "Store the odds values"

    # Game-info.
    game_id = mongoengine.StringField(db_field="gameId", required=True)
    game_opta_id = mongoengine.IntField(db_field="game_optaId", required=False)
    game_date = mongoengine.DateTimeField(db_field="gameDate", required=False)
    competition_id = mongoengine.StringField(db_field="competitionId", required=True)
    competition_opta_id = mongoengine.IntField(
        db_field="competition_optaId", required=False
    )
    
    season_id = mongoengine.IntField(db_field="seasonId", required=True)
    home_team_id = mongoengine.StringField(db_field="homeTeamId", required=True)
    away_team_id = mongoengine.StringField(db_field="awayTeamId", required=True)

    # Bookmaker info.
    bookmaker_id = mongoengine.IntField(db_field="bookmakerId", required=True)
    bookmaker_name = mongoengine.StringField(db_field="bookmakerName", required=True)
    source = mongoengine.StringField(required=False)

    # Market info.
    market_type = mongoengine.StringField(
        choices=MARKET_TYPES, db_field="marketType", required=True
    )
    line_id = mongoengine.FloatField(db_field="lineId", required=False)

    # Odds.
    received_at = mongoengine.DateTimeField(db_field="timeReceived", required=False)
    odds1 = mongoengine.FloatField(required=True, min_value=1)
    odds2 = mongoengine.FloatField(required=True, min_value=1)
    oddsX = mongoengine.FloatField(required=False, min_value=1)
    overround = mongoengine.FloatField(required=False)
        
        
    meta = {
        "db_alias": "features",
        "collection": "odds",
        "ordering": ["-received_at"],
    }

    @classmethod
    def get_all_odds(
        cls,
        ra_game_id: str,  # Real-analytics game identifier.
        date: datetime.datetime = None,  # Find the lastest data document prior to `date`.
    ) -> pd.DataFrame:  # ALL markets information.
        "Extract latest odds (1x2, Asian handicap and Total) available for a given game prior to a given date"

        if date is None:
            date = datetime.datetime.now()

        odds_feats = cls.objects(game_id=ra_game_id, received_at__lt=date).order_by(
            "-received_at"
        )
        if not odds_feats:
            return None

        return pd.concat(
            [pd.DataFrame([{x: odds[x] for x in odds}]) for odds in odds_feats]
        ).reset_index(drop=True)

    @classmethod
    def get_latest_market(
        cls,
        ra_game_id: str,  # Real-analytics game identifier.
        market: str,  #  Type of market required; should one of MARKET_TYPES.
        date: datetime.datetime = None,  # Find the lastest data document prior to `date`.
    ):
        "Extract latest odds available for a given game prior to a given date"

        if date is None:
            date = datetime.datetime.now()

        if market == "asian":
            # Asian lines.
            odds_feats = cls.objects(
                game_id=ra_game_id, market_type=market, received_at__lt=date
            ).order_by("-received_at")

            # Case empty.
            if odds_feats is None:
                return None

            # Get all lines.
            all_lines = pd.DataFrame()
            for odds in odds_feats:
                odds_df = pd.DataFrame({x: odds[x] for x in odds}, index=[0])
                all_lines = pd.concat([all_lines, odds_df])

            # Remove duplicates and keep the fresh one.
            all_lines = all_lines.drop_duplicates(
                subset=["game_id", "line_id"], keep="first"
            )

            # Calculate delta between odds1 and odds 2.
            all_lines["delta"] = abs(all_lines["odds1"] - 2.0) + abs(
                all_lines["odds2"] - 2.0
            )
            # Keep the line that has a minimum delta (even line).
            line = all_lines.loc[all_lines["delta"] == all_lines["delta"].min()]

            return line

        else:
            return (
                cls.objects(
                    game_id=ra_game_id, market_type=market, received_at__lt=date
                )
                .order_by("-received_at")
                .first()
            )

    @classmethod
    def get_odds_features(
        cls,
        ra_game_id: str,  # Real-analytics game identifier.
        market: str = None,  # Type of market required; should one of MARKET_TYPES.
        date: datetime.datetime = None,  # Find the lastest data document prior to `date`.
    ) -> pd.DataFrame:  # 1-row Data.Frame with odds columns and probabilities columns.
        "Extract odds features and compute implied probs"

        if date is None:
            date = datetime.datetime.now()

        # Extract squad info.
        odds_feats = cls.get_latest_market(ra_game_id, market, date)
        if odds_feats is None:
            return None

        # Convert to dataFrame.
        odds_df = pd.DataFrame({x: odds_feats[x] for x in odds_feats}, index=[0])

        # Compute probs.
        odds_cols = odds_df.columns[odds_df.columns.str.startswith("odds")].tolist()
        probs_cols = [re.sub("odds", "proba", col) for col in odds_cols]
        odds_values = odds_df.loc[:, odds_cols]
        probs = pd.DataFrame(
            (1 / odds_values.values) / (1 / odds_values.values).sum(),
            index=[0],
            columns=probs_cols,
        )

        return pd.concat([odds_df, probs], axis=1)
