import csv
import datetime
import logging
import math
from decimal import Decimal

import pandas as pd
import pybaseball as pb
import random
import requests
import time

from db_calls import db_get
from db_calls_card_creation import *
from bs4 import BeautifulSoup

D20_CHANCES = {
        '2': {
            'chances': 1,
            'inc': .05
        },
        '3': {
            'chances': 2,
            'inc': .1
        },
        '4': {
            'chances': 3,
            'inc': .15
        },
        '5': {
            'chances': 4,
            'inc': .2
        },
        '6': {
            'chances': 5,
            'inc': .25
        },
        '7': {
            'chances': 6,
            'inc': .3
        },
        '8': {
            'chances': 5,
            'inc': .25
        },
        '9': {
            'chances': 4,
            'inc': .2
        },
        '10': {
            'chances': 3,
            'inc': .15
        },
        '11': {
            'chances': 2,
            'inc': .1
        },
        '12': {
            'chances': 1,
            'inc': .05
        }
    }
BLANK_RESULTS = {
    'vL': {
        '1': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        },
        '2': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        },
        '3': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        }
    },
    'vR': {
        '1': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        },
        '2': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        },
        '3': {
            '2': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '3': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '4': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '5': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '6': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '7': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '8': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '9': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '10': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '11': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            '12': {
                'result': None,
                'splits': None,
                '2d6': None
            },
            'splits': 0
        }
    }
}
TESTING = False
YES = ['y', 'yes', 'yeet', 'please', 'yeah']
CLUB_LIST = {
    'ARI': 'Arizona Diamondbacks',
    'ATL': 'Atlanta Braves',
    'BAL': 'Baltimore Orioles',
    'BOS': 'Boston Red Sox',
    'CHC': 'Chicago Cubs',
    'CHW': 'Chicago White Sox',
    'CIN': 'Cincinnati Reds',
    'CLE': 'Cleveland Guardians',
    'COL': 'Colorado Rockies',
    'DET': 'Detroit Tigers',
    'HOU': 'Houston Astros',
    'KCR': 'Kansas City Royals',
    'LAA': 'Los Angeles Angels',
    'LAD': 'Los Angeles Dodgers',
    'FLA': 'Florida Marlins',
    'MIA': 'Miami Marlins',
    'MIL': 'Milwaukee Brewers',
    'MIN': 'Minnesota Twins',
    'NYM': 'New York Mets',
    'NYY': 'New York Yankees',
    'OAK': 'Oakland Athletics',
    'PHI': 'Philadelphia Phillies',
    'PIT': 'Pittsburgh Pirates',
    'SDP': 'San Diego Padres',
    'SEA': 'Seattle Mariners',
    'SFG': 'San Francisco Giants',
    'STL': 'St Louis Cardinals',
    'TBR': 'Tampa Bay Rays',
    'TEX': 'Texas Rangers',
    'TOR': 'Toronto Blue Jays',
    'WSN': 'Washington Nationals',
    'TOT': 'None',
    '2 Tms': 'None',
    '3 Tms': 'None',
    '4 Tms': 'None'
}
FRANCHISE_LIST = {
    'ARI': 'Arizona Diamondbacks',
    'ATL': 'Atlanta Braves',
    'BAL': 'Baltimore Orioles',
    'BOS': 'Boston Red Sox',
    'CHC': 'Chicago Cubs',
    'CHW': 'Chicago White Sox',
    'CIN': 'Cincinnati Reds',
    'CLE': 'Cleveland Guardians',
    'COL': 'Colorado Rockies',
    'DET': 'Detroit Tigers',
    'HOU': 'Houston Astros',
    'KCR': 'Kansas City Royals',
    'LAA': 'Los Angeles Angels',
    'LAD': 'Los Angeles Dodgers',
    'FLA': 'Miami Marlins',
    'MIA': 'Miami Marlins',
    'MIL': 'Milwaukee Brewers',
    'MIN': 'Minnesota Twins',
    'NYM': 'New York Mets',
    'NYY': 'New York Yankees',
    'OAK': 'Oakland Athletics',
    'PHI': 'Philadelphia Phillies',
    'PIT': 'Pittsburgh Pirates',
    'SDP': 'San Diego Padres',
    'SEA': 'Seattle Mariners',
    'SFG': 'San Francisco Giants',
    'STL': 'St Louis Cardinals',
    'TBR': 'Tampa Bay Rays',
    'TEX': 'Texas Rangers',
    'TOR': 'Toronto Blue Jays',
    'WSN': 'Washington Nationals',
    'TOT': 'None',
    '2 Tms': 'None',
    '3 Tms': 'None',
    '4 Tms': 'None'
}


def get_args(args):
    logging.info(f'Process arguments: {args}')
    final_args = {}
    for x in args:
        if "=" not in x:
            raise TypeError(f'Invalid <key>=<value> argument: {x}')

        key, value = x.split("=")
        logging.info(f'key: {key} / value: {value}')

        if key in final_args:
            raise ValueError(f'Duplicate argument: {key}')

        final_args[key] = value
    return final_args


async def pd_players_df(cardset_id: int):
    p_query = await db_get(
        'players',
        params=[('inc_dex', False), ('cardset_id', cardset_id), ('short_output', True)]
    )
    if p_query['count'] == 0:
        return pd.DataFrame({
            'player_id': [], 'p_name': [], 'cost': [], 'image': [], 'image2': [], 'mlbclub': [], 'franchise': [],
            'cardset': [], 'set_num': [], 'rarity': [], 'pos_1': [], 'pos_2': [], 'pos_3': [], 'pos_4': [], 'pos_5': [],
            'pos_6': [], 'pos_7': [], 'pos_8': [], 'headshot': [], 'vanity_card': [], 'strat_code': [], 'bbref_id': [],
            'fangr_id': [], 'description': [], 'quantity': [], 'mlbplayer': []
        })
    return pd.DataFrame(p_query['players'])


async def pd_pitchingcards_df(cardset_id: int):
    bc_query = await db_get('pitchingcards', params=[('cardset_id', cardset_id), ('short_output', True)])
    if bc_query['count'] == 0:
        raise ValueError(f'No pitching cards returned from Paper Dynasty API')
    return pd.DataFrame(bc_query['cards']).rename(columns={'id': 'pitchingcard_id', 'player': 'player_id'})


async def pd_pitchingcardratings_df(cardset_id: int):
    vl_query = await db_get(
        'pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'L'), ('short_output', True)])
    vr_query = await db_get(
        'pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'R'), ('short_output', True)])
    if 0 in [vl_query['count'], vr_query['count']]:
        raise ValueError(f'No pitching card ratings returned from Paper Dynasty API')
    vl = pd.DataFrame(vl_query['ratings'])
    vr = pd.DataFrame(vr_query['ratings'])
    ratings = (pd.merge(vl, vr, on='pitchingcard', suffixes=('_vL', '_vR'))
               .rename(columns={'pitchingcard': 'pitchingcard_id'}))

    def get_total_ops(df_data):
        ops_vl = df_data['obp_vL'] + df_data['slg_vL']
        ops_vr = df_data['obp_vR'] + df_data['slg_vR']
        return (ops_vr + ops_vl + max(ops_vl, ops_vr)) / 3
    ratings['total_OPS'] = ratings.apply(get_total_ops, axis=1)

    return ratings


async def pd_positions_df(cardset_id: int):
    pos_query = await db_get(
        'cardpositions', params=[('cardset_id', cardset_id), ('short_output', True), ('sort', 'innings-desc')])
    if pos_query['count'] == 0:
        raise ValueError('No position ratings returned from Paper Dynasty API')
    all_pos = pd.DataFrame(pos_query['positions']).rename(columns={'player': 'player_id'})

    return all_pos


def get_pitching_stats(file_path: str = None, start_date: datetime.datetime = None, end_date: datetime.datetime = None):
    if file_path is not None:
        vl_basic = pd.read_csv(f'{file_path}vlhh-basic.csv').query('TBF >= 20')
        vr_basic = pd.read_csv(f'{file_path}vrhh-basic.csv').query('TBF >= 40')
        total_basic = pd.merge(vl_basic, vr_basic, on="playerId", suffixes=('_vL', '_vR'))

        vl_rate = pd.read_csv(f'{file_path}vlhh-rate.csv').query('TBF >= 20')
        vr_rate = pd.read_csv(f'{file_path}vrhh-rate.csv').query('TBF >= 40')
        total_rate = pd.merge(vl_rate, vr_rate, on="playerId", suffixes=('_vL', '_vR'))

        return pd.merge(total_basic, total_rate, on="playerId", suffixes=('', '_rate'))

    else:
        raise LookupError(f'Date-based stat pulls not implemented, yet. Please provide batting csv files.')
        # vrb_url = f'https://www.fangraphs.com/leaders/splits-leaderboards?splitArr=6&splitArrPitch=&position=P' \
        #           f'&autoPt=false&splitTeams=false&statType=player&statgroup=1' \
        #           f'&startDate={start_date.year}-{start_date.month}-{start_date.day}' \
        #           f'&endDate={end_date.year}-{end_date.month}-{end_date.day}' \
        #           f'&players=&filter=&groupBy=season&sort=4,1&wxTemperature=&wxPressure=&wxAirDensity=' \
        #           f'&wxElevation=&wxWindSpeed='
        # vrr_url = f'https://www.fangraphs.com/leaders/splits-leaderboards?splitArr=6&splitArrPitch=&position=P' \
        #           f'&autoPt=false&splitTeams=false&statType=player&statgroup=3' \
        #           f'&startDate={start_date.year}-{start_date.month}-{start_date.day}' \
        #           f'&endDate={end_date.year}-{end_date.month}-{end_date.day}' \
        #           f'&players=&filter=&groupBy=season&sort=4,1&wxTemperature=&wxPressure=&wxAirDensity=' \
        #           f'&wxElevation=&wxWindSpeed='
        # vlb_url = f'https://www.fangraphs.com/leaders/splits-leaderboards?splitArr=5&splitArrPitch=&position=P' \
        #           f'&autoPt=false&splitTeams=false&statType=player&statgroup=1' \
        #           f'&startDate={start_date.year}-{start_date.month}-{start_date.day}' \
        #           f'&endDate={end_date.year}-{end_date.month}-{end_date.day}' \
        #           f'&players=&filter=&groupBy=season&sort=4,1&wxTemperature=&wxPressure=&wxAirDensity=' \
        #           f'&wxElevation=&wxWindSpeed='
        # vlr_url = f'https://www.fangraphs.com/leaders/splits-leaderboards?splitArr=5&splitArrPitch=&position=P' \
        #           f'&autoPt=false&splitTeams=false&statType=player&statgroup=3' \
        #           f'&startDate={start_date.year}-{start_date.month}-{start_date.day}' \
        #           f'&endDate={end_date.year}-{end_date.month}-{end_date.day}' \
        #           f'&players=&filter=&groupBy=season&sort=4,1&wxTemperature=&wxPressure=&wxAirDensity=' \
        #           f'&wxElevation=&wxWindSpeed='
        #
        # soup = BeautifulSoup(requests.get(vrb_url).text, 'html.parser')
        # time.sleep(3)
        # table = soup.find('a', {'class': 'data-export'})


def get_pitching_peripherals(season: int):
    url = f'https://www.baseball-reference.com/leagues/majors/{season}-standard-pitching.shtml'
    soup = BeautifulSoup(requests.get(url).text, 'html.parser')
    time.sleep(3)
    table = soup.find('table', {'id': 'players_standard_pitching'})
    headers = []
    data = []
    indeces = []
    for row in table.find_all('tr'):
        row_data = []
        col_names = []
        for cell in row.find_all('td'):
            try:
                player_id = cell['data-append-csv']
                row_data.append(player_id)
                if len(headers) == 0:
                    col_names.append('key_bbref')
            except Exception as e:
                pass
            row_data.append(cell.text)
            if len(headers) == 0:
                col_names.append(cell['data-stat'])
        if len(row_data) > 0:
            data.append(row_data)
            indeces.append(row_data[0])
            if len(headers) == 0:
                headers.extend(col_names)
    pit_frame = pd.DataFrame(data, index=indeces, columns=headers).query('key_bbref == key_bbref')
    return pit_frame.drop_duplicates(subset=['key_bbref'], keep='first')


def mround(x, prec=2, base=.05):
    return round(base * round(float(x) / base), prec)


def chances_from_row(row_num):
    if row_num == '2' or row_num == '12':
        return 1
    if row_num == '3' or row_num == '11':
        return 2
    if row_num == '4' or row_num == '10':
        return 3
    if row_num == '5' or row_num == '9':
        return 4
    if row_num == '6' or row_num == '8':
        return 5
    if row_num == '7':
        return 6
    raise ValueError(f'No chance count found for row_num {row_num}')


def legal_splits(tot_chances):
    legal_2d6 = []
    for x in D20_CHANCES:
        num_incs = mround(tot_chances) / D20_CHANCES[x]['inc']
        if num_incs - int(num_incs) == 0 and int(20 - num_incs) > 0:
            legal_2d6.append({
                '2d6': int(x),
                'incs': int(num_incs),
                'bad_chances': mround(D20_CHANCES[x]['chances'] * (int(20 - num_incs) / 20)),
                'bad_incs': int(20 - num_incs)
            })

    random.shuffle(legal_2d6)
    # if TESTING: print(f'tot_chances: {myround(tot_chances)}')
    # if TESTING: print(f'legal_2d6: {legal_2d6}')
    return legal_2d6


def result_string(tba_data, row_num, split_min=None, split_max=None):
    bold1 = f'{"<b>" if tba_data["bold"] else ""}'
    bold2 = f'{"</b>" if tba_data["bold"] else ""}'
    row_string = f'{"<b> </b>" if int(row_num) < 10 else ""}{row_num}'
    if TESTING: print(f'adding {tba_data["string"]} to row {row_num} / '
                      f'split_min: {split_min} / split_max: {split_max}')

    # No splits; standard result
    if not split_min:
        return f'{bold1}{row_string}-{tba_data["string"]}{bold2}'

    # With splits
    split_nums = f'{split_min if split_min != 20 else ""}{"-" if split_min != 20 else ""}{split_max}'
    data_string = tba_data["sm-string"] if "sm-string" in tba_data.keys() else tba_data["string"]
    spaces = 18 - len(data_string) - len(split_nums)
    if 'WALK' in data_string:
        spaces -= 3
    elif 'SI**' in data_string:
        spaces += 1
    elif 'DO*' in data_string:
        spaces -= 1
    elif 'DO*' in data_string:
        spaces -= 2
    elif 'so' in data_string:
        spaces += 3
    elif 'gb' in data_string:
        spaces -= 3

    if TESTING: print(f'len(tba_data["string"]): {len(data_string)} / len(split_nums): {len(split_nums)} '
                      f'spaces: {spaces}')
    if split_min == 1 or split_min is None:
        row_output = f'{row_string}-'
    else:
        row_output = '<b>   </b>'
    if TESTING: print(f'row_output: {row_output}')
    return f'{bold1}{row_output}{data_string}{" " * spaces}{split_nums}{bold2}'


def result_data(tba_data, row_num, tba_data_bottom=None, top_split_max=None, fatigue=False):
    ret_data = {}
    top_bold1 = f'{"<b>" if tba_data["bold"] else ""}'
    top_bold2 = f'{"</b>" if tba_data["bold"] else ""}'
    bot_bold1 = None
    bot_bold2 = None
    if tba_data_bottom:
        bot_bold1 = f'{"<b>" if tba_data_bottom["bold"] else ""}'
        bot_bold2 = f'{"</b>" if tba_data_bottom["bold"] else ""}'

    if tba_data_bottom is None:
        ret_data['2d6'] = f'{top_bold1}{int(row_num)}-{top_bold2}'
        ret_data['splits'] = f'{top_bold1}‎{top_bold2}'
        ret_data['result'] = f'{top_bold1}' \
                             f'{tba_data["string"]}{" •" if fatigue else ""}' \
                             f'{top_bold2}'
    else:
        ret_data['2d6'] = f'{top_bold1}{int(row_num)}-{top_bold2}\n'
        ret_data['splits'] = f'{top_bold1}1{"-" if top_split_max != 1 else ""}' \
                             f'{top_split_max if top_split_max != 1 else ""}{top_bold2}\n' \
                             f'{bot_bold1}{top_split_max+1}{"-20" if top_split_max != 19 else ""}{bot_bold2}'
        ret_data['result'] = \
            f'{top_bold1}{tba_data["sm-string"] if "sm-string" in tba_data.keys() else tba_data["string"]}' \
            f'{top_bold2}\n' \
            f'{bot_bold1}' \
            f'{tba_data_bottom["sm-string"] if "sm-string" in tba_data_bottom.keys() else tba_data_bottom["string"]}' \
            f'{bot_bold2}'

    return ret_data


def get_of(batter_hand, pitcher_hand, pull_side=True):
    if batter_hand == 'R':
        return 'lf' if pull_side else 'rf'

    if batter_hand == 'L':
        return 'rf' if pull_side else 'lf'

    if batter_hand == 'S':
        if pitcher_hand == 'L':
            return 'rf' if pull_side else 'rf'
        else:
            return 'lf' if pull_side else 'lf'


def get_col(col_num):
    if col_num == '1':
        return 'one'
    if col_num == '2':
        return 'two'
    if col_num == '3':
        return 'three'


def write_to_csv(output_path, file_name: str, row_data: list):
    # Build the csv output
    fpath = (output_path / f'{file_name}').with_suffix('.csv')
    # logging.info(f'Printing following data to {file_name}:\n\n{row_data}')
    with fpath.open(mode='w+', newline='', encoding='utf-8') as csv_File:
        writer = csv.writer(csv_File)
        writer.writerows(row_data)


def get_position_string(all_pos: list, inc_p: bool):
    if len(all_pos) == 0:
        return 'dh'

    of_arm = None
    of_error = None
    of_innings = None
    lf_range = None
    lf_innings = 0
    cf_range = None
    cf_innings = 0
    rf_range = None
    rf_innings = 0

    all_def = []

    for x in all_pos:
        if x.position == 'OF':
            of_arm = f'{"+" if "-" not in x.arm else ""}{x.arm}'
            of_error = x.error
            of_innings = x.innings
        elif x.position == 'CF':
            cf_range = x.range
            cf_innings = x.innings
        elif x.position == 'LF':
            lf_range = x.range
            lf_innings = x.innings
        elif x.position == 'RF':
            rf_range = x.range
            rf_innings = x.innings
        elif x.position == 'C':
            all_def.append(
                (f'c-{x.range}({"+" if int(x.arm) >= 0 else ""}{x.arm}) e{x.error} T-{x.overthrow}(pb-{x.pb})', x.innings)
            )
        elif 'P' in x.position and not inc_p:
            pass
        else:
            all_def.append((f'{x.position.lower()}-{x.range}e{x.error}', x.innings))

    if of_arm is not None:
        logging.info(
            f'\n\nProcessing OF player ID {all_pos[0].player_id}\nlf-{lf_range} / cf-{cf_range} / rf-{rf_range}'
        )
        all_of = []
        if lf_innings > 0:
            all_of.append((lf_range, lf_innings, 'lf'))
        if cf_innings > 0:
            all_of.append((cf_range, cf_innings, 'cf'))
        if rf_innings > 0:
            all_of.append((rf_range, rf_innings, 'rf'))

        logging.info(f'all_of: {all_of}')
        if len(all_of) > 0:
            all_of.sort(key=lambda y: y[1], reverse=True)
            logging.info(f'sorted of: {all_of}')
            out_string = f'{all_of[0][2]}-{all_of[0][0]}({of_arm})e{of_error}'
            if len(all_of) >= 2:
                out_string += f', {all_of[1][2]}-{all_of[1][0]}e{of_error}'
            if len(all_of) >= 3:
                out_string += f', {all_of[2][2]}-{all_of[2][0]}e{of_error}'
            logging.info(f'of string: {out_string}')
            all_def.append((out_string, of_innings))

    all_def.sort(key=lambda z: z[1], reverse=True)

    final_defense = ''
    for x in all_def:
        if len(final_defense) > 0:
            final_defense += f', '
        final_defense += f'{x[0]}'

    return final_defense


def ordered_positions(all_pos: list) -> list:
    if len(all_pos) == 0:
        return ['DH']

    all_def = []

    for x in all_pos:
        if x.position not in ['OF', 'P', 'SP', 'RP', 'CP']:
            all_def.append((x.innings, x.position))

    all_def.sort(key=lambda y: y[0], reverse=True)
    return [x[1] for x in all_def]


def ordered_pitching_positions(all_pos: list) -> list:
    all_def = []

    for x in all_pos:
        if x.position in ['SP', 'RP', 'CP']:
            all_def.append((x.innings, x.position))

    all_def.sort(key=lambda y: y[0], reverse=True)
    return [x[1] for x in all_def]


def defense_rg(all_pos: list) -> list:
    rg_data = [
        None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
        None, None, None, None, None, None
    ]
    all_pitcher = True
    for line in all_pos:
        if 'P' not in line.position:
            all_pitcher = False
            break

    for line in all_pos:
        if line.position == 'P' and all_pitcher:
            this_pit = PitcherData.get_or_none(PitcherData.player == line.player, PitcherData.cardset == line.cardset)
            if this_pit:
                rg_data[0] = line.range
                rg_data[9] = line.error
                rg_data[22] = this_pit.wild_pitch
                rg_data[23] = this_pit.balk
        elif line.position == 'C':
            rg_data[1] = line.range
            rg_data[10] = line.error
            rg_data[19] = line.arm
            rg_data[20] = line.overthrow
            rg_data[21] = line.pb
        elif line.position == '1B':
            rg_data[2] = line.range
            rg_data[11] = line.error
        elif line.position == '2B':
            rg_data[3] = line.range
            rg_data[12] = line.error
        elif line.position == '3B':
            rg_data[4] = line.range
            rg_data[13] = line.error
        elif line.position == 'SS':
            rg_data[5] = line.range
            rg_data[14] = line.error
        elif line.position == 'LF':
            rg_data[6] = line.range
        elif line.position == 'CF':
            rg_data[7] = line.range
        elif line.position == 'RF':
            rg_data[8] = line.range
        elif line.position == 'OF':
            rg_data[15] = line.error
            rg_data[16] = line.error
            rg_data[17] = line.error
            rg_data[18] = line.arm

    return rg_data


def sanitize_chance_output(total_chances, min_chances=1.0, rounding=0.05):
    # r_val = mround(total_chances) if total_chances >= min_chances else 0
    r_val = Decimal(total_chances) if total_chances >= min_chances else Decimal(0)
    logging.debug(f'r_val: {r_val}')
    rounded_val = Decimal(float(math.floor(r_val / Decimal(rounding)) * Decimal(rounding))).quantize(Decimal("0.05"))
    if math.floor(rounded_val) == rounded_val:
        return rounded_val

    exact_chances = [
        Decimal('1.05'), Decimal('1.1'), Decimal('1.2'), Decimal('1.25'), Decimal('1.3'), Decimal('1.35'),
        Decimal('1.4'), Decimal('1.5'), Decimal('1.6'), Decimal('1.65'), Decimal('1.7'), Decimal('1.75'),
        Decimal('1.8'), Decimal('1.9'), Decimal('1.95'), Decimal('2.1'), Decimal('2.2'), Decimal('2.25'),
        Decimal('2.4'), Decimal('2.5'), Decimal('2.55'), Decimal('2.6'), Decimal('2.7'), Decimal('2.75'),
        Decimal('2.8'), Decimal('2.85'), Decimal('3.2'), Decimal('3.25'), Decimal('3.3'), Decimal('3.4'),
        Decimal('3.5'), Decimal('3.6'), Decimal('3.75'), Decimal('3.8'), Decimal('3.9'), Decimal('4.2'),
        Decimal('4.25'), Decimal('4.5'), Decimal('4.75'), Decimal('4.8'), Decimal('5.1'), Decimal('5.4'),
        Decimal('5.7')
    ]
    if rounded_val > exact_chances[-1]:
        return rounded_val

    for x in exact_chances:
        if rounded_val <= x:
            return x


def mlbteam_and_franchise(mlbam_playerid):
    api_url = f'https://statsapi.mlb.com/api/v1/people/{mlbam_playerid}?hydrate=currentTeam'
    logging.info(f'Calling {api_url}')
    p_data = {'mlbclub': None, 'franchise': None}
    # club_list = [
    #     'Arizona Diamondbacks',
    #     'Atlanta Braves',
    #     'Baltimore Orioles',
    #     'Boston Red Sox',
    #     'Chicago Cubs',
    #     'Chicago White Sox',
    #     'Cincinnati Reds',
    #     'Cleveland Guardians',
    #     'Colorado Rockies',
    #     'Detroit Tigers',
    #     'Houston Astros',
    #     'Kansas City Royals',
    #     'Los Angeles Angels',
    #     'Los Angeles Dodgers',
    #     'Miami Marlins',
    #     'Milwaukee Brewers',
    #     'Minnesota Twins',
    #     'New York Mets',
    #     'New York Yankees',
    #     'Oakland Athletics',
    #     'Philadelphia Phillies',
    #     'Pittsburgh Pirates',
    #     'San Diego Padres',
    #     'Seattle Mariners',
    #     'San Francisco Giants',
    #     'St Louis Cardinals',
    #     'Tampa Bay Rays',
    #     'Texas Rangers',
    #     'Toronto Blue Jays',
    #     'Washington Nationals'
    # ]

    try:
        resp = requests.get(api_url, timeout=2)
    except requests.ReadTimeout as e:
        logging.error(f'mlbteam_and_franchise - ReadTimeout pull MLB team for MLB AM player ID {mlbam_playerid}')
        return p_data

    if resp.status_code == 200:
        data = resp.json()
        data = data['people'][0]
        logging.debug(f'data: {data}')
        if data['currentTeam']['name'] in CLUB_LIST.values():
            p_data['mlbclub'] = data['currentTeam']['name']
            p_data['franchise'] = data['currentTeam']['name']
        else:
            logging.error(f'Could not set team for {mlbam_playerid}; received {data["currentTeam"]["name"]}')
    else:
        logging.error(f'mlbteam_and_franchise - Bad response from mlbstatsapi: {resp.status_code}')

    return p_data


def get_all_pybaseball_ids(player_id: list, key_type: str):
    q = pb.playerid_reverse_lookup(player_id, key_type=key_type)
    if len(q.values) > 0:
        return_val = q.loc[0]
    else:
        logging.error(f'get_all_pybaseball_ids - Could not find id {player_id} / {key_type} in pybaseball')
        return_val = None

    return return_val


def sanitize_name(start_name: str) -> str:
    return (start_name
            .replace("é", "e")
            .replace("á", "a")
            .replace(".", "")
            .replace("Á", "A")
            .replace("ñ", "n")
            .replace("ó", "o")
            .replace("í", "i")
            .replace("ú", "u")
            .replace("'", "")
            .replace('-', ' '))