from db_engine import *

import datetime
import logging
import os

from typing import Optional, List
from fastapi import FastAPI, HTTPException, Depends, Response
from fastapi.security import OAuth2PasswordBearer
import pydantic

from playhouse.shortcuts import model_to_dict
from pandas import DataFrame

# TODO: remove block comments from GET /schedules and /transactions

date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
log_level = logging.INFO if os.environ.get('LOG_LEVEL') == 'INFO' else 'WARN'
logging.basicConfig(
    filename=f'logs/database/{date}.log',
    format='%(asctime)s - database - %(levelname)s - %(message)s',
    level=log_level
)
SEASON_DEFAULT = 7

db.create_tables([Current, Division, Manager, Team, Result, Player, Schedule, Transaction, BattingStat, PitchingStat,
                  Standings, BattingCareer, PitchingCareer, FieldingCareer, Manager, Award, DiceRoll, DraftList])
db.close()

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def valid_token(token):
    if token == os.environ.get('API_TOKEN'):
        return True
    else:
        return False


class PlayerPydantic(pydantic.BaseModel):
    name: str
    wara: float
    image: str
    image2: Optional[str] = None
    team_id: int
    season: int
    pitcher_injury: Optional[int] = None
    pos_1: str
    pos_2: Optional[str] = None
    pos_3: Optional[str] = None
    pos_4: Optional[str] = None
    pos_5: Optional[str] = None
    pos_6: Optional[str] = None
    pos_7: Optional[str] = None
    pos_8: Optional[str] = None
    last_game: Optional[str] = None
    last_game2: Optional[str] = None
    il_return: Optional[str] = None
    demotion_week: Optional[int] = None
    strat_code: Optional[str] = None
    bbref_id: Optional[str] = None
    injury_rating: Optional[str] = None


class PlayerModel(pydantic.BaseModel):
    players: List[PlayerPydantic]


class ResultModel(pydantic.BaseModel):
    week: int
    game: int
    away_team_id: int
    home_team_id: int
    away_score: int
    home_score: int
    season: int
    scorecard_url: Optional[str] = None


class BatStat(pydantic.BaseModel):
    player_id: int
    team_id: int
    pos: str
    pa: int
    ab: int
    run: int
    hit: int
    rbi: int
    double: int
    triple: int
    hr: int
    bb: int
    so: int
    hbp: int
    sac: int
    ibb: int
    gidp: int
    sb: int
    cs: int
    bphr: int
    bpfo: int
    bp1b: int
    bplo: int
    xba: int
    xbt: int
    xch: int
    xhit: int
    error: int
    pb: int
    sbc: int
    csc: int
    roba: int
    robs: int
    raa: int
    rto: int
    week: int
    game: int
    season: int


class BattingStatModel(pydantic.BaseModel):
    count: int
    stats: List[BatStat]


class PitStat(pydantic.BaseModel):
    player_id: int
    team_id: int
    ip: float
    hit: int
    run: int
    erun: int
    so: int
    bb: int
    hbp: int
    wp: int
    balk: int
    hr: int
    gs: int
    win: int
    loss: int
    hold: int
    sv: int
    bsv: int
    ir: int
    irs: int
    week: int
    game: int
    season: int


class PitchingStatModel(pydantic.BaseModel):
    count: int
    stats: List[PitStat]


class GameModel(pydantic.BaseModel):
    season: int
    week: int
    away_team_id: int
    home_team_id: int
    game_num: int


class TransactionPydantic(pydantic.BaseModel):
    week: int
    player_id: int
    oldteam_id: int
    newteam_id: int
    season: int
    moveid: str
    cancelled: Optional[bool] = False
    frozen: Optional[bool] = False


class TransModel(pydantic.BaseModel):
    count: int
    moves: List[TransactionPydantic]


class DraftPickPydantic(pydantic.BaseModel):
    overall: Optional[int] = None
    round: int
    origowner_id: int
    owner_id: Optional[int] = None
    season: int
    player_id: Optional[int] = None


class DraftPickModel(pydantic.BaseModel):
    picks: List[DraftPickPydantic]


class ManagerModel(pydantic.BaseModel):
    name: str
    image: Optional[str] = None
    headline: Optional[str] = None
    bio: Optional[str] = None


class AwardPydantic(pydantic.BaseModel):
    name: str
    season: int
    timing: Optional[str] = "In-Season"
    manager1_id: Optional[int] = None
    manager2_id: Optional[int] = None
    player_id: Optional[int] = None
    team_id: Optional[int] = None
    image: Optional[str] = None


class AwardModel(pydantic.BaseModel):
    count: int
    awards: List[AwardPydantic]


class DiceRollPydantic(pydantic.BaseModel):
    season: int
    week: int
    team_id: int
    roller: int
    dsix: Optional[int]
    twodsix: Optional[int]
    threedsix: Optional[int]
    dtwenty: Optional[int]


class DiceModel(pydantic.BaseModel):
    count: int
    rolls: List[DiceRollPydantic]


class DraftListPydantic(pydantic.BaseModel):
    season: int
    team_id: int
    rank: int
    player_id: int


class DraftListModel(pydantic.BaseModel):
    count: int
    draft_list: List[DraftListPydantic]


class SchedulePydantic(pydantic.BaseModel):
    week: int
    away_team_id: int
    home_team_id: int
    game_count: int
    season: int


class ScheduleModel(pydantic.BaseModel):
    count: int
    schedules: List[SchedulePydantic]


class TeamModel(pydantic.BaseModel):
    abbrev: str
    sname: str
    lname: str
    gmid: Optional[int] = None
    gmid2: Optional[int] = None
    manager1_id: Optional[int] = None
    manager2_id: Optional[int] = None
    division_id: Optional[int] = None
    stadium: Optional[str] = None
    thumbnail: Optional[str] = None
    color: Optional[str] = None
    dice_color: Optional[str] = None
    season: int


class CurrentModel(pydantic.BaseModel):
    week: Optional[int] = 0
    freeze: Optional[bool] = False
    season: int
    transcount: Optional[int] = 0
    bstatcount: Optional[int] = 0
    pstatcount: Optional[int] = 0
    bet_week: Optional[int] = 0
    trade_deadline: int
    pick_trade_start: int
    pick_trade_end: int
    playoffs_begin: int
    injury_count: Optional[int] = 0


@app.get('/api/v1/current')
async def v1_current(season: Optional[int] = None, return_type: Optional[str] = 'json'):
    if season:
        current = Current.get_or_none(season=season)
    else:
        current = Current.latest()

    if return_type == 'csv':
        current_list = [
            ['season', 'week', 'trade deadline', 'pick trade start', 'pick trade end', 'playoffs begin', 'injury count'],
            [current.season, current.week, current.trade_deadline, current.pick_trade_start, current.pick_trade_end,
             current.playoffs_begin, current.injury_count]
        ]
        return_current = DataFrame(current_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_current, media_type='text/csv')
    else:
        db.close()
        return model_to_dict(current)


@app.patch('/api/v1/current')
async def v1_current_patch(
        season: Optional[int] = SEASON_DEFAULT, week: Optional[int] = None, freeze: Optional[bool] = None,
        transcount: Optional[int] = None, bstatcount: Optional[int] = None, pstatcount: Optional[int] = None,
        bet_week: Optional[int] = None, trade_deadline: Optional[int] = None, pick_trade_start: Optional[int] = None,
        pick_trade_end: Optional[int] = None, injury_count: Optional[int] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post current')
    current = Current.get_or_none(season=season)
    if not current:
        db.close()
        raise HTTPException(status_code=404, detail=f'Current for season {season} not found')

    if week is not None:
        current.week = week
    if freeze is not None:
        current.freeze = freeze
    if transcount is not None:
        current.transcount = transcount
    if bstatcount is not None:
        current.bstatcount = bstatcount
    if pstatcount is not None:
        current.pstatcount = pstatcount
    if bet_week is not None:
        current.bet_week = bet_week
    if trade_deadline is not None:
        current.trade_deadline = trade_deadline
    if pick_trade_start is not None:
        current.pick_trade_start = pick_trade_start
    if pick_trade_end is not None:
        current.pick_trade_end = pick_trade_end
    if injury_count is not None:
        current.injury_count = injury_count

    current.save()
    db.close()
    return model_to_dict(current)


@app.post('/api/v1/current')
async def v1_current_post(current: CurrentModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post current')

    this_current = Current(
        week=current.week,
        freeze=current.freeze,
        season=current.season,
        transcount=current.transcount,
        bstatcount=current.bstatcount,
        pstatcount=current.pstatcount,
        bet_week=current.bet_week,
        trade_deadline=current.trade_deadline,
        pick_trade_start=current.pick_trade_start,
        pick_trade_end=current.pick_trade_end,
        playoffs_begin=current.playoffs_begin,
        injury_count=current.injury_count
    )

    saved = this_current.save()
    db.close()
    if saved == 1:
        return model_to_dict(this_current)
    else:
        raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot. I could not post that.')


@app.get('/api/v1/teams')
async def v1_teams_get(
        season: Optional[int] = None, owner_id: Optional[int] = None, manager_id: Optional[int] = None,
        active_only: Optional[bool] = False, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    Param: season: int
    Param: team_abbrev: string
    Param: owner_id: int
    """
    if season:
        all_teams = Team.select_season(season)
    else:
        all_teams = Team.select()

    if manager_id:
        print(f'query: {all_teams}')
        try:
            this_manager = Manager.get_by_id(manager_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager ID {manager_id} not found')

        all_teams = all_teams.where(
            (Team.manager1 == this_manager) | (Team.manager2 == this_manager)
        ).order_by(-Team.season)

    if owner_id:
        all_teams = all_teams.where((Team.gmid == owner_id) | (Team.gmid2 == owner_id))

    if active_only:
        all_teams = all_teams.where(
            ~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL'))
        )

    if all_teams.count() == 0:
        db.close()
        logging.error(f'No teams found\n\nseason: {season} / {owner_id}')
        raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if return_type == 'csv' or csv:
        teams_list = [['abbrev', 'sname', 'lname', 'league', 'division', 'stadium', 'manager1', 'manager2', 'thumbnail',
                       'color', 'season', 'dice_color', 'id']]
        for line in all_teams:
            teams_list.append(
                [
                    line.abbrev, line.sname, line.lname, line.division.league_abbrev if line.division else '',
                    line.division.division_name if line.division else '', line.stadium,
                    line.manager1.name if line.manager1 else '', line.manager2.name if line.manager2 else '',
                    line.thumbnail, line.color, line.season, line.dice_color, line.id
                ]
            )
        return_teams = DataFrame(teams_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_teams, media_type='text/csv')

    else:
        return_teams = {'count': all_teams.count(), 'teams': []}
        for team in all_teams:
            return_teams['teams'].append(model_to_dict(team))

        db.close()
        return return_teams


@app.get('/api/v1/teams/{id_or_abbrev}')
async def v1_teams_get_one(id_or_abbrev, season: Optional[int] = SEASON_DEFAULT):
    try:
        this_team = Team.get_by_id(id_or_abbrev)
    except Exception as e:
        if season:
            this_team = Team.get_season(id_or_abbrev, season)
        else:
            this_team = Team.get_season(id_or_abbrev, Current.latest().season)

    if not this_team:
        db.close()
        raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found')
    else:
        db.close()
        return model_to_dict(this_team)


@app.get('/api/v1/teams/{id_or_abbrev}/roster/{which}')
async def v1_teams_get_roster(id_or_abbrev, which, sort: Optional[str] = None):
    try:
        this_team = Team.get_by_id(id_or_abbrev)
    except Exception as e:
        this_team = Team.get_season(id_or_abbrev, SEASON_DEFAULT)

    if not this_team:
        db.close()
        raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found')

    if which != 'current' and which != 'next':
        db.close()
        raise HTTPException(status_code=404, detail=f'Missing /current or /next')

    if which == 'current':
        full_roster = this_team.get_this_week()
    else:
        full_roster = this_team.get_next_week()

    active_players = copy.deepcopy(full_roster['active']['players'])
    sil_players = copy.deepcopy(full_roster['shortil']['players'])
    lil_players = copy.deepcopy(full_roster['longil']['players'])
    full_roster['active']['players'] = []
    full_roster['shortil']['players'] = []
    full_roster['longil']['players'] = []

    for player in active_players:
        full_roster['active']['players'].append(model_to_dict(player))
    for player in sil_players:
        full_roster['shortil']['players'].append(model_to_dict(player))
    for player in lil_players:
        full_roster['longil']['players'].append(model_to_dict(player))

    if sort:
        if sort == 'wara-desc':
            full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)
            full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)
            full_roster['active']['players'].sort(key=lambda p: p["wara"], reverse=True)

    db.close()
    return full_roster


@app.get('/api/v1/teams/{id_or_abbrev}/record/{week_num}')
async def v1_teams_get_record(id_or_abbrev, week_num: int):
    try:
        this_team = Team.get_by_id(id_or_abbrev)
    except Exception as e:
        this_team = Team.get_season(id_or_abbrev, Current.latest().season)

    if not this_team:
        db.close()
        raise HTTPException(status_code=404, detail=f'Team {id_or_abbrev} not found')

    db.close()
    return this_team.get_record(week=week_num)


@app.patch('/api/v1/teams/{team_id}')
async def v1_teams_patch(
        team_id, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None, gmid: Optional[int] = None,
        gmid2: Optional[int] = None, mascot: Optional[str] = None, stadium: Optional[str] = None,
        thumbnail: Optional[str] = None, color: Optional[str] = None, abbrev: Optional[str] = None,
        sname: Optional[str] = None, lname: Optional[str] = None, dice_color: Optional[str] = None,
        division_id: Optional[int] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch teams')
    try:
        this_team = Team.get_by_id(team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')

    if abbrev is not None:
        this_team.abbrev = abbrev
    if manager1_id is not None:
        if manager1_id == 0:
            this_team.manager1 = None
        else:
            try:
                this_manager = Manager.get_by_id(manager1_id)
                this_team.manager1 = this_manager
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'No manager found with id {manager1_id}')
    if manager2_id is not None:
        print('in mgr2')
        if manager2_id == 0:
            print('deleting mgr2')
            this_team.manager2 = None
        else:
            try:
                print('trying mgr2')
                this_manager = Manager.get_by_id(manager2_id)
                this_team.manager2 = this_manager
                print('set mgr2')
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'No manager found with id {manager2_id}')
    if gmid is not None:
        this_team.gmid = gmid
    if gmid2 is not None:
        if gmid2 == 0:
            this_team.gmid2 = None
        else:
            this_team.gmid2 = gmid2
    if mascot is not None:
        if mascot == 'False':
            this_team.mascot = None
        else:
            this_team.mascot = mascot
    if stadium is not None:
        this_team.stadium = stadium
    if thumbnail is not None:
        this_team.thumbnail = thumbnail
    if color is not None:
        this_team.color = color
    if dice_color is not None:
        this_team.dice_color = dice_color
    if sname is not None:
        this_team.sname = sname
    if lname is not None:
        this_team.lname = lname
    if division_id is not None:
        if division_id == 0:
            this_team.division = None
        else:
            try:
                this_division = Division.get_by_id(division_id)
                this_team.division = this_division
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'No division found with id {division_id}')

    if this_team.save():
        db.close()
        return model_to_dict(this_team)
    else:
        db.close()
        raise HTTPException(status_code=400, detail='This update did not go through')


@app.post('/api/v1/teams')
async def v1_teams_post(team: TeamModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post teams')

    dupe_team = Team.get_or_none(Team.season == team.season, Team.abbrev == team.abbrev)
    if dupe_team:
        db.close()
        raise HTTPException(status_code=400, detail=f'There is already a season {team.season} team using {team.abbrev}')

    manager1 = None
    if team.manager1_id:
        try:
            manager1 = Manager.get_by_id(team.manager1_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager with id {team.manager1_id} not found')

    manager2 = None
    if team.manager2_id:
        try:
            manager2 = Manager.get_by_id(team.manager2_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager with id {team.manager2_id} not found')

    division = None
    if team.division_id:
        try:
            division = Division.get_by_id(team.division_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Division with id {team.division_id} not found')

    this_team = Team(
        season=team.season,
        abbrev=team.abbrev,
        sname=team.sname,
        lname=team.lname,
        gmid=team.gmid,
        gmid2=team.gmid2,
        manager1=manager1,
        manager2=manager2,
        division=division,
        stadium=team.stadium,
        thumbnail=team.thumbnail,
        color=team.color,
        dice_color=team.dice_color
    )

    saved = this_team.save()
    db.close()
    if saved == 1:
        return model_to_dict(this_team)
    else:
        raise HTTPException(status_code=418, detail='Well slap my ass and call me a teapot; I could not save that team')


@app.delete('/api/v1/teams/{team_id}')
async def v1_teams_delete(team_id, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to delete teams')
    try:
        this_team = Team.get_by_id(team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {team_id}')

    count = this_team.delete_instance()
    db.close()

    if count == 1:
        raise HTTPException(status_code=200, detail=f'Team {team_id} has been deleted')
    else:
        raise HTTPException(status_code=500, detail=f'Team {team_id} was not deleted')


@app.get('/api/v1/results')
async def v1_results_get(
        season: int, team_abbrev: Optional[str] = None, week: Optional[int] = None, game: Optional[int] = None,
        away_abbrev: Optional[str] = None, home_abbrev: Optional[str] = None, return_type: Optional[str] = 'json',
        csv: Optional[bool] = False):
    """
    Param: team_abbrev: string, may not use with away_abbrev or home_abbrev
    Param: week: int
    Param: away_abbrev: string, may not use with team_abbrev
    Param: home_abbrev: string, may not use with team_abbrev
    """
    all_results = Result.select_season(season)
    # if all_results.count() == 0:
    #     db.close()
    #     raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if week:
        all_results = all_results.where(Result.week == week)

    if game:
        all_results = all_results.where(Result.game == game)

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_results = all_results.where(
            (Result.awayteam == this_team) | (Result.hometeam == this_team)
        )
    else:
        if away_abbrev:
            this_team = Team.get_season(away_abbrev, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {away_abbrev} not found')

            all_results = all_results.where(Result.awayteam == this_team)

        if home_abbrev:
            this_team = Team.get_season(home_abbrev, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {home_abbrev} not found')

            all_results = all_results.where(Result.hometeam == this_team)

    if return_type == 'csv' or csv:
        helper_list = [['season', 'week', 'game_num', 'away_team', 'away_score', 'home_team', 'home_score']]
        for game in all_results:
            helper_list.append([game.season, game.week, game.game, game.awayteam.abbrev, game.awayscore,
                                game.hometeam.abbrev, game.homescore])

        return_results = DataFrame(helper_list).to_csv(header=False, index=False)
        db.close()
        return Response(content=return_results, media_type='text/csv')
    else:
        return_results = {}
        for game in all_results:
            return_results[f'{game.id}'] = model_to_dict(game)

    db.close()
    return return_results


@app.post('/api/v1/results')
async def v1_results_post(result: ResultModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post results')

    away_team = Team.get_by_id(result.away_team_id)
    if not away_team:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {result.away_team_id}')

    home_team = Team.get_by_id(result.home_team_id)
    if not home_team:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {result.home_team_id}')

    new_result = Result(
        week=result.week,
        game=result.game,
        awayteam=away_team,
        hometeam=home_team,
        awayscore=result.away_score,
        homescore=result.home_score,
        season=result.season,
        scorecard_url=result.scorecard_url
    )

    saved = new_result.save()
    if saved != 1:
        db.close()
        raise HTTPException(status_code=400, detail=f'Unable to create {away_team.abbrev} @ {home_team.abbrev} '
                                                    f'w{result.week}g{result.game}')

    # new_result.update_standings()

    # away_team.run_pythag_last8()
    # home_team.run_pythag_last8()
    #
    # away_team.division.sort_division(new_result.season)
    # if away_team.division != home_team.division:
    #     home_team.division.sort_division(new_result.season)
    #
    # away_team.division.sort_wildcard(new_result.season, away_team.division.league_abbrev)
    # if away_team.division.league_abbrev != home_team.division.league_abbrev:
    #     home_team.division.sort_wildcard(new_result.season, home_team.division.league_abbrev)

    db.close()
    raise HTTPException(status_code=200, detail=f'{away_team.abbrev} @ {home_team.abbrev} '
                                                f'w{result.week}g{result.game} has been logged')


@app.patch('/api/v1/results/{result_id}')
async def v1_results_patch(
        result_id, away_score: Optional[int] = None, home_score: Optional[int] = None,
        scorecard_url: Optional[str] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch results')

    try:
        this_result = Result.get_by_id(result_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Result id {result_id} not found')

    if away_score:
        this_result.awayscore = away_score
    if home_score:
        this_result.homescore = home_score
    if scorecard_url:
        this_result.scorecard_url = scorecard_url

    this_result.save()
    db.close()
    return model_to_dict(this_result)


@app.delete('/api/v1/results/{result_id}')
async def v1_results_delete(result_id, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch results')

    try:
        this_result = Result.get_by_id(result_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Result id {result_id} not found')

    this_result.delete_instance()
    db.close()
    raise HTTPException(status_code=200, detail=f'Deleted 1 result')


@app.get('/api/v1/schedules')
async def v1_schedules_get(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev1: Optional[str] = None, team_abbrev2: Optional[str] = None,
        away_abbrev: Optional[str] = None, home_abbrev: Optional[str] = None, week_start: Optional[int] = None,
        week_end: Optional[int] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    Param: team_abbrev1: string, may not use with away_abbrev or home_abbrev
    Param: team_abbrev2: string, may not use with away_abbrev or home_abbrev
    Param: away_abbrev: string, may not use with team_abbrev1/2
    Param: home_abbrev: string, may not use with team_abbrev1/2
    Param: week_start: int
    Param: week_end: int
    """
    if not season:
        season = SEASON_DEFAULT
    schedule = Schedule.select_season(season)
    # if schedule.count() == 0:
    #     db.close()
    #     raise HTTPException(status_code=404, detail=f'Season {season} not found')

    # Check for team_abbrev or away/home_abbrev
    if team_abbrev1 or team_abbrev2:
        if team_abbrev1:
            this_team = Team.get_season(team_abbrev1, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Team {team_abbrev1} not found')

            schedule = schedule.where(
                (Schedule.hometeam == this_team) | (Schedule.awayteam == this_team)
            )
        if team_abbrev2:
            this_team = Team.get_season(team_abbrev2, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Team {team_abbrev2} not found')

            schedule = schedule.where(
                (Schedule.hometeam == this_team) | (Schedule.awayteam == this_team)
            )
    else:
        if away_abbrev:
            this_team = Team.get_season(away_abbrev, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Team {away_abbrev} not found')

            schedule = schedule.where(Schedule.awayteam == this_team)

        if home_abbrev:
            this_team = Team.get_season(home_abbrev, season)
            if not this_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'Team {home_abbrev} not found')

            schedule = schedule.where(Schedule.hometeam == this_team)

    # Check for week limits
    start = 0
    end = Schedule.select(fn.MAX(Schedule.week)).scalar()
    if week_start:
        start = week_start

    if week_end:
        end = week_end

    if start > end:
        logging.error('Start week is after end week - cannot pull schedule')
        db.close()
        raise HTTPException(status_code=404,
                            detail=f'Start week {start} is after end week {end} - cannot pull schedule')

    schedule = schedule.where(
        (Schedule.week >= start) & (Schedule.week <= end)
    )
    logging.info(f'schedule query: {schedule}')

    if return_type == 'csv' or csv:
        schedule_list = [['season', 'week', 'away_abbrev', 'home_abbrev', 'game_count']]
        for line in schedule:
            schedule_list.append(
                [
                    line.season, line.week, line.awayteam.abbrev, line.hometeam.abbrev, line.gamecount
                ]
            )
        return_schedule = DataFrame(schedule_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_schedule, media_type='text/csv')
    else:
        return_schedule = {}
        for game in schedule:
            return_schedule[f'{game.id}'] = model_to_dict(game)

        db.close()
        return return_schedule


@app.post('/api/v1/schedules')
async def v1_schedules_post(schedule: ScheduleModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post schedules')
    all_scheds = []

    for x in schedule.schedules:
        try:
            away_team = Team.get_by_id(x.away_team_id)
            if not away_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'No team found with id {x.away_team_id}')

            home_team = Team.get_by_id(x.home_team_id)
            if not home_team:
                db.close()
                raise HTTPException(status_code=404, detail=f'No team found with id {x.home_team_id}')

            this_sched = Schedule(
                season=x.season,
                week=x.week,
                awayteam=away_team,
                hometeam=home_team,
                gamecount=x.game_count
            )
            all_scheds.append(this_sched)
        except:
            db.close()
            raise HTTPException(
                status_code=404,
                detail=f'Failed to enter Team {x.away_team_id} @ Team {x.home_team_id}'
            )

    with db.atomic():
        Schedule.bulk_create(all_scheds, batch_size=20)

    db.close()
    raise HTTPException(status_code=200, detail=f'Added {schedule.count} game{"s" if schedule.count > 1 else ""}')


@app.patch('/api/v1/schedules/{schedule_id}')
async def v1_schedules_patch(
        schedule_id: int, week: Optional[int] = None, away_team_id: Optional[int] = None,
        home_team_id: Optional[int] = None, game_count: Optional[int] = None, season: Optional[int] = None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch schedules')

    this_schedule = None
    try:
        this_schedule = Schedule.get_by_id(schedule_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'No schedule found with id {schedule_id}')

    if week is not None:
        this_schedule.week = week
    if away_team_id is not None:
        try:
            away_team = Team.get_by_id(away_team_id)
            this_schedule.awayteam = away_team
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {away_team_id}')
    if home_team_id is not None:
        try:
            home_team = Team.get_by_id(home_team_id)
            this_schedule.hometeam = home_team
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {home_team_id}')
    if game_count is not None:
        this_schedule.gamecount = game_count
    if season is not None:
        this_schedule.season = season

    this_schedule.save()
    db.close()
    return model_to_dict(this_schedule)


@app.delete('/api/v1/schedules/{schedule_id}')
async def v1_schedules_delete(schedule_id: int, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to delete schedules')

    delete_query = Schedule.delete().where(Schedule.id == schedule_id)

    count = delete_query.execute()
    db.close()
    if count > 0:
        raise HTTPException(status_code=200, detail=f'Removed {count} schedules')
    else:
        raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
                                                    f'I did not delete any records')


@app.get('/api/v1/transactions')
async def v1_transactions(
        season = SEASON_DEFAULT, team_abbrev: Optional[str] = None, week_start: Optional[int] = 0,
        week_end: Optional[int] = None, cancelled: Optional[bool] = None, frozen: Optional[bool] = None,
        player_name: Optional[str] = None, player_id: Optional[int] = None, move_id: Optional[str] = None,
        is_trade: Optional[bool] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    Param: season: int
    Param: team_abbrev: string
    Param: week_start: int
    Param: week_end: int
    Param: player_id: int, overrides player_name (and season)
    Param: player_name: string, overridden by player_id
    Param: include_cancelled: bool, default False
    Param: include_frozen: bool, default False
    """
    if season:
        transactions = Transaction.select_season(season)
    else:
        transactions = Transaction.select()

    # if transactions.count() == 0:
    #     db.close()
    #     raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if team_abbrev:
        these_teams = Team.select().where(
            (Team.abbrev == team_abbrev.upper()) | (Team.abbrev == f'{team_abbrev.upper()}SIL') |
            (Team.abbrev == f'{team_abbrev.upper()}MiL') | (Team.abbrev == f'{team_abbrev.upper()}IL')
        )
        if these_teams.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        transactions = transactions.where(
            (Transaction.newteam << these_teams) | (Transaction.oldteam << these_teams)
        )

    if week_start is not None:
        transactions = transactions.where(Transaction.week >= week_start)

    if week_end is not None:
        transactions = transactions.where(Transaction.week <= week_end)

    if move_id:
        transactions = transactions.where(Transaction.moveid == move_id)

    if player_id or player_name:
        if player_id:
            try:
                this_player = Player.get_by_id(player_id)
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')

            transactions = transactions.where(Transaction.player == this_player)
        else:
            these_players = Player.select().where(fn.Lower(Player.name) == player_name.lower())
            print(f'these_players: {these_players}\nCount: {these_players.count()}')
            if these_players.count() == 0:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {player_name} not found')

            transactions = transactions.where(Transaction.player << these_players)

    if cancelled:
        transactions = transactions.where(Transaction.cancelled == 1)
    else:
        transactions = transactions.where(Transaction.cancelled == 0)

    if frozen:
        transactions = transactions.where(Transaction.frozen == 1)
    else:
        transactions = transactions.where(Transaction.frozen == 0)

    if is_trade is not None:
        if not is_trade:
            db.close()
            raise HTTPException(status_code=501, detail='The is_trade parameter only supports True')

    # if is_trade is not None:
    #     if is_trade:
    #         old_team = Team.alias()
    #         new_team = Team.alias()
    #         transactions = transactions.join(Team, on=Transaction.oldteam).where(
    #             Transaction.oldteam.gmid != Transaction.newteam.gmid
    #         )
    #         logging.info(transactions)
    #
    #         # # Get IL, MIL and FA teams
    #         # ilteams = Team.select_season(season).where((fn.Lower(Team.abbrev).endswith('il')))
    #         # all_teams = Team.select_season(season).where(
    #         #     ~(Team.abbrev.endswith('IL')) & ~(Team.abbrev.endswith('MiL'))
    #         # )
    #         # fa_team = Team.get(Team.season == season, Team.abbrev == 'FA')
    #         #
    #         # # Get transactions where new team << bad_teams
    #         # transactions = transactions.where(
    #         #     (~(Transaction.oldteam << fa_team) & (Transaction.oldteam << all_teams) &
    #         #         ~(Transaction.newteam << ilteams)) |
    #         #     ((Transaction.oldteam << all_teams) & ~(Transaction.newteam << ilteams))
    #         # )
    #         # # Get transactions where old team << bad_teams
    #     else:
    #         db.close()
    #         raise HTTPException(status_code=501, detail='The "is_trade" parameter only supports True')

    transactions = transactions.order_by(-Transaction.week, Transaction.moveid)

    if return_type == 'csv' or csv:
        trans_list = [['season', 'week', 'player_name', 'old_team_abbrev', 'new_team_abbrev', 'move_id']]
        for line in transactions:
            if (is_trade and line.oldteam.gmid and line.newteam.gmid and line.oldteam.gmid != line.newteam.gmid and
                    'rule5' not in line.moveid) or not is_trade:
                trans_list.append(
                    [
                        line.season, line.week, line.player.name, line.oldteam.abbrev, line.newteam.abbrev, line.moveid
                    ]
                )
        return_trans = DataFrame(trans_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_trans, media_type='text/csv')
    else:
        return_trans = {}
        for line in transactions:
            if (is_trade and line.oldteam.gmid and line.newteam.gmid and line.oldteam.gmid != line.newteam.gmid and
                    'rule5' not in line.moveid) or not is_trade:
                return_trans[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_trans


@app.post('/api/v1/transactions')
async def v1_transactions_post(moves: TransModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post transactions')

    all_moves = []

    for x in moves.moves:
        try:
            old_team = Team.get_by_id(x.oldteam_id)
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'Old team with id {x.oldteam_id} not found')
        try:
            new_team = Team.get_by_id(x.newteam_id)
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'Away team with id {x.newteam_id} not found')
        try:
            this_player = Player.get_by_id(x.player_id)
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {x.player_id} not found')

        this_move = Transaction(
            week=x.week,
            player=this_player,
            oldteam=old_team,
            newteam=new_team,
            season=x.season,
            moveid=x.moveid,
            cancelled=x.cancelled,
            frozen=x.frozen
        )

        all_moves.append(this_move)

    with db.atomic():
        Transaction.bulk_create(all_moves, batch_size=15)

    db.close()
    raise HTTPException(status_code=200, detail=f'{len(all_moves)} of {moves.count} lines have been added')


@app.patch('/api/v1/transactions/{move_id}')
async def v1_transactions_patch(
        move_id, token: str = Depends(oauth2_scheme), frozen: Optional[bool] = None, cancelled: Optional[bool] = None):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch transactions')

    these_moves = Transaction.select().where(Transaction.moveid == move_id)
    if these_moves.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'Move ID {move_id} not found')

    if frozen is not None:
        for x in these_moves:
            x.frozen = frozen
            x.save()
    if cancelled is not None:
        for x in these_moves:
            x.cancelled = cancelled
            x.save()

    db.close()
    raise HTTPException(status_code=200, detail=f'Updated {these_moves.count()} transactions')


@app.delete('/api/v1/transactions/{move_id}')
async def v1_transactions_delete(
        move_id, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch transactions')

    delete_query = Transaction.delete().where(Transaction.moveid == move_id)

    count = delete_query.execute()
    db.close()
    if count > 0:
        raise HTTPException(status_code=200, detail=f'Removed {count} transactions')
    else:
        raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
                                                    f'I did not delete any records')


@app.get('/api/v1/players')
async def v1_players_get(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None,
        injured: Optional[bool] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    Param: season: int
    Param: team_abbrev: string
    Param: sort: string, [wara-desc]
    """
    all_players = Player.select_season(season)
    if all_players.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = all_players.where(Player.team == this_team)

    if sort:
        if sort == 'wara-desc':
            all_players = all_players.order_by(-Player.wara)

    if injured is not None:
        all_players = all_players.where(Player.il_return.is_null(False))

    if return_type == 'csv' or csv:
        player_list = [
            ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'pit_inj', 'pos_1', 'pos_2', 'pos_3', 'pos_4',
             'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week']
        ]
        for line in all_players:
            player_list.append(
                [
                    line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.pitcher_injury,
                    line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8,
                    line.last_game, line.last_game2, line.il_return, line.demotion_week
                ]
            )
        return_players = DataFrame(player_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_players, media_type='text/csv')
    else:
        return_players = {}
        for player in all_players:
            return_players[f'{player.name}'] = model_to_dict(player)

        db.close()
        return return_players


@app.get('/api/v2/players')
async def v2_players_get(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None,
        injured: Optional[bool] = None, csv: Optional[bool] = False):
    """
    Param: season: int
    Param: team_abbrev: string
    Param: sort: string, [wara-desc]
    """
    all_players = Player.select_season(season)
    if all_players.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = all_players.where(Player.team == this_team)

    if sort:
        if sort == 'wara-desc':
            all_players = all_players.order_by(-Player.wara)

    if injured is not None:
        all_players = all_players.where(Player.il_return.is_null(False))

    if csv:
        player_list = [
            ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3', 'pos_4',
             'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week', 'strat_code',
             'bbref_id']
        ]
        for line in all_players:
            player_list.append(
                [
                    line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.injury_rating,
                    line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8,
                    line.last_game, line.last_game2, line.il_return, line.demotion_week, line.strat_code, line.bbref_id
                ]
            )
        return_players = DataFrame(player_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_players, media_type='text/csv')
    else:
        return_players = {}
        for player in all_players:
            return_players[f'{player.name}'] = model_to_dict(player)

        db.close()
        return return_players


@app.get('/api/v3/players')
async def v3_players_get(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None,
        injured: Optional[bool] = None, csv: Optional[bool] = False):
    """
    Param: season: int
    Param: team_abbrev: string
    Param: sort: string, [wara-desc]
    """
    all_players = Player.select_season(season)
    if all_players.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'Season {season} not found')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = all_players.where(Player.team == this_team)

    if sort:
        if sort == 'wara-desc':
            all_players = all_players.order_by(-Player.wara)

    if injured is not None:
        all_players = all_players.where(Player.il_return.is_null(False))

    if csv:
        player_list = [
            ['name', 'wara', 'image', 'vanity_card', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3', 'pos_4',
             'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return', 'dem_week', 'strat_code',
             'bbref_id']
        ]
        for line in all_players:
            player_list.append(
                [
                    line.name, line.wara, line.image, line.vanity_card, line.team.abbrev, line.injury_rating,
                    line.pos_1, line.pos_2, line.pos_3, line.pos_4, line.pos_5, line.pos_6, line.pos_7, line.pos_8,
                    line.last_game, line.last_game2, line.il_return, line.demotion_week, line.strat_code, line.bbref_id
                ]
            )
        return_players = DataFrame(player_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_players, media_type='text/csv')
    else:
        return_players = {'count': all_players.count(), 'players': []}
        for player in all_players:
            return_players['players'].append(model_to_dict(player))

        db.close()
        return return_players


@app.get('/api/v2/players/{id_or_name}')
async def v2_players_get_one(id_or_name, season: Optional[int] = SEASON_DEFAULT, csv: Optional[bool] = False):
    this_player = None

    try:
        this_player = Player.get_by_id(id_or_name)
    except Exception as e:
        this_player = Player.get_season(id_or_name, season)

    if this_player:
        if csv:
            player_list = [['name', 'wara', 'image', 'image2', 'team_abbrev', 'inj_rat', 'pos_1', 'pos_2', 'pos_3',
                            'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return',
                            'dem_week', 'strat_code', 'bbref_id'],
                           [this_player.name, this_player.wara, this_player.image, this_player.image2,
                            this_player.team.abbrev, this_player.injury_rating, this_player.pos_1, this_player.pos_2,
                            this_player.pos_3, this_player.pos_4, this_player.pos_5, this_player.pos_6,
                            this_player.pos_7, this_player.pos_8, this_player.last_game, this_player.last_game2,
                            this_player.il_return, this_player.demotion_week, this_player.strat_code,
                            this_player.bbref_id]
                           ]
            return_players = DataFrame(player_list).to_csv(header=False, index=False)

            db.close()
            return Response(content=return_players, media_type='text/csv')
        else:
            db.close()
            return model_to_dict(this_player)
    else:
        db.close()
        raise HTTPException(status_code=404, detail=f'Player id {id_or_name} not found')


@app.get('/api/v1/players/{id_or_name}')
async def v1_players_get_one(id_or_name, season: Optional[int] = SEASON_DEFAULT, return_type: Optional[str] = 'json',
                             csv: Optional[bool] = False):
    this_player = None

    try:
        this_player = Player.get_by_id(id_or_name)
    except Exception as e:
        this_player = Player.get_season(id_or_name, season)

    if this_player:
        if return_type == 'csv' or csv:
            player_list = [['name', 'wara', 'image', 'image2', 'team_abbrev', 'pit_inj', 'pos_1', 'pos_2', 'pos_3',
                            'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8', 'last_game', 'last_game2', 'il_return',
                            'dem_week'],
                           [this_player.name, this_player.wara, this_player.image, this_player.image2,
                            this_player.team.abbrev, this_player.pitcher_injury, this_player.pos_1, this_player.pos_2,
                            this_player.pos_3, this_player.pos_4, this_player.pos_5, this_player.pos_6,
                            this_player.pos_7, this_player.pos_8, this_player.last_game, this_player.last_game2,
                            this_player.il_return, this_player.demotion_week]
                           ]
            return_players = DataFrame(player_list).to_csv(header=False, index=False)

            db.close()
            return Response(content=return_players, media_type='text/csv')
        else:
            db.close()
            return model_to_dict(this_player)
    else:
        db.close()
        raise HTTPException(status_code=404, detail=f'Player id {id_or_name} not found')


@app.patch('/api/v2/players/{pid}')
async def v1_players_patch(
        pid: int, token: str = Depends(oauth2_scheme), name: Optional[str] = None, wara: Optional[float] = None,
        image: Optional[str] = None, image2=None, team_id: Optional[int] = None, season: Optional[int] = None,
        pitcher_injury=None, pos_1=None, pos_2=None, pos_3=None, pos_4=None, pos_5=None, pos_6=None, pos_7=None,
        pos_8=None, last_game=None, last_game2=None, il_return=None, demotion_week=None, headshot=None,
        vanity_card=None, strat_code=None, bbref_id=None, injury_rating=None):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch players')

    try:
        this_player = Player.get_by_id(pid)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Player id {pid} not found')

    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            this_player.team = this_team
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
    if name:
        this_player.name = name
    if wara is not None:
        this_player.wara = wara
    if image:
        this_player.image = image
    if image2 is not None:
        if image2.lower() == 'false':
            this_player.image2 = None
        else:
            this_player.image2 = image2
    if season:
        this_player.season = season
    if pitcher_injury is not None:
        if pitcher_injury.lower() == 'false':
            this_player.pitcher_injury = None
        else:
            this_player.pitcher_injury = pitcher_injury
    if pos_1:
        this_player.pos_1 = pos_1
    if pos_2 is not None:
        if pos_2.lower() == 'false':
            this_player.pos_2 = None
        else:
            this_player.pos_2 = pos_2
    if pos_3 is not None:
        if pos_3.lower() == 'false':
            this_player.pos_3 = None
        else:
            this_player.pos_3 = pos_3
    if pos_4 is not None:
        if pos_4.lower() == 'false':
            this_player.pos_4 = None
        else:
            this_player.pos_4 = pos_4
    if pos_5 is not None:
        if pos_5.lower() == 'false':
            this_player.pos_5 = None
        else:
            this_player.pos_5 = pos_5
    if pos_6 is not None:
        if pos_6.lower() == 'false':
            this_player.pos_6 = None
        else:
            this_player.pos_6 = pos_6
    if pos_7 is not None:
        if pos_7.lower() == 'false':
            this_player.pos_7 = None
        else:
            this_player.pos_7 = pos_7
    if pos_8 is not None:
        if pos_8.lower() == 'false':
            this_player.pos_8 = None
        else:
            this_player.pos_8 = pos_8
    if last_game is not None:
        if last_game.lower() == 'false':
            this_player.last_game = None
        else:
            this_player.last_game = last_game
    if last_game2 is not None:
        if last_game2.lower() == 'false':
            this_player.last_game2 = None
        else:
            this_player.last_game2 = last_game2
    if il_return is not None:
        if il_return.lower() == 'false':
            this_player.il_return = None
        else:
            this_player.il_return = il_return
    if demotion_week is not None:
        if demotion_week.lower() == 'false':
            this_player.demotion_week = None
        else:
            this_player.demotion_week = demotion_week
    if headshot is not None:
        if headshot.lower() == 'false':
            this_player.headshot = None
        else:
            this_player.headshot = headshot
    if vanity_card is not None:
        if vanity_card.lower() == 'false':
            this_player.vanity_card = None
        else:
            this_player.vanity_card = vanity_card
    if strat_code is not None:
        if strat_code.lower() == 'false':
            this_player.strat_code = None
        else:
            this_player.strat_code = strat_code
    if bbref_id is not None:
        if bbref_id.lower() == 'false':
            this_player.bbref_id = None
        else:
            this_player.bbref_id = bbref_id
    if injury_rating is not None:
        if injury_rating.lower() == 'false':
            this_player.injury_rating = None
        else:
            this_player.injury_rating = injury_rating

    saved = this_player.save()
    db.close()
    if saved == 1:
        return model_to_dict(this_player)
    else:
        raise HTTPException(status_code=400, detail=f'Unable to update {this_player.name}')


@app.post('/api/v2/players')
async def v1_players_post(players: PlayerModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post players')

    new_players = []
    for x in players.players:
        logging.info(f'x: {x}')
        this_player = Player(
            name=x.name,
            wara=x.wara,
            image=x.image,
            image2=x.image2,
            team_id=x.team_id,
            season=x.season,
            pitcher_injury=x.pitcher_injury,
            pos_1=x.pos_1,
            pos_2=x.pos_2,
            pos_3=x.pos_3,
            pos_4=x.pos_4,
            pos_5=x.pos_5,
            pos_6=x.pos_6,
            pos_7=x.pos_7,
            pos_8=x.pos_8,
            last_game=x.last_game,
            last_game2=x.last_game2,
            il_return=x.il_return,
            demotion_week=x.demotion_week,
            strat_code=x.strat_code,
            bbref_id=x.bbref_id,
            injury_rating=x.injury_rating
        )
        new_players.append(this_player)

    with db.atomic():
        Player.bulk_create(new_players, batch_size=15)
    db.close()

    raise HTTPException(status_code=200, detail=f'{len(new_players)} players have been added')


@app.get('/api/v1/battingcareer/{id_or_name}')
async def v1_battingcareer_get(id_or_name, csv: Optional[bool] = False):
    this_career = None
    try:
        this_career = BattingCareer.get_by_id(id_or_name)
    except Exception as e:
        logging.info(f'Could not find BattingCareer with id {id_or_name}')

    if not this_career:
        this_career = BattingCareer.get_or_none(fn.Lower(BattingCareer.name) == id_or_name.lower())

    if this_career:
        if csv:
            career_list = [
                ['name', 'pa', 'ab', 'run', 'hit', 'rbi', 'double', 'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb',
                 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo', 'games'],
                [this_career.name, this_career.pa, this_career.ab, this_career.run, this_career.hit, this_career.rbi,
                 this_career.double, this_career.triple, this_career.hr, this_career.bb, this_career.so,
                 this_career.hbp, this_career.sac, this_career.ibb, this_career.gidp, this_career.sb, this_career.cs,
                 this_career.bphr, this_career.bpfo, this_career.bp1b, this_career.bplo, this_career.games]
            ]
            return_career = DataFrame(career_list).to_csv(header=False, index=False)

            db.close()
            return Response(content=return_career, media_type='text/csv')
        else:
            db.close()
            return model_to_dict(this_career)
    else:
        db.close()
        raise HTTPException(status_code=404, detail=f'Career id {id_or_name} not found')


@app.get('/api/v1/pitchingcareer/{id_or_name}')
async def v1_pitchingcareer_get(id_or_name, csv: Optional[bool] = False):
    this_career = None
    try:
        this_career = PitchingCareer.get_by_id(id_or_name)
    except Exception as e:
        logging.info(f'Could not find PitchingCareer with id {id_or_name}')

    if not this_career:
        this_career = PitchingCareer.get_or_none(fn.Lower(PitchingCareer.name) == id_or_name.lower())

    if this_career:
        if csv:
            career_list = [
                ['name', 'ip', 'hit', 'run', 'erun', 'so', 'bb', 'hbp', 'wp', 'balk', 'hr', 'ir', 'irs', 'gs', 'win',
                 'loss', 'hold', 'sv', 'bsv', 'game'],
                [this_career.name, this_career.ip, this_career.hit, this_career.run, this_career.erun, this_career.so,
                 this_career.bb, this_career.hbp, this_career.wp, this_career.balk, this_career.hr, this_career.ir,
                 this_career.irs, this_career.gs, this_career.win, this_career.loss, this_career.hold, this_career.sv,
                 this_career.bsv, this_career.game]
            ]
            return_career = DataFrame(career_list).to_csv(header=False, index=False)

            db.close()
            return Response(content=return_career, media_type='text/csv')
        else:
            db.close()
            return model_to_dict(this_career)
    else:
        db.close()
        raise HTTPException(status_code=404, detail=f'Career id {id_or_name} not found')


@app.get('/api/v1/battingstats/s{season}/{s_type}')
async def v1_battingstats_get(
        s_type: str, season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None,
        player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None,
        week_end: Optional[int] = None, game_num: Optional[int] = None, position: Optional[str] = None,
        limit: Optional[int] = None, sort: Optional[str] = None, return_type: Optional[str] = 'json',
        csv: Optional[bool] = False):
    """
    :param season: int
    :param s_type: string (regular, post, combined)
    :param team_abbrev: string
    :param player_name: string
    :param player_id: integer, overrides player_name
    :param week_start: integer
    :param week_end: integer
    :param game_num: integer
    :param position: string
    :param limit: int
    :param sort: string, 'oldest' or 'newest' to return first
    :return:
    """
    if s_type.lower() == 'post':
        all_stats = BattingStat.post_season(season)
        if all_stats.count() == 0:
            db.close()
            return {}
    elif s_type.lower() == 'combined':
        all_stats = BattingStat.combined_season(season)
        if all_stats.count() == 0:
            db.close()
            return {}
    else:
        all_stats = BattingStat.regular_season(season)
        if not all_stats:
            db.close()
            return {}

    if position:
        all_stats = all_stats.where(BattingStat.pos == position.upper())

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_stats = all_stats.where(BattingStat.team == this_team)

    if player_name or player_id:
        if player_id:
            try:
                this_player = Player.get_by_id(player_id)
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')
        else:
            this_player = Player.get_season(player_name, season)
            if not this_player:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {player_name} not found')

        all_stats = all_stats.where(BattingStat.player == this_player)

    start = 1
    end = Current.get(Current.season == season).week
    if week_start is not None:
        start = week_start

    if week_end is not None:
        end = min(week_end, end)

    if start > end:
        db.close()
        raise HTTPException(status_code=404,
                            detail=f'Start week {start} is after end week {end} - cannot pull stats')
    if start + 5 < end and team_abbrev is None and player_name is None and player_id is None and not csv:
        start = end - 5

    logging.info(f'\n\nweek_start: {week_start} / week_end: {week_end} / game_num: {game_num} / '
                 f'start: {start} / end: {end}')
    all_stats = all_stats.where(
        (BattingStat.week >= start) & (BattingStat.week <= end)
    )

    if game_num:
        all_stats = all_stats.where(BattingStat.game == game_num)

    if limit:
        all_stats = all_stats.limit(limit)

    if sort:
        if sort == 'newest':
            all_stats = all_stats.order_by(-BattingStat.week, -BattingStat.game)

    if return_type == 'csv' or csv:
        stats_list = [['season', 'week', 'game_num', 'player', 'team', 'pos', 'pa', 'ab', 'run', 'hit', 'rbi', 'double',
                       'triple', 'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp', 'sb', 'cs', 'xba', 'xbt']]
        for line in all_stats:
            stats_list.append(
                [
                    line.season, line.week, line.game, line.player.name, line.team.abbrev, line.pos, line.pa, line.ab,
                    line.run, line.hit, line.rbi, line.double, line.triple, line.hr, line.bb, line.so, line.hbp,
                    line.sac, line.ibb, line.gidp, line.sb, line.cs, line.xba, line.xbt
                ]
            )
        return_stats = DataFrame(stats_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)
            # Need recurse set to false to allow JSON data
            # return_stats[f'{line.id}'] = model_to_dict(line, recurse=False)

    db.close()
    return return_stats


@app.post('/api/v1/battingstats')
async def v1_battingstats_post(stats: BattingStatModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post batting stats')

    all_stats = []
    season = None
    week = None
    teams = []
    game_num = None

    for x in stats.stats:
        try:
            team = Team.get_by_id(x.team_id)
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {x.team_id}')
        try:
            this_player = Player.get_by_id(x.player_id)
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {x.player_id} not found')

        # Helpers for query
        if team not in teams:
            teams.append(team)
        if not season:
            season = x.season
        if not week:
            week = x.week
        if not game_num:
            game_num = x.game

        new_stats = BattingStat(
            player=this_player,
            team=team,
            pos=x.pos,
            pa=x.pa,
            ab=x.ab,
            run=x.run,
            hit=x.hit,
            rbi=x.rbi,
            double=x.double,
            triple=x.triple,
            hr=x.hr,
            bb=x.bb,
            so=x.so,
            hbp=x.hbp,
            sac=x.sac,
            ibb=x.ibb,
            gidp=x.gidp,
            sb=x.sb,
            cs=x.cs,
            bphr=x.bphr,
            bpfo=x.bpfo,
            bp1b=x.bp1b,
            bplo=x.bplo,
            xba=x.xba,
            xbt=x.xbt,
            xch=x.xch,
            xhit=x.xhit,
            error=x.error,
            pb=x.pb,
            sbc=x.sbc,
            csc=x.csc,
            roba=x.roba,
            robs=x.robs,
            raa=x.raa,
            rto=x.rto,
            week=x.week,
            game=x.game,
            season=x.season,
        )
        all_stats.append(new_stats)

    with db.atomic():
        BattingStat.bulk_create(all_stats, batch_size=15)

    this_game_stats = BattingStat.select().where(
        (BattingStat.season == season) & (BattingStat.week == week) &
        (BattingStat.game == game_num) & ((BattingStat.team == teams[0]) | (BattingStat.team == teams[1]))
    )

    logging.warning(f'I pulled {this_game_stats.count()} batting lines with this query:\n{this_game_stats}')

    # Update season and career stats with freshly submitted for regular season games
    if (season > 2) and week <= 22:
        s_type = 'Regular'
    else:
        s_type = 'Post'

    for line in this_game_stats:
        this_season = BattingSeason.get_or_none(player=line.player, season=line.player.season, season_type=s_type)
        if not this_season:
            this_season = BattingSeason(player=line.player, season=line.player.season, season_type=s_type)
            this_season.save()
        this_career = BattingCareer.get_or_none(BattingCareer.name == this_season.player.name)
        if not this_career:
            this_career = BattingCareer(name=this_season.player.name)
            this_career.save()

        fielding_season = FieldingSeason.get_or_none(
            player=line.player, season=line.player.season, pos=line.pos, season_type=s_type
        )
        if not fielding_season:
            fielding_season = FieldingSeason(
                player=line.player, season=line.player.season, pos=line.pos, season_type=s_type
            )
            fielding_season.save()
        fielding_career = FieldingCareer.get_or_none(name=line.player.name, pos=line.pos)
        if not fielding_career:
            fielding_career = FieldingCareer(name=line.player.name, pos=line.pos)
            fielding_career.save()

        this_season.pa += line.pa
        this_season.ab += line.ab
        this_season.run += line.run
        this_season.hit += line.hit
        this_season.rbi += line.rbi
        this_season.double += line.double
        this_season.triple += line.triple
        this_season.hr += line.hr
        this_season.bb += line.bb
        this_season.so += line.so
        this_season.hbp += line.hbp
        this_season.sac += line.sac
        this_season.ibb += line.ibb
        this_season.gidp += line.gidp
        this_season.sb += line.sb
        this_season.cs += line.cs
        this_season.bphr += line.bphr
        this_season.bpfo += line.bpfo
        this_season.bp1b += line.bp1b
        this_season.bplo += line.bplo
        this_season.xba += line.xba
        this_season.xbt += line.xbt
        this_season.game += 1
        fielding_season.xch += line.xch
        fielding_season.xhit += line.xhit
        fielding_season.error += line.error
        fielding_season.pb += line.pb
        fielding_season.sbc += line.sbc
        fielding_season.csc += line.csc
        fielding_season.roba += line.roba
        fielding_season.robs += line.robs
        fielding_season.raa += line.raa
        fielding_season.rto += line.rto
        fielding_season.game += 1

        if s_type == 'Regular':
            this_career.pa += line.pa
            this_career.ab += line.ab
            this_career.run += line.run
            this_career.hit += line.hit
            this_career.rbi += line.rbi
            this_career.double += line.double
            this_career.triple += line.triple
            this_career.hr += line.hr
            this_career.bb += line.bb
            this_career.so += line.so
            this_career.hbp += line.hbp
            this_career.sac += line.sac
            this_career.ibb += line.ibb
            this_career.gidp += line.gidp
            this_career.sb += line.sb
            this_career.cs += line.cs
            this_career.bphr += line.bphr
            this_career.bpfo += line.bpfo
            this_career.bp1b += line.bp1b
            this_career.bplo += line.bplo
            this_career.xba += line.xba
            this_career.xbt += line.xbt
            this_career.game += 1
            fielding_career.xch += line.xch
            fielding_career.xhit += line.xhit
            fielding_career.error += line.error
            fielding_career.pb += line.pb
            fielding_career.sbc += line.sbc
            fielding_career.csc += line.csc
            fielding_career.roba += line.roba
            fielding_career.robs += line.robs
            fielding_career.raa += line.raa
            fielding_career.rto += line.rto
            fielding_career.game += 1

        this_season.save()
        this_career.save()
        fielding_season.save()
        fielding_career.save()

    db.close()
    raise HTTPException(status_code=200, detail=f'{len(all_stats)} of {stats.count} lines have been added')


@app.delete('/api/v1/battingstats')
async def v1_battingstats_delete(game: GameModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to delete batting stats')

    # Get teams
    try:
        away_team = Team.get_by_id(game.away_team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Away team with id {game.away_team_id} not found')
    try:
        home_team = Team.get_by_id(game.home_team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Away team with id {game.home_team_id} not found')

    # Check for matchup
    this_matchup = Schedule.select_season(Current.latest().season).where(
        (Schedule.awayteam == away_team) & (Schedule.hometeam == home_team) & (Schedule.week == game.week)
    )
    if this_matchup.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No game found between {away_team.abbrev} and {home_team.abbrev} '
                                                    f'in week {game.week}')

    # Check for game batting stats
    this_game_stats = BattingStat.select().where(
        (BattingStat.season == game.season) & (BattingStat.week == game.week) & (BattingStat.game == game.game_num) &
        ((BattingStat.team == away_team) | (BattingStat.team == home_team)) & (BattingStat.pa > 0)
    )
    if this_game_stats.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in '
                                                    f'that matchup')

    # # Remove stats from season lines
    # for line in this_game_stats:
    #     this_season = BattingSeason.get_or_none(player=line.player, season=line.player.season)
    #     if not this_season:
    #         db.close()
    #         raise HTTPException(status_code=404, detail=f'Could not find existing batting season stats '
    #                                                     f'for {line.player.name} (ID: {line.player.id})')
    #     this_career = BattingCareer.get_or_none(BattingCareer.name == this_season.player.name)
    #     if not this_career:
    #         this_career = BattingCareer(name=this_season.player.name)
    #         this_career.save()
    #
    #     this_season.pa -= line.pa
    #     # this_career.pa -= line.pa
    #     this_season.ab -= line.ab
    #     # this_career.ab -= line.ab
    #     this_season.run -= line.run
    #     # this_career.run -= line.run
    #     this_season.hit -= line.hit
    #     # this_career.hit -= line.hit
    #     this_season.rbi -= line.rbi
    #     # this_career.rbi -= line.rbi
    #     this_season.double -= line.double
    #     # this_career.double -= line.double
    #     this_season.triple -= line.triple
    #     # this_career.triple -= line.triple
    #     this_season.hr -= line.hr
    #     # this_career.hr -= line.hr
    #     this_season.bb -= line.bb
    #     # this_career.bb -= line.bb
    #     this_season.so -= line.so
    #     # this_career.so -= line.so
    #     this_season.hbp -= line.hbp
    #     # this_career.hbp -= line.hbp
    #     this_season.sac -= line.sac
    #     # this_career.sac -= line.sac
    #     this_season.ibb -= line.ibb
    #     # this_career.ibb -= line.ibb
    #     this_season.gidp -= line.gidp
    #     # this_career.gidp -= line.gidp
    #     this_season.sb -= line.sb
    #     # this_career.sb -= line.sb
    #     this_season.cs -= line.cs
    #     # this_career.cs -= line.cs
    #     this_season.game -= 1
    #     # this_career.game -= 1
    #
    #     this_season.save()
    #     # this_career.save()
    #
    #     this_career.recalculate()
    #
    # # Check for game fielding stats
    # this_game_stats = BattingStat.select().where(
    #     (BattingStat.season == game.season) & (BattingStat.week == game.week) & (BattingStat.game == game.game_num) &
    #     ((BattingStat.team == away_team) | (BattingStat.team == home_team)) & (BattingStat.xch + BattingStat.sbc > 0)
    # )
    # if this_game_stats.count() == 0:
    #     db.close()
    #     raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in '
    #                                                 f'that matchup')
    #
    # # Remove stats from season lines
    # for line in this_game_stats:
    #     this_season = FieldingSeason.get_or_none(player=line.player, season=line.player.season)
    #     if not this_season:
    #         db.close()
    #         raise HTTPException(status_code=404, detail=f'Could not find existing batting season stats '
    #                                                     f'for {line.player.name} (ID: {line.player.id})')
    #     this_career = FieldingCareer.get_or_none(FieldingCareer.name == this_season.player.name,
    #                                              FieldingCareer.pos == line.pos)
    #     if not this_career:
    #         this_career = FieldingCareer(name=this_season.player.name, pos=this_season.pos)
    #         this_career.save()
    #
    #     this_season.xch -= line.xch
    #     # this_career.xch -= line.xch
    #     this_season.xhit -= line.xhit
    #     # this_career.xhit -= line.xhit
    #     this_season.error -= line.error
    #     # this_career.error -= line.error
    #     this_season.pb -= line.pb
    #     # this_career.pb -= line.pb
    #     this_season.sbc -= line.sbc
    #     # this_career.sbc -= line.sbc
    #     this_season.csc -= line.csc
    #     # this_career.csc -= line.csc
    #     this_season.game -= 1
    #     # this_career.game -= 1
    #
    #     this_season.save()
    #     # this_career.save()
    #
    #     this_career.recalculate()

    # Delete the stats
    delete_query = BattingStat.delete().where(
        (BattingStat.season == game.season) & (BattingStat.week == game.week) &
        ((BattingStat.team == away_team) | (BattingStat.team == home_team))
    )
    if game.game_num:
        delete_query = delete_query.where(BattingStat.game == game.game_num)

    count = delete_query.execute()
    db.close()
    if count > 0:
        raise HTTPException(status_code=200, detail=f'Removed {count} batting stat lines')
    else:
        raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
                                                    f'I did not delete any records')


@app.get('/api/v1/pitchingstats/s{season}/{s_type}')
async def v1_pitchingstat_get(
        s_type: str, season: int, team_abbrev: Optional[str] = None,
        player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None,
        week_end: Optional[int] = None, game_num: Optional[int] = None, limit: Optional[int] = None,
        sort: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    :param season: int
    :param s_type: string (regular, post, combined)
    :param team_abbrev: string
    :param player_name: string
    :param player_id: integer, overrides player_name
    :param week_start: integer
    :param week_end: integer
    :param limit: int
    :param sort: string, 'newest' or 'oldest' to get those first
    :return:
    """
    if s_type.lower() == 'post':
        all_stats = PitchingStat.post_season(season)
        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No postseason stats found for season {season}')
    elif s_type.lower() == 'combined':
        all_stats = PitchingStat.combined_season(season)
        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No stats found for season {season}')
    else:
        all_stats = PitchingStat.regular_season(season)
        if not all_stats:
            db.close()
            return {}

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_stats = all_stats.where(PitchingStat.team == this_team)

    if player_name or player_id:
        if player_id:
            try:
                this_player = Player.get_by_id(player_id)
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')
        else:
            this_player = Player.get_season(player_name, season)
            if not this_player:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {player_name} not found')

        all_stats = all_stats.where(PitchingStat.player == this_player)

    start = 1
    end = Current.get(Current.season == season).week
    if week_start is not None:
        start = week_start

    if week_end is not None:
        end = min(week_end, end)

    if start > end:
        db.close()
        raise HTTPException(status_code=404,
                            detail=f'Start week {start} is after end week {end} - cannot pull stats')
    if start + 5 < end and team_abbrev is None and player_name is None and player_id is None and not csv:
        start = end - 5

    all_stats = all_stats.where(
        (PitchingStat.week >= start) & (PitchingStat.week <= end)
    )

    if game_num:
        all_stats = all_stats.where(PitchingStat.game == game_num)

    if limit:
        all_stats = all_stats.limit(limit)

    if sort:
        if sort == 'newest':
            all_stats = all_stats.order_by(-PitchingStat.week, -PitchingStat.game)

    if return_type == 'csv' or csv:
        stats_list = [['pitcher', 'team', 'ip', 'h', 'r', 'er', 'so', 'bb', 'hbp', 'wp', 'bk', 'hr',
                       'gs', 'w', 'l', 'hd', 'sv', 'bs', 'week', 'game', 'code']]
        for line in all_stats:
            stats_list.append(
                [
                    line.player.name, line.team.abbrev, line.ip, line.hit, line.run, line.erun, line.so, line.bb,
                    line.hbp, line.wp, line.balk, line.hr, line.gs, line.win, line.loss, line.hold, line.sv, line.bsv,
                    line.week, line.game, f'{line.week}.{line.game}'
                ]
            )
        return_stats = DataFrame(stats_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')

    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.post('/api/v1/pitchingstats')
async def v1_pitchingstats_post(stats: PitchingStatModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post pitching stats')

    all_stats = []
    season = None
    week = None
    teams = []
    game_num = None

    for x in stats.stats:
        team = Team.get_by_id(x.team_id)
        if not team:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {x.team_id}')

        player = Player.get_by_id(x.player_id)
        if not player:
            db.close()
            raise HTTPException(status_code=404, detail=f'No player found with id {x.player_id}')

        # Helpers for query
        if team not in teams:
            teams.append(team)
        if not season:
            season = x.season
        if not week:
            week = x.week
        if not game_num:
            game_num = x.game

        new_stats = PitchingStat(
            player=player,
            team=team,
            ip=x.ip,
            hit=x.hit,
            run=x.run,
            erun=x.erun,
            so=x.so,
            bb=x.bb,
            hbp=x.hbp,
            wp=x.wp,
            balk=x.balk,
            hr=x.hr,
            gs=x.gs,
            win=x.win,
            loss=x.loss,
            hold=x.hold,
            sv=x.sv,
            bsv=x.bsv,
            ir=x.ir,
            irs=x.irs,
            week=x.week,
            game=x.game,
            season=x.season
        )
        all_stats.append(new_stats)

    with db.atomic():
        PitchingStat.bulk_create(all_stats, batch_size=20)

    this_game_stats = PitchingStat.select().where(
        (PitchingStat.season == season) & (PitchingStat.week == week) & (PitchingStat.game == game_num) &
        ((PitchingStat.team == teams[0]) | (PitchingStat.team == teams[1]))
    )

    # Update season stats with freshly submitted for regular season games
    if (season > 2) and week <= 22:
        s_type = 'Regular'
    else:
        s_type = 'Post'

    for line in this_game_stats:
        this_season = PitchingSeason.get_or_none(
            player=line.player, season=line.season, season_type=s_type
        )
        if not this_season:
            this_season = PitchingSeason(player=line.player, season=line.season, season_type=s_type)
            this_season.save()

        this_career = PitchingCareer.get_or_none(name=line.player.name)
        if not this_career:
            this_career = PitchingCareer(name=line.player.name)
            this_career.save()

        this_season.ip += line.ip
        this_season.hit += line.hit
        this_season.run += line.run
        this_season.erun += line.erun
        this_season.so += line.so
        this_season.bb += line.bb
        this_season.hbp += line.hbp
        this_season.wp += line.wp
        this_season.balk += line.balk
        this_season.hr += line.hr
        this_season.ir += line.ir
        this_season.irs += line.irs
        this_season.gs += line.gs
        this_season.win += line.win
        this_season.loss += line.loss
        this_season.hold += line.hold
        this_season.sv += line.sv
        this_season.bsv += line.bsv
        this_season.game += 1

        if s_type == 'Regular':
            this_career.ip += line.ip
            this_career.hit += line.hit
            this_career.run += line.run
            this_career.erun += line.erun
            this_career.so += line.so
            this_career.bb += line.bb
            this_career.hbp += line.hbp
            this_career.wp += line.wp
            this_career.balk += line.balk
            this_career.hr += line.hr
            this_career.ir += line.ir
            this_career.irs += line.irs
            this_career.gs += line.gs
            this_career.win += line.win
            this_career.loss += line.loss
            this_career.hold += line.hold
            this_career.sv += line.sv
            this_career.bsv += line.bsv
            this_career.game += 1

        this_season.save()
        this_career.save()

    db.close()
    raise HTTPException(status_code=200, detail=f'{len(all_stats)} of {stats.count} lines have been added')


@app.delete('/api/v1/pitchingstats')
async def v1_pitchingstats_delete(game: GameModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to delete pitching stats')

    # Get teams
    try:
        away_team = Team.get_by_id(game.away_team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Away team with id {game.away_team_id} not found')
    try:
        home_team = Team.get_by_id(game.home_team_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Away team with id {game.home_team_id} not found')

    # Check for matchup
    this_matchup = Schedule.select_season(Current.latest().season).where(
        (Schedule.awayteam == away_team) & (Schedule.hometeam == home_team) & (Schedule.week == game.week)
    )
    if this_matchup.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No game found between {away_team.abbrev} and {home_team.abbrev} '
                                                    f'in week {game.week}')

    # # Check for game stats
    # this_game_stats = PitchingStat.select().where(
    #     (PitchingStat.season == game.season) & (PitchingStat.week == game.week) &
    #     ((PitchingStat.team == away_team) | (PitchingStat.team == home_team))
    # )
    # if game.game_num:
    #     this_game_stats = this_game_stats.where(PitchingStat.game == game.game_num)
    # if this_game_stats.count() == 0:
    #     db.close()
    #     raise HTTPException(status_code=404, detail=f'No stats found for {away_team.abbrev} at {home_team.abbrev} in '
    #                                                 f'that matchup')
    #
    # # Remove stats from season lines
    # for line in this_game_stats:
    #     this_season = PitchingSeason.get_or_none(player=line.player, season=line.player.season)
    #     if not this_season:
    #         db.close()
    #         raise HTTPException(status_code=404, detail=f'Could not find existing pitching season stats '
    #                                                     f'for {line.player.name} (ID: {line.player.id})')
    #     this_career = PitchingCareer.get_or_none(PitchingCareer.name == this_season.player.name)
    #     if not this_career:
    #         this_career = PitchingCareer(name=this_season.player.name)
    #         this_career.save()
    #
    #     # this_career.ip -= line.ip
    #     this_season.ip -= line.ip
    #     # this_career.hit -= line.hit
    #     this_season.hit -= line.hit
    #     # this_career.run -= line.run
    #     this_season.run -= line.run
    #     # this_career.erun -= line.erun
    #     this_season.erun -= line.erun
    #     # this_career.so -= line.so
    #     this_season.so -= line.so
    #     # this_career.bb -= line.bb
    #     this_season.bb -= line.bb
    #     # this_career.hbp -= line.hbp
    #     this_season.hbp -= line.hbp
    #     # this_career.wp -= line.wp
    #     this_season.wp -= line.wp
    #     # this_career.balk -= line.balk
    #     this_season.balk -= line.balk
    #     # this_career.hr -= line.hr
    #     this_season.hr -= line.hr
    #     # this_career.gs -= line.gs
    #     this_season.gs -= line.gs
    #     # this_career.win -= line.win
    #     this_season.win -= line.win
    #     # this_career.loss -= line.loss
    #     this_season.loss -= line.loss
    #     # this_career.hold -= line.hold
    #     this_season.hold -= line.hold
    #     this_season.sv -= line.sv
    #     # this_career.sv -= line.sv
    #     this_season.bsv -= line.bsv
    #     # this_career.bsv -= line.bsv
    #     this_season.game -= 1
    #     # this_career.game -= 1
    #
    #     this_season.save()
    #     # this_career.save()
    #
    #     this_career.recalculate()

    # Delete the stats
    delete_query = PitchingStat.delete().where(
        (PitchingStat.season == game.season) & (PitchingStat.week == game.week) &
        ((PitchingStat.team == away_team) | (PitchingStat.team == home_team))
    )
    if game.game_num:
        delete_query = delete_query.where(PitchingStat.game == game.game_num)

    count = delete_query.execute()
    db.close()
    if count > 0:
        raise HTTPException(status_code=200, detail=f'Removed {count} pitching stat lines')
    else:
        raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
                                                    f'I did not delete any records')


@app.get('/api/v1/fieldingstats/s{season}/{s_type}')
async def v1_fieldingstats_get(
        s_type: str, season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None,
        player_name: Optional[str] = None, player_id: Optional[int] = None, week_start: Optional[int] = None,
        week_end: Optional[int] = None, position: Optional[str] = None, return_type: Optional[str] = 'json',
        csv: Optional[bool] = False):
    """
    :param season: int
    :param s_type: string (regular, post, combined)
    :param team_abbrev: string
    :param player_name: string
    :param player_id: integer, overrides player_name
    :param week_start: integer
    :param week_end: integer
    :param position: string
    :return:
    """
    if s_type.lower() == 'post':
        all_stats = BattingStat.post_season(season)
        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No postseason stats found for season {season}')
    elif s_type.lower() == 'combined':
        all_stats = BattingStat.combined_season(season)
        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No stats found for season {season}')
    else:
        all_stats = BattingStat.regular_season(season)
        if not all_stats:
            db.close()
            return {}

    all_stats = all_stats.where(
        (BattingStat.xch > 0) | (BattingStat.pb > 0) | (BattingStat.sbc > 0)
    )

    if position:
        all_stats = all_stats.where(BattingStat.pos == position.upper())

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_stats = all_stats.where(BattingStat.team == this_team)

    if player_name or player_id:
        if player_id:
            try:
                this_player = Player.get_by_id(player_id)
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')
        else:
            this_player = Player.get_season(player_name, season)
            if not this_player:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {player_name} not found')

        all_stats = all_stats.where(BattingStat.player == this_player)

    start = 1
    end = Current.get(Current.season == season).week
    if week_start is not None:
        start = week_start

    if week_end is not None:
        end = min(week_end, end)

    if start > end:
        db.close()
        raise HTTPException(status_code=404,
                            detail=f'Start week {start} is after end week {end} - cannot pull stats')
    if start + 5 < end and team_abbrev is None and player_name is None and player_id is None and not csv:
        start = end - 5

    all_stats = all_stats.where(
        (BattingStat.week >= start) & (BattingStat.week <= end)
    )

    if return_type == 'csv' or csv:
        stats_list = [['season', 'week', 'game_num', 'player', 'team', 'pos', 'xch', 'xhit', 'error',
                       'pb', 'sbc', 'csc', 'roba', 'robs', 'raa', 'rto']]
        for line in all_stats:
            stats_list.append(
                [
                    line.season, line.week, line.game, line.player.name, line.team.abbrev, line.pos, line.xch,
                    line.xhit, line.error, line.pb, line.sbc, line.csc, line.roba, line.robs, line.raa, line.rto
                ]
            )
        return_stats = DataFrame(stats_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.post('/api/v1/battingseasons/recalculate')
async def v1_battingseasons_recalculate(
        season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats')

    all_players = BattingSeason.select()

    if season:
        all_players = all_players.where(BattingSeason.season == season)
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            all_players = all_players.join(Player).where(BattingSeason.player.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
    if player_id:
        try:
            this_player = Player.get_by_id(player_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')

        all_players = all_players.where(BattingSeason.player == this_player)
        if all_players.count() == 0:
            if BattingStat.select().where(BattingStat.player == this_player).count() > 0:
                this_season = BattingSeason(player=this_player, season=this_player.season)
                this_season.save()
            else:
                db.close()
                raise HTTPException(status_code=404, detail=f'No batting stats found for player id {player_id}')

    for line in all_players:
        count = line.recalculate()

    db.close()
    raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} batting seasons')


@app.post('/api/v1/pitchingseasons/recalculate')
async def v1_pitchingseasons_recalculate(
        season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats')

    all_players = PitchingSeason.select()

    if season:
        all_players = all_players.where(PitchingSeason.season == season)
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            all_players = all_players.join(Player).where(PitchingSeason.player.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
    if player_id:
        try:
            this_player = Player.get_by_id(player_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')

        all_players = all_players.where(PitchingSeason.player == this_player)
        if all_players.count() == 0:
            if PitchingStat.select().where(PitchingStat.player == this_player).count() > 0:
                this_season = PitchingSeason(player=this_player, season=this_player.season)
                this_season.save()
            else:
                db.close()
                raise HTTPException(status_code=404, detail=f'No pitching stats found for player id {player_id}')

    for line in all_players:
        count = line.recalculate()

    db.close()
    raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} pitching seasons')


@app.post('/api/v1/fieldingseasons/recalculate')
async def v1_fieldingseasons_recalculate(
        season: Optional[int] = None, team_id: Optional[int] = None, player_id: Optional[int] = None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats')

    all_players = FieldingSeason.select()

    if season:
        all_players = all_players.where(FieldingSeason.season == season)
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            all_players = all_players.join(Player).where(FieldingSeason.player.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')
    if player_id:
        try:
            this_player = Player.get_by_id(player_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')

        all_players = all_players.where(FieldingSeason.player == this_player)
        if all_players.count() == 0:
            search_query = BattingStat.select().where(
                (BattingStat.player == this_player) & (BattingStat.xch + BattingStat.sbc > 0)
            )
            if search_query.count() > 0:
                pos_list = []
                for x in search_query:
                    pos_list.append(x.pos)
                for x in pos_list:
                    this_season = FieldingSeason(player=this_player, season=this_player.season, pos=x)
                    this_season.save()
            else:
                db.close()
                raise HTTPException(status_code=404, detail=f'No fielding stats found for player id {player_id}')

    for line in all_players:
        count = line.recalculate()
        logging.info(count)

    db.close()
    raise HTTPException(status_code=200, detail=f'Recalculated {all_players.count()} fielding seasons')


# @app.post('/api/v1/seasonstats/recalculate')
# async def v1_seasonstats_recalculate(
#         which: str, season_start: int, season_end: int, mgr_start: int, mgr_end: int,
#         token: str = Depends(oauth2_scheme)):
#     if not valid_token(token):
#         db.close()
#         raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats')
#
#     if mgr_start > mgr_end:
#         db.close()
#         raise HTTPException(status_code=404, detail=f'mgr_start may not be greater than mgr_end')
#     if season_start > season_end:
#         db.close()
#         raise HTTPException(status_code=404, detail=f'season_start may not be greater than season_end')
#     if which not in ['batting', 'fielding', 'pitching']:
#         db.close()
#         raise HTTPException(status_code=404, detail=f'Invalid which value')
#
#     x = season_start
#     while x <= season_end:
#         y = mgr_start
#         while y <= mgr_end:
#             if which == 'batting':
#                 BattingSeason.recalculate(x, y)
#             elif which == 'pitching':
#                 PitchingSeason.recalculate(x, y)
#             elif which == 'fielding':
#                 FieldingSeason.recalculate(x, y)
#
#     db.close()
#     raise HTTPException(status_code=200, detail='Season stats have been recalculated')


@app.post('/api/v1/careerstats/recalculate')
async def v1_careerstats_recalculate(which: str, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to recalculate stats')

    if which == 'all':
        BattingCareer.recalculate()
        PitchingCareer.recalculate()
        FieldingCareer.recalculate()
    elif which == 'batting':
        BattingCareer.recalculate()
    elif which == 'pitching':
        PitchingCareer.recalculate()
    elif which == 'fielding':
        FieldingCareer.recalculate()

    db.close()
    raise HTTPException(status_code=200, detail='Career stats have been recalculated')


@app.get('/api/v1/standings')
async def v1_standings(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, return_type: Optional[str] = 'json',
        league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None, csv: Optional[bool] = False):
    """
    :param season: int
    :param team_abbrev: string, overrides league/division_abbrev
    :param league_abbrev: string
    :param division_abbrev: string
    """
    standings = Standings.select_season(season)

    if standings.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No output for season {season}')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        db.close()
        return model_to_dict(Standings.get_or_none(Standings.team == this_team))

    if league_abbrev:
        these_divisions = Division.select().where(fn.Lower(Division.league_abbrev) == league_abbrev.lower())
        if these_divisions.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for league {league_abbrev}')

        standings = standings.where(Standings.team.division << these_divisions)

    if division_abbrev:
        this_division = Division.select().where(fn.Lower(Division.division_abbrev) == division_abbrev.lower())
        if not this_division:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for division {division_abbrev}')

        standings = standings.where(Standings.team.division << this_division)

    def win_pct(this_team_stan):
        if this_team_stan.wins + this_team_stan.losses == 0:
            return 0
        else:
            return (this_team_stan.wins / (this_team_stan.wins + this_team_stan.losses)) + \
                   (this_team_stan.run_diff * .000001)

    div_teams = [team_stan for team_stan in standings]
    div_teams.sort(key=lambda team: win_pct(team), reverse=True)

    if return_type == 'csv' or csv:
        standings_list = [
            ['season', 'team', 'league', 'division', 'wins', 'losses', 'run diff', 'div gb', 'div enum', 'wc gb',
             'wc enum', 'home wins', 'home losses', 'away wins', 'away losses', 'last 8 wins', 'last 8 losses',
             'streak wl', 'streak num', 'one run wins', 'one run losses', 'pythag_wins', 'pythag losses',
             'wins v bote', 'losses v bote', 'wins v dc', 'losses v dc', 'wins v nlw', 'losses v nlw']
        ]
        for line in div_teams:
            standings_list.append(
                [line.team.season, line.team.abbrev, line.team.division.league_abbrev, line.team.division.division_name,
                 line.wins, line.losses, line.run_diff, line.div_gb, line.div_e_num, line.wc_gb, line.wc_e_num,
                 line.home_wins, line.home_losses, line.away_wins, line.away_losses, line.last8_wins, line.last8_losses,
                 line.streak_wl, line.streak_num, line.one_run_wins, line.one_run_losses, line.pythag_wins,
                 line.pythag_losses, line.div1_wins, line.div1_losses, line.div2_wins, line.div2_losses, line.div3_wins,
                 line.div3_losses]
            )
        return_stats = DataFrame(standings_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_standings = {}
        for line in div_teams:
            return_standings[f'{line.team.abbrev}'] = model_to_dict(line)

        db.close()
        return return_standings


@app.patch('/api/v1/standings/{stan_id}')
async def v1_standings_patch(
        stan_id, wins: Optional[int] = None, losses: Optional[int] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch standings')

    try:
        this_stan = Standings.get_by_id(stan_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {stan_id}')

    if wins:
        this_stan.wins = wins
    if losses:
        this_stan.losses = losses

    this_stan.save()
    db.close()

    return model_to_dict(this_stan)


@app.post('/api/v1/standings/s{season}/recalculate')
async def v1_standings_recalculate(season: int, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to calculate stats')

    code = Standings.recalculate(season)
    db.close()
    if code == 69:
        HTTPException(status_code=500, detail=f'Error recreating Standings rows')
    raise HTTPException(status_code=200, detail=f'Just recalculated standings for season {season}')


@app.get('/api/v1/battingseasons')
async def v1_battingseasons_get(
        season: Optional[int] = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None,
        season_type: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    :param season: int
    :param player_id: int, overrides other args
    :param player_name: int, overrides team_abbrev
    :param team_abbrev: str,  lowest priority arg
    :param sort: str, pa-desc; wara-desc
    """
    all_stats = BattingSeason.select().where(BattingSeason.pa > 0)

    if season:
        all_stats = all_stats.where(BattingSeason.season == season)

    if all_stats.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No output for season {season}')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = Player.select_season(season).where(Player.team == this_team)
        all_stats = all_stats.where(BattingSeason.player << all_players)

        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}')

    if season_type:
        all_stats = all_stats.where(fn.Lower(BattingSeason.season_type) == season_type.lower())

    if sort:
        if sort == 'pa-desc':
            all_stats = all_stats.order_by(-BattingSeason.pa)
        elif sort == 'wara-desc':
            all_stats = all_stats.order_by(-BattingSeason.player.wara)

    if return_type == 'csv' or csv:
        stat_list = [
            ['season', 'season_type', 'player', 'pa', 'ab', 'run', 'hit', 'rbi', 'double', 'triple',
             'hr', 'bb', 'so', 'hbp', 'sac', 'ibb', 'gidp', 'sb', 'cs', 'bphr', 'bpfo', 'bp1b', 'bplo',
             'xba', 'xbt']
        ]
        for line in all_stats:
            stat_list.append(
                [line.player.season, line.season_type, line.player.name, line.pa, line.ab, line.run, line.hit,
                 line.rbi, line.double, line.triple, line.hr, line.bb, line.so, line.hbp, line.sac, line.ibb, line.gidp,
                 line.sb, line.cs, line.bphr, line.bpfo, line.bp1b, line.bplo, line.xba, line.xbt]
            )
        return_stats = DataFrame(stat_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/battingseasons/{id_or_name}')
async def v1_battingseasons_get_player(id_or_name):
    try:
        this_player = Player.get_by_id(id_or_name)
    except Exception as e:
        this_player = None

    if this_player:
        these_stats = BattingSeason.get_or_none(BattingSeason.player == this_player)
        if these_stats:
            db.close()
            return model_to_dict(these_stats)
        else:
            db.close()
            return {}
    else:
        these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower())
        if these_players.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found')

        these_stats = BattingSeason.select().where(BattingSeason.player << these_players)
        return_stats = {}
        for line in these_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/pitchingseasons')
async def v1_pitchingseasons_get(
        season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, sort: Optional[str] = None,
        season_type: Optional[str] = None, return_type: Optional[str] = 'json', csv: Optional[bool] = False):
    """
    :param season: int
    :param team_abbrev: str,  lowest priority arg
    :param sort: str, ip-desc; wara-desc
    """
    all_stats = PitchingSeason.select_season(season)

    if season:
        all_stats = all_stats.where(PitchingSeason.season == season)

    if all_stats.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No output for season {season}')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = Player.select_season(season).where(Player.team == this_team)
        all_stats = all_stats.where(PitchingSeason.player << all_players)

        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}')

    if season_type:
        all_stats = all_stats.where(fn.Lower(PitchingSeason.season_type) == season_type.lower())

    if sort:
        logging.warning('In sort!')
        if sort == 'ip-desc':
            logging.warning('Matched ip-desc')
            all_stats = all_stats.order_by(-PitchingSeason.ip)
            logging.warning(all_stats)
        elif sort == 'wara-desc':
            all_stats = all_stats.order_by(-PitchingSeason.player.wara)

    if return_type == 'csv' or csv:
        stat_list = [
            ['season', 'season_type', 'player', 'ip', 'hit', 'run', 'erun', 'so', 'bb', 'hbp',
             'wp', 'balk', 'hr', 'ir', 'irs', 'gs', 'win', 'loss', 'hold', 'sv', 'bsv', 'games']
        ]
        for line in all_stats:
            stat_list.append(
                [line.player.season, line.season_type, line.player.name, line.ip, line.hit, line.run, line.erun,
                 line.so, line.bb, line.hbp, line.wp, line.balk, line.hr, line.ir, line.irs, line.gs, line.win,
                 line.loss, line.hold, line.sv, line.bsv, line.game]
            )
        return_stats = DataFrame(stat_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/pitchingseasons/{id_or_name}')
async def v1_pitchingseasons_get_player(id_or_name):
    try:
        this_player = Player.get_by_id(id_or_name)
    except Exception as e:
        this_player = None

    if this_player:
        these_stats = PitchingSeason.get_or_none(PitchingSeason.player == this_player)
        if these_stats:
            db.close()
            return model_to_dict(these_stats)
        else:
            db.close()
            return {}
    else:
        these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower())
        if these_players.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found')

        these_stats = PitchingSeason.select().where(PitchingSeason.player << these_players)
        return_stats = {}
        for line in these_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/fieldingseasons')
async def v1_fieldingseasons_get(
        season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None, position: Optional[str] = None,
        season_type: Optional[str] = None, sort: Optional[str] = None, return_type: Optional[str] = 'json',
        csv: Optional[bool] = False):
    """
    :param season: int
    :param player_id: int, overrides other args
    :param player_name: int, overrides team_abbrev
    :param team_abbrev: str,  lowest priority arg
    :param position: str
    :param sort: str, pa-desc; wara-desc
    """
    all_stats = FieldingSeason.select_season(season).where((FieldingSeason.xch + FieldingSeason.sbc) > 0)

    if season:
        all_stats = all_stats.where(FieldingSeason.season == season)

    if position:
        all_stats = all_stats.where(FieldingSeason.pos == position.upper())

    if all_stats.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No output for season {season}')

    if team_abbrev:
        this_team = Team.get_season(team_abbrev, season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {team_abbrev} not found')

        all_players = Player.select_season(season).where(Player.team == this_team)
        all_stats = all_stats.where(FieldingSeason.player << all_players)

        if all_stats.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for team {team_abbrev}')

    if season_type:
        all_stats = all_stats.where(fn.Lower(FieldingSeason.season_type) == season_type.lower())

    if sort:
        if sort == 'xch-desc':
            all_stats = all_stats.order_by(-(FieldingSeason.xch + FieldingSeason.sbc))
        elif sort == 'wara-desc':
            all_stats = all_stats.order_by(-FieldingSeason.player.wara)

    if return_type == 'csv' or csv:
        stat_list = [
            ['season', 'season_type', 'player', 'pos', 'xch', 'xhit', 'error', 'pb', 'sbc', 'csc', 'roba', 'robs',
             'raa', 'rto']
        ]
        for line in all_stats:
            stat_list.append(
                [line.player.season, line.season_type, line.player.name, line.pos, line.xch, line.xhit, line.error,
                 line.pb, line.sbc, line.csc, line.roba, line.robs, line.raa, line.rto]
            )
        return_stats = DataFrame(stat_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_stats, media_type='text/csv')
    else:
        return_stats = {}
        for line in all_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/fieldingseasons/{id_or_name}')
async def v1_fieldingseasons_get_player(id_or_name):
    try:
        this_player = Player.get_by_id(id_or_name)
    except Exception as e:
        this_player = None

    if this_player:
        these_stats = FieldingSeason.get_or_none(FieldingSeason.player == this_player)
        if these_stats:
            db.close()
            return model_to_dict(these_stats)
        else:
            db.close()
            return {}
    else:
        these_players = Player.select().where(fn.Lower(Player.name) == id_or_name.lower())
        if these_players.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player {id_or_name} not found')

        these_stats = FieldingSeason.select().where(FieldingSeason.player << these_players)
        return_stats = {}
        for line in these_stats:
            return_stats[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_stats


@app.get('/api/v1/draftpicks')
async def v1_draftpicks_get(
        season: int = SEASON_DEFAULT, owner_team_abbrev: Optional[str] = None,
        orig_team_abbrev: Optional[str] = None, pick_round_start: Optional[int] = None,
        pick_round_end: Optional[int] = None, traded: Optional[bool] = None, overall: Optional[int] = None,
        team_season: Optional[int] = None, overall_start: Optional[int] = None, overall_end: Optional[int] = None,
        return_type = 'json', csv: Optional[bool] = False):
    """
    :param season: int
    :param owner_team_abbrev: string
    :param orig_team_abbrev: string
    :param pick_round_start: int
    :param pick_round_end: int
    :param traded: bool
    :param overall: int
    """
    all_picks = DraftPick.select_season(season)
    if all_picks.count() == 0:
        db.close()
        raise HTTPException(status_code=404, detail=f'No picks found for season {season}')

    if owner_team_abbrev:
        if not team_season:
            team_season = season
        this_team = Team.get_season(owner_team_abbrev, team_season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {owner_team_abbrev} not found')

        all_picks = all_picks.where(DraftPick.owner == this_team)
        if all_picks.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for team {owner_team_abbrev}')

    if orig_team_abbrev:
        if not team_season:
            team_season = season
        this_team = Team.get_season(orig_team_abbrev, team_season)
        if not this_team:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team {orig_team_abbrev} not found')

        all_picks = all_picks.where(DraftPick.origowner == this_team)
        if all_picks.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No output for team {orig_team_abbrev}')

    if pick_round_start and pick_round_end:
        if pick_round_start > pick_round_end:
            db.close()
            raise HTTPException(status_code=400, detail=f'Cannot search for pick with end round before start round')

    if pick_round_start:
        all_picks = all_picks.where(DraftPick.round >= pick_round_start)
        if all_picks.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No picks found beginning in round {pick_round_start}')

    if pick_round_end:
        all_picks = all_picks.where(DraftPick.round <= pick_round_end)
        if all_picks.count() == 0:
            db.close()
            raise HTTPException(status_code=404, detail=f'No picks found ending with round {pick_round_end}')

    if traded:
        all_picks = all_picks.where(DraftPick.origowner != DraftPick.owner)

    if overall is not None:
        all_picks = all_picks.where(DraftPick.overall == overall)

    if overall_start:
        all_picks = all_picks.where(DraftPick.overall >= overall_start)

    if overall_end:
        all_picks = all_picks.where(DraftPick.overall <= overall_end)

    logging.info('Trying to spit out draft picks')
    if return_type == 'csv' or csv:
        logging.info('Working on csv')
        pick_list = [
            ['season', 'overall', 'round', 'orig_owner', 'owner', 'player', 'swar']
        ]
        logging.info(f'built the pick_list / len(all_picks): {len(all_picks)}')
        for line in all_picks:
            logging.info(f'line: {line}')
            pick_list.append(
                [line.season, line.overall, line.round, line.origowner.abbrev, line.owner.abbrev,
                 f'{line.player.name if line.player else ""}', f'{line.player.wara if line.player else ""}']
            )
        logging.info('filled the pick_list')
        return_picks = DataFrame(pick_list).to_csv(header=False, index=False)

        db.close()
        return Response(content=return_picks, media_type='text/csv')
    else:
        return_picks = {}
        for line in all_picks:
            return_picks[f'{line.id}'] = model_to_dict(line)

        db.close()
        return return_picks


@app.post('/api/v1/draftpicks')
async def v1_draftpicks_post(picks: DraftPickModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post draft picks')
    all_picks = []

    for x in picks.picks:
        this_origowner = None
        if x.origowner_id:
            try:
                this_origowner = Team.get_by_id(x.origowner_id)
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'No owner found with id {x.origowner_id}')

        this_owner = None
        if x.owner_id:
            try:
                this_owner = Team.get_by_id(x.owner_id)
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'No owner found with id {x.owner_id}')
        else:
            this_owner = this_origowner

        this_player = None
        if x.player_id:
            try:
                this_player = Player.get_by_id(x.player_id)
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'No player found with id {x.player_id}')

        this_pick = DraftPick(
            season=x.season,
            overall=x.overall,
            round=x.round,
            origowner=this_origowner,
            owner=this_owner,
            player=this_player
        )

        all_picks.append(this_pick)

    with db.atomic():
        DraftPick.bulk_create(all_picks, batch_size=15)
    db.close()

    raise HTTPException(status_code=200, detail=f'Created {len(all_picks)} draft picks')


# player_id is not checked for [int] to enable False as a clear parameter
@app.patch('/api/v1/draftpicks/{pick_id}')
async def v1_draftpicks_patch(
        pick_id: int, overall: Optional[int] = None, round: Optional[int] = None, orig_owner_id: Optional[int] = None,
        owner_id: Optional[int] = None, season: Optional[int] = None, player_id=None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch draft picks')

    try:
        this_pick = DraftPick.get_by_id(pick_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'No team found with id {pick_id}')

    if overall:
        this_pick.overall = overall
    if season:
        this_pick.season = season
    if round:
        this_pick.round = round
    if orig_owner_id:
        try:
            orig_owner = Team.get_by_id(orig_owner_id)
            this_pick.origowner = orig_owner
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {orig_owner_id}')
    if owner_id:
        try:
            new_owner = Team.get_by_id(owner_id)
            this_pick.owner = new_owner
        except Exception as e:
            db.close()
            raise HTTPException(status_code=404, detail=f'No team found with id {owner_id}')
    if player_id is not None:
        if player_id.lower() == 'false':
            this_pick.player = None
        else:
            try:
                this_player = Player.get_by_id(player_id)
                this_pick.player = this_player
            except Exception as e:
                db.close()
                raise HTTPException(status_code=404, detail=f'No player found with id {player_id}')

    this_pick.save()
    db.close()
    return model_to_dict(this_pick)


@app.get('/api/v1/draftpicks/{pick_id}')
async def v1_draftpicks_get_one(pick_id):
    try:
        this_pick = DraftPick.get_or_none(DraftPick.id == pick_id)
    except Exception as e:
        db.close()
        raise HTTPException(status_code=404, detail=f'Draft pick id {pick_id} not found')

    db.close()
    return model_to_dict(this_pick)


@app.get('/api/v1/draftdata')
async def v1_draftdata():
    draft_data = DraftData.get_or_none()
    db.close()

    if not draft_data:
        db.close()
        raise HTTPException(status_code=404, detail=f'No draft data found')
    else:
        db.close()
        return model_to_dict(draft_data)


@app.patch('/api/v1/draftdata')
async def v1_draftdata_patch(
        currentpick: Optional[int] = None, timer: Optional[bool] = None,
        pick_deadline: Optional[datetime.datetime] = None, result_channel: Optional[int] = None,
        ping_channel: Optional[int] = None, pick_minutes: Optional[int] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch draftdata')

    draft_data = DraftData.get_or_none()

    if not draft_data:
        db.close()
        raise HTTPException(status_code=404, detail=f'No draft data found')

    if currentpick is not None:
        draft_data.currentpick = currentpick
    if timer is not None:
        draft_data.timer = timer
    if pick_deadline is not None:
        draft_data.pick_deadline = pick_deadline
    if result_channel is not None:
        draft_data.result_channel = result_channel
    if ping_channel is not None:
        draft_data.ping_channel = ping_channel
    if pick_minutes is not None:
        draft_data.pick_minutes = pick_minutes

    saved = draft_data.save()
    db.close()

    if saved == 1:
        return model_to_dict(draft_data)
    else:
        raise HTTPException(status_code=500, detail='Updating draft data failed')


@app.get('/api/v1/leaders')
async def v1_leaders(
        group: str = 'batting', season=SEASON_DEFAULT, team_abbrev: Optional[str] = None,
        league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None):
    current = Current.get_or_none(season=season)
    all_leaders = None
    if season == 1:
        min_ip = 68
        min_pa = 204
        min_ch_high = 34
        min_ch_mid = 17
        min_ch_low = 8
        min_ch_pit = 4
    elif season == 2:
        min_ip = 76
        min_pa = 228
        min_ch_high = 38
        min_ch_mid = 19
        min_ch_low = 9
        min_ch_pit = 4
    elif int(season) < SEASON_DEFAULT:
        min_ip = 88
        min_pa = 264
        min_ch_high = 44
        min_ch_mid = 22
        min_ch_low = 11
        min_ch_pit = 5
    else:
        min_ip = ((current.week - 1) * 4) if current.week > 5 else current.week * 2
        min_pa = ((current.week - 1) * 12) if current.week > 5 else current.week * 6
        min_ch_high = ((current.week - 1) * 2) if current.week > 5 else current.week
        min_ch_mid = (min_ch_high / 2) if (min_ch_high / 2) > 1 else 1
        min_ch_low = (min_ch_high / 4) if (min_ch_high / 3) > 1 else 1
        min_ch_pit = (min_ch_high / 8) if (min_ch_high / 6) > 1 else 1

    if group == 'pitching':
        all_leaders = {
            'Starter Wins': {'abbrev': 'W', 'leaders': []},
            'Wins': {'abbrev': 'W', 'leaders': []},
            'Earned Run Average': {'abbrev': 'ERA', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Innings Pitched': {'abbrev': 'IP', 'leaders': []},
            'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []},
            'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []},
            'Saves': {'abbrev': 'SV', 'leaders': []},
            'Walks Allowed': {'abbrev': 'BB', 'leaders': []},
            'Runs Allowed': {'abbrev': 'R', 'leaders': []},
            'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []},
            'Blown Saves': {'abbrev': 'BSV', 'leaders': []},
            'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []},
            'Hits Allowed': {'abbrev': 'H', 'leaders': []},
            'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []},
            'Wild Pitches': {'abbrev': 'WP', 'leaders': []},
            'Holds': {'abbrev': 'HD', 'leaders': []},
            'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []},
            'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []},
            'Losses': {'abbrev': 'L', 'leaders': []},
            'Inherited Runners': {'abbrev': 'IR', 'leaders': []},
            'Inherited Runners Scored': {'abbrev': 'IRS', 'leaders': []},
            'Runners Stranded %': {'abbrev': 'RS%', 'leaders': []},
        }

        top_swin = (PitchingStat
                    .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin'))
                    .where(PitchingStat.season == season, PitchingStat.gs == 1, PitchingStat.win == 1)
                    .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player)
                    .group_by(PitchingStat.player)
                    .limit(10))
        top_win = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.win)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.win, PitchingSeason.player)
                   .limit(10))
        top_ip = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.ip)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.ip, PitchingSeason.player)
                  .limit(10))
        top_so = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.so)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.so, PitchingSeason.player)
                  .limit(10))
        top_era = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.erun, PitchingSeason.ip)
                   .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip)
                   .order_by(((PitchingSeason.erun * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_kpn = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.so, PitchingSeason.ip)
                   .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip)
                   .order_by(-((PitchingSeason.so * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_whip = (PitchingSeason
                    .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.hit, PitchingSeason.ip)
                    .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip)
                    .order_by(+((PitchingSeason.bb + PitchingSeason.hit) / PitchingSeason.ip), PitchingSeason.player)
                    .limit(10))
        top_sv = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.sv)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.sv, PitchingSeason.player)
                  .limit(10))
        top_bb = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.bb)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.bb, PitchingSeason.player)
                  .limit(10))
        top_run = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.run)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.run, PitchingSeason.player)
                   .limit(10))
        top_erun = (PitchingSeason
                    .select(PitchingSeason.player, PitchingSeason.erun)
                    .where(PitchingSeason.season == season)
                    .order_by(-PitchingSeason.erun, PitchingSeason.player)
                    .limit(10))
        top_bsv = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bsv)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.bsv, PitchingSeason.player)
                   .limit(10))
        top_hbp = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.hbp)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.hbp, PitchingSeason.player)
                   .limit(10))
        top_hit = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.hit)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.hit, PitchingSeason.player)
                   .limit(10))
        top_hr = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.hr)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.hr, PitchingSeason.player)
                  .limit(10))
        top_wp = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.wp)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.wp, PitchingSeason.player)
                  .limit(10))
        top_hd = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.hold)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.hold, PitchingSeason.player)
                  .limit(10))
        top_bpn = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.ip)
                   .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip)
                   .order_by(((PitchingSeason.bb * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_kpw = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.so)
                   .where(PitchingSeason.season == season, PitchingSeason.ip >= min_ip)
                   .order_by(-(PitchingSeason.so / PitchingSeason.bb), PitchingSeason.player)
                   .limit(10))
        top_los = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.loss)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.loss, PitchingSeason.player)
                   .limit(10))
        top_ir = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.ir)
                  .where(PitchingSeason.season == season)
                  .order_by(-PitchingSeason.ir, PitchingSeason.player)
                  .limit(10))
        top_irs = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.irs)
                   .where(PitchingSeason.season == season)
                   .order_by(-PitchingSeason.irs, PitchingSeason.player)
                   .limit(10))
        top_irp = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.ir, PitchingSeason.irs)
                   .where(PitchingSeason.season == season, PitchingSeason.ir >= current.week)
                   .order_by(-((PitchingSeason.ir - PitchingSeason.irs) / PitchingSeason.ir), PitchingSeason.player)
                   .limit(10))

        db.close()

        for x in top_swin:
            all_leaders['Starter Wins']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)}
            )
        for x in top_win:
            all_leaders['Wins']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.win)}
            )
        for x in top_ip:
            all_leaders['Innings Pitched']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.ip:.1f}'}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.so)}
            )
        for x in top_era:
            all_leaders['Earned Run Average']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.erun * 9) / x.ip):.2f}'}
            )
        for x in top_kpn:
            all_leaders['Strikeouts per Nine']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.so * 9) / x.ip):.2f}'}
            )
        for x in top_whip:
            all_leaders['Walks + Hits / IP']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'}
            )
        for x in top_sv:
            all_leaders['Saves']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.sv)}
            )
        for x in top_bb:
            all_leaders['Walks Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bb)}
            )
        for x in top_run:
            all_leaders['Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.run)}
            )
        for x in top_erun:
            all_leaders['Earned Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.erun)}
            )
        for x in top_bsv:
            all_leaders['Blown Saves']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bsv)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitches Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hbp)}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hit)}
            )
        for x in top_hr:
            all_leaders['Home Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hr)}
            )
        for x in top_wp:
            all_leaders['Wild Pitches']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.wp)}
            )
        for x in top_hd:
            all_leaders['Holds']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hold)}
            )
        for x in top_bpn:
            all_leaders['Walks Per Nine']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.bb * 9) / x.ip):.2f}'}
            )
        for x in top_kpw:
            all_leaders['Strikeouts Per Walk']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{(x.so / x.bb):.2f}'}
            )
        for x in top_los:
            all_leaders['Losses']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.loss)}
            )
        for x in top_ir:
            all_leaders['Inherited Runners']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ir)}
            )
        for x in top_irs:
            all_leaders['Inherited Runners Scored']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.irs)}
            )
        for x in top_irp:
            all_leaders['Runners Stranded %']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{((x.ir - x.irs) / x.ir) * 100:.1f}'}
            )
    elif group == 'fielding':
        all_leaders = {
            'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []},
            'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []},
            'Errors': {'abbrev': 'E', 'leaders': []},
            'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []},
            'Passed Balls': {'abbrev': 'PB', 'leaders': []},
            'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []},
            'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []},
            'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []},
        }

        top_xch = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.sbc)
                   .where(FieldingSeason.season == season)
                   .order_by(-(FieldingSeason.xch + FieldingSeason.sbc), FieldingSeason.player)
                   .limit(10))
        raw_ss = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'SS') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_high)))
        raw_cf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'CF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid)))
        raw_2b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '2B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_high)))
        raw_3b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '3B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid)))
        raw_1b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '1B') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid)))
        raw_lf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'LF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_low)))
        raw_rf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'RF') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_low)))
        raw_ca = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'C') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_mid)))
        raw_pi = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'P') & (FieldingSeason.season == season) & (FieldingSeason.xch >= min_ch_pit)))
        top_err = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.error)
                   .where(FieldingSeason.season == season)
                   .order_by(-FieldingSeason.error, FieldingSeason.player)
                   .limit(10))
        top_hit = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.xhit)
                   .where(FieldingSeason.season == season)
                   .order_by(-FieldingSeason.xhit, FieldingSeason.player)
                   .limit(10))
        top_sbc = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.sbc)
                   .where((FieldingSeason.season == season) & (FieldingSeason.sbc > 0))
                   .order_by(-FieldingSeason.sbc, FieldingSeason.player)
                   .limit(10))
        top_csc = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.csc)
                   .where((FieldingSeason.season == season) & (FieldingSeason.csc > 0))
                   .order_by(-FieldingSeason.csc, FieldingSeason.player)
                   .limit(10))
        top_csp = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.sbc, FieldingSeason.csc)
                   .where((FieldingSeason.season == season) & (FieldingSeason.sbc >= min_ch_low))
                   .order_by(-(FieldingSeason.csc / FieldingSeason.sbc), FieldingSeason.player)
                   .limit(10))
        top_pb = (FieldingSeason
                  .select(FieldingSeason.player, FieldingSeason.pb)
                  .where((FieldingSeason.season == season) & (FieldingSeason.pb > 0))
                  .order_by(-FieldingSeason.pb, FieldingSeason.player)
                  .limit(10))
        # Removed for season 5
        # top_roba = (FieldingSeason
        #             .select(FieldingSeason.player, FieldingSeason.roba)
        #             .where((FieldingSeason.season == season) & (FieldingSeason.roba > 0))
        #             .order_by(-FieldingSeason.roba, FieldingSeason.player)
        #             .limit(10))
        # top_robs = (FieldingSeason
        #             .select(FieldingSeason.player, FieldingSeason.robs)
        #             .where((FieldingSeason.season == season) & (FieldingSeason.robs > 0))
        #             .order_by(-FieldingSeason.robs, FieldingSeason.player)
        #             .limit(10))
        # top_raa = (FieldingSeason
        #            .select(FieldingSeason.player, FieldingSeason.raa)
        #            .where((FieldingSeason.season == season) & (FieldingSeason.raa > 0))
        #            .order_by(-FieldingSeason.raa, FieldingSeason.player)
        #            .limit(10))
        # top_rto = (FieldingSeason
        #            .select(FieldingSeason.player, FieldingSeason.rto)
        #            .where((FieldingSeason.season == season) & (FieldingSeason.rto > 0))
        #            .order_by(-FieldingSeason.rto, FieldingSeason.player)
        #            .limit(10))

        db.close()

        for x in top_xch:
            all_leaders['Fielding Chances']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.xch + x.sbc}
            )
        for x in top_err:
            all_leaders['Errors']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.error}
            )
        for x in top_sbc:
            all_leaders['Steal Attempts Against']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.sbc}
            )
        for x in top_csc:
            all_leaders['Runners Thrown Out (C)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.csc}
            )
        for x in top_csp:
            all_leaders['Caught Stealing % (Catchers)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.csc * 100 / x.sbc:.1f}'}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.xhit}
            )
        for x in top_pb:
            all_leaders['Passed Balls']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.pb}
            )
        # for x in top_roba:
        #     all_leaders['Rob Attempts']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.roba}
        #     )
        # for x in top_robs:
        #     all_leaders['Rob Successes']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.robs}
        #     )
        # for x in top_raa:
        #     all_leaders['Runner Advance Attempts']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.raa}
        #     )
        # for x in top_rto:
        #     all_leaders['Runners Thrown Out (OF)']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': x.rto}
        #     )

        for x in raw_ss:
            all_leaders['Weighted Fielding % (SS)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_cf:
            all_leaders['Weighted Fielding % (CF)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_2b:
            all_leaders['Weighted Fielding % (2B)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_3b:
            all_leaders['Weighted Fielding % (3B)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_1b:
            all_leaders['Weighted Fielding % (1B)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_lf:
            all_leaders['Weighted Fielding % (LF)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_rf:
            all_leaders['Weighted Fielding % (RF)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_ca:
            all_leaders['Weighted Fielding % (C)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_pi:
            all_leaders['Weighted Fielding % (P)']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for stat in all_leaders:
            if 'Weighted Fielding' in stat:
                all_leaders[stat]['leaders'] = sorted(
                    all_leaders[stat]['leaders'],
                    key=lambda leader: leader['stat'],
                    reverse=True
                )
                all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10]
    else:
        # return batting leaders
        all_leaders = {
            'Home Runs': {'abbrev': 'HR', 'leaders': []},
            'Batting Average': {'abbrev': 'AVG', 'leaders': []},
            'On Base Percentage': {'abbrev': 'OBP', 'leaders': []},
            'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []},
            'On Base Plus Slugging': {'abbrev': 'OPS', 'leaders': []},
            'Weighted On Base Avg': {'abbrev': 'wOBA', 'leaders': []},
            'Avg on Balls in Play': {'abbrev': 'BABIP', 'leaders': []},
            'Runs Batted In': {'abbrev': 'RBI', 'leaders': []},
            'Doubles': {'abbrev': '2B', 'leaders': []},
            'Triples': {'abbrev': '3B', 'leaders': []},
            'Hits': {'abbrev': 'H', 'leaders': []},
            'Plate Appearances': {'abbrev': 'PA', 'leaders': []},
            'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []},
            'Walks': {'abbrev': 'BB', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []},
            'Intentional Walks': {'abbrev': 'IBB', 'leaders': []},
            'Stolen Bases': {'abbrev': 'SB', 'leaders': []},
            'Caught Stealing': {'abbrev': 'CS', 'leaders': []},
            'Runs Scored': {'abbrev': 'R', 'leaders': []},
            'At Bats': {'abbrev': 'AB', 'leaders': []},
            'No-Doubt Home Runs': {'abbrev': 'NDHR', 'leaders': []},
            'Ballpark Home Runs': {'abbrev': 'BPHR', 'leaders': []},
            'Ballpark Flyouts': {'abbrev': 'BPFO', 'leaders': []},
            'Ballpark Singles': {'abbrev': 'BP1B', 'leaders': []},
            'Ballpark Lineouts': {'abbrev': 'BPLO', 'leaders': []},
        }

        top_avg = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(-(BattingSeason.hit / BattingSeason.ab), BattingSeason.player)
                   .limit(10))
        top_hr = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.hr)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.hr, BattingSeason.player)
                  .limit(10))
        top_obp = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(
            -((BattingSeason.hit + BattingSeason.bb + BattingSeason.hbp + BattingSeason.ibb) / BattingSeason.pa),
            BattingSeason.player)
                   .limit(10))
        top_slg = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(-(((BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (BattingSeason.double * 2) + (
                BattingSeason.hit - BattingSeason.hr - BattingSeason.triple - BattingSeason.double)) / BattingSeason.ab))
                   .limit(10))
        top_ops = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(-((BattingSeason.ab * (
                    BattingSeason.hit + BattingSeason.bb + BattingSeason.ibb + BattingSeason.hbp) + (
                                            (BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (
                                                BattingSeason.double * 2) + (
                                                        BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr)) * (
                                            BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)) /
                               (BattingSeason.ab * (
                                           BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp))))
                   .limit(10))
        top_wob = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(-(((BattingSeason.bb * .69) + (BattingSeason.hbp * .72) + (BattingSeason.hr * 2.1) + (
                    BattingSeason.triple * 1.62) + (BattingSeason.double * 1.27) + ((
                                                                                                BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr) * .89)) /
                               (
                                           BattingSeason.ab + BattingSeason.bb - BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)))
                   .limit(10))
        top_bab = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa) & (BattingSeason.season == season))
                   .order_by(-((BattingSeason.hit - BattingSeason.hr) /
                               (BattingSeason.ab - BattingSeason.so - BattingSeason.hr + BattingSeason.sac)))
                   .limit(10))
        top_rbi = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.rbi)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.rbi, BattingSeason.player)
                   .limit(10))
        top_dbl = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.double)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.double, BattingSeason.player)
                   .limit(10))
        top_trp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.triple)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.triple, BattingSeason.player)
                   .limit(10))
        top_hit = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.hit)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.hit, BattingSeason.player)
                   .limit(10))
        top_pa = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.pa)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.pa, BattingSeason.player)
                  .limit(10))
        top_gdp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.gidp)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.gidp, BattingSeason.player)
                   .limit(10))
        top_bb = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.bb)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.bb, BattingSeason.player)
                  .limit(10))
        top_so = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.so)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.so, BattingSeason.player)
                  .limit(10))
        top_hbp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.hbp)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.hbp, BattingSeason.player)
                   .limit(10))
        top_ibb = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.ibb)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.ibb, BattingSeason.player)
                   .limit(10))
        top_sb = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.sb)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.sb, BattingSeason.player)
                  .limit(10))
        top_cs = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.cs)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.cs, BattingSeason.player)
                  .limit(10))
        top_run = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.run)
                   .where(BattingSeason.season == season)
                   .order_by(-BattingSeason.run, BattingSeason.player)
                   .limit(10))
        top_ab = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.ab)
                  .where(BattingSeason.season == season)
                  .order_by(-BattingSeason.ab, BattingSeason.player)
                  .limit(10))
        top_bphr = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bphr)
                    .where((BattingSeason.season == season) & (BattingSeason.bphr > 0))
                    .order_by(-BattingSeason.bphr, BattingSeason.player)
                    .limit(10))
        top_bpfo = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bpfo)
                    .where((BattingSeason.season == season) & (BattingSeason.bpfo > 0))
                    .order_by(-BattingSeason.bpfo, BattingSeason.player)
                    .limit(10))
        top_bp1b = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bp1b)
                    .where((BattingSeason.season == season) & (BattingSeason.bp1b > 0))
                    .order_by(-BattingSeason.bp1b, BattingSeason.player)
                    .limit(10))
        top_bplo = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bplo)
                    .where((BattingSeason.season == season) & (BattingSeason.bplo > 0))
                    .order_by(-BattingSeason.bplo, BattingSeason.player)
                    .limit(10))
        # Removed for season 5
        # top_xba = (BattingSeason
        #            .select(BattingSeason.player, BattingSeason.xba)
        #            .where((BattingSeason.season == season) & (BattingSeason.xba > 0))
        #            .order_by(-BattingSeason.xba, BattingSeason.player)
        #            .limit(10))
        # top_xbt = (BattingSeason
        #            .select(BattingSeason.player, BattingSeason.xbt)
        #            .where((BattingSeason.season == season) & (BattingSeason.xbt > 0))
        #            .order_by(-BattingSeason.xbt, BattingSeason.player)
        #            .limit(10))
        top_ndhr = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bphr, BattingSeason.hr)
                    .where((BattingSeason.season == season) & (BattingSeason.hr > 0))
                    .order_by(-(BattingSeason.hr - BattingSeason.bphr), BattingSeason.player)
                    .limit(10))
        db.close()

        for x in top_avg:
            all_leaders['Batting Average']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.hit / x.ab:.3f}'}
            )
        for x in top_hr:
            all_leaders['Home Runs']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hr)}
            )
        for x in top_obp:
            all_leaders['On Base Percentage']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'}
            )
        for x in top_slg:
            all_leaders['Slugging Percentage']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'}
            )
        for x in top_ops:
            all_leaders['On Base Plus Slugging']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(x.ab * (x.hit + x.bb + x.ibb + x.hbp) + ((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.double - x.triple - x.hr)) * (x.ab + x.bb + x.ibb + x.sac + x.hbp)) / (x.ab * (x.ab + x.bb + x.ibb + x.sac + x.hbp)):.3f}'}
            )
        for x in top_wob:
            all_leaders['Weighted On Base Avg']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{(((x.bb * .69) + (x.hbp * .72) + (x.hr * 2.1) + (x.triple * 1.62) + (x.double * 1.27) + ((x.hit - x.double - x.triple - x.hr) * .89)) / (x.ab + x.bb - x.ibb + x.sac + x.hbp)):.3f}'}
            )
        for x in top_bab:
            all_leaders['Avg on Balls in Play']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev,
                 'stat': f'{((x.hit - x.hr) / (x.ab - x.so - x.hr + x.sac)):.3f}'}
            )
        for x in top_rbi:
            all_leaders['Runs Batted In']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.rbi)}
            )
        for x in top_dbl:
            all_leaders['Doubles']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.double)}
            )
        for x in top_trp:
            all_leaders['Triples']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.triple)}
            )
        for x in top_hit:
            all_leaders['Hits']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hit)}
            )
        for x in top_pa:
            all_leaders['Plate Appearances']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.pa)}
            )
        for x in top_gdp:
            all_leaders['Ground Into Double Play']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.gidp)}
            )
        for x in top_bb:
            all_leaders['Walks']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bb)}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.so)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitch']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.hbp)}
            )
        for x in top_ibb:
            all_leaders['Intentional Walks']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ibb)}
            )
        for x in top_sb:
            all_leaders['Stolen Bases']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.sb)}
            )
        for x in top_cs:
            all_leaders['Caught Stealing']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.cs)}
            )
        for x in top_run:
            all_leaders['Runs Scored']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.run)}
            )
        for x in top_ab:
            all_leaders['At Bats']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.ab)}
            )
        for x in top_bphr:
            all_leaders['Ballpark Home Runs']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bphr)}
            )
        for x in top_bpfo:
            all_leaders['Ballpark Flyouts']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bpfo)}
            )
        for x in top_bp1b:
            all_leaders['Ballpark Singles']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bp1b)}
            )
        for x in top_bplo:
            all_leaders['Ballpark Lineouts']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.bplo)}
            )
        # for x in top_xba:
        #     all_leaders['Extra Base Attempts']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.xba)}
        #     )
        # for x in top_xbt:
        #     all_leaders['Extra Bases Taken']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.xbt)}
        #     )
        for x in top_ndhr:
            all_leaders['No-Doubt Home Runs']['leaders'].append(
                {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': f'{x.hr - x.bphr:.0f}'}
            )

    db.close()
    return all_leaders


@app.get('/api/v1/single-season-leaders')
async def v1_single_season_leaders_get(
        group: str = 'batting', team_abbrev: Optional[str] = None,
        league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None):
    all_leaders = None
    min_ip = 76
    min_pa = 228
    min_ch_high = 38
    min_ch_mid = 19
    min_ch_low = 9
    min_ch_pit = 5
    weeks = 20

    if group == 'pitching':
        all_leaders = {
            # 'Starter Wins': {'abbrev': 'W', 'leaders': []},
            'Wins': {'abbrev': 'W', 'leaders': []},
            'Earned Run Average': {'abbrev': 'ERA', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Innings Pitched': {'abbrev': 'IP', 'leaders': []},
            'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []},
            'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []},
            'Saves': {'abbrev': 'SV', 'leaders': []},
            'Walks Allowed': {'abbrev': 'BB', 'leaders': []},
            'Runs Allowed': {'abbrev': 'R', 'leaders': []},
            'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []},
            'Blown Saves': {'abbrev': 'BSV', 'leaders': []},
            'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []},
            'Hits Allowed': {'abbrev': 'H', 'leaders': []},
            'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []},
            'Wild Pitches': {'abbrev': 'WP', 'leaders': []},
            'Holds': {'abbrev': 'HD', 'leaders': []},
            'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []},
            'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []},
            'Losses': {'abbrev': 'L', 'leaders': []},
            'Inherited Runners': {'abbrev': 'IR', 'leaders': []},
            'Inherited Runners Scored': {'abbrev': 'IRS', 'leaders': []},
            'Runners Stranded %': {'abbrev': 'RS%', 'leaders': []},
        }

        # top_swin = (PitchingStat
        #             .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin'))
        #             .where(PitchingStat.gs == 1, PitchingStat.win == 1)
        #             .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player)
        #             .group_by(PitchingStat.player)
        #             .limit(10))
        top_win = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.win)
                   .order_by(-PitchingSeason.win, PitchingSeason.player)
                   .limit(10))
        top_ip = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.ip)
                  .order_by(-PitchingSeason.ip, PitchingSeason.player)
                  .limit(10))
        top_so = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.so)
                  .order_by(-PitchingSeason.so, PitchingSeason.player)
                  .limit(10))
        top_era = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.erun, PitchingSeason.ip)
                   .where(PitchingSeason.ip >= min_ip)
                   .order_by(((PitchingSeason.erun * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_kpn = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.so, PitchingSeason.ip)
                   .where(PitchingSeason.ip >= min_ip)
                   .order_by(-((PitchingSeason.so * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_whip = (PitchingSeason
                    .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.hit, PitchingSeason.ip)
                    .where(PitchingSeason.ip >= min_ip)
                    .order_by(+((PitchingSeason.bb + PitchingSeason.hit) / PitchingSeason.ip), PitchingSeason.player)
                    .limit(10))
        top_sv = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.sv)
                  .order_by(-PitchingSeason.sv, PitchingSeason.player)
                  .limit(10))
        top_bb = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.bb)
                  .order_by(-PitchingSeason.bb, PitchingSeason.player)
                  .limit(10))
        top_run = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.run)
                   .order_by(-PitchingSeason.run, PitchingSeason.player)
                   .limit(10))
        top_erun = (PitchingSeason
                    .select(PitchingSeason.player, PitchingSeason.erun)
                    .order_by(-PitchingSeason.erun, PitchingSeason.player)
                    .limit(10))
        top_bsv = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bsv)
                   .order_by(-PitchingSeason.bsv, PitchingSeason.player)
                   .limit(10))
        top_hbp = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.hbp)
                   .order_by(-PitchingSeason.hbp, PitchingSeason.player)
                   .limit(10))
        top_hit = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.hit)
                   .order_by(-PitchingSeason.hit, PitchingSeason.player)
                   .limit(10))
        top_hr = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.hr)
                  .order_by(-PitchingSeason.hr, PitchingSeason.player)
                  .limit(10))
        top_wp = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.wp)
                  .order_by(-PitchingSeason.wp, PitchingSeason.player)
                  .limit(10))
        top_hd = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.hold)
                  .order_by(-PitchingSeason.hold, PitchingSeason.player)
                  .limit(10))
        top_bpn = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.ip)
                   .where(PitchingSeason.ip >= min_ip)
                   .order_by(((PitchingSeason.bb * 9) / PitchingSeason.ip), PitchingSeason.player)
                   .limit(10))
        top_kpw = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.bb, PitchingSeason.so)
                   .where(PitchingSeason.ip >= min_ip)
                   .order_by(-(PitchingSeason.so / PitchingSeason.bb), PitchingSeason.player)
                   .limit(10))
        top_los = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.loss)
                   .order_by(-PitchingSeason.loss, PitchingSeason.player)
                   .limit(10))
        top_ir = (PitchingSeason
                  .select(PitchingSeason.player, PitchingSeason.ir)
                  .order_by(-PitchingSeason.ir, PitchingSeason.player)
                  .limit(10))
        top_irs = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.irs)
                   .order_by(-PitchingSeason.irs, PitchingSeason.player)
                   .limit(10))
        top_irp = (PitchingSeason
                   .select(PitchingSeason.player, PitchingSeason.ir, PitchingSeason.irs)
                   .where(PitchingSeason.ir >= weeks)
                   .order_by(-((PitchingSeason.ir - PitchingSeason.irs) / PitchingSeason.ir), PitchingSeason.player)
                   .limit(10))

        db.close()

        # for x in top_swin:
        #     all_leaders['Starter Wins']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)}
        #     )
        for x in top_win:
            all_leaders['Wins']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.win)}
            )
        for x in top_ip:
            all_leaders['Innings Pitched']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{x.ip:.1f}'}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.so)}
            )
        for x in top_era:
            all_leaders['Earned Run Average']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.erun * 9) / x.ip):.2f}'}
            )
        for x in top_kpn:
            all_leaders['Strikeouts per Nine']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.so * 9) / x.ip):.2f}'}
            )
        for x in top_whip:
            all_leaders['Walks + Hits / IP']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'}
            )
        for x in top_sv:
            all_leaders['Saves']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.sv)}
            )
        for x in top_bb:
            all_leaders['Walks Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bb)}
            )
        for x in top_run:
            all_leaders['Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.run)}
            )
        for x in top_erun:
            all_leaders['Earned Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.erun)}
            )
        for x in top_bsv:
            all_leaders['Blown Saves']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bsv)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitches Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hbp)}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hit)}
            )
        for x in top_hr:
            all_leaders['Home Runs Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hr)}
            )
        for x in top_wp:
            all_leaders['Wild Pitches']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.wp)}
            )
        for x in top_hd:
            all_leaders['Holds']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.hold)}
            )
        for x in top_bpn:
            all_leaders['Walks Per Nine']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.bb * 9) / x.ip):.2f}'}
            )
        for x in top_kpw:
            all_leaders['Strikeouts Per Walk']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.so / x.bb):.2f}'}
            )
        for x in top_los:
            all_leaders['Losses']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.loss)}
            )
        for x in top_ir:
            all_leaders['Inherited Runners']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ir)}
            )
        for x in top_irs:
            all_leaders['Inherited Runners Scored']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.irs)}
            )
        for x in top_irp:
            all_leaders['Runners Stranded %']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.ir - x.irs) / x.ir) * 100:.1f}'}
            )
    elif group == 'fielding':
        all_leaders = {
            'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []},
            'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []},
            'Errors': {'abbrev': 'E', 'leaders': []},
            'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []},
            'Passed Balls': {'abbrev': 'PB', 'leaders': []},
            'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []},
            'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []},
            'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []},
            # 'Rob Attempts': {'abbrev': 'RobA', 'leaders': []},
            # 'Rob Successes': {'abbrev': 'RobS', 'leaders': []},
            # 'Runner Advance Attempts': {'abbrev': 'RAA', 'leaders': []},
            # 'Runners Thrown Out (OF)': {'abbrev': 'RTO', 'leaders': []},
        }

        top_xch = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.sbc)
                   .order_by(-(FieldingSeason.xch + FieldingSeason.sbc), FieldingSeason.player)
                   .limit(10))
        raw_ss = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'SS') & (FieldingSeason.xch >= min_ch_high)))
        raw_cf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'CF') & (FieldingSeason.xch >= min_ch_mid)))
        raw_2b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '2B') & (FieldingSeason.xch >= min_ch_high)))
        raw_3b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '3B') & (FieldingSeason.xch >= min_ch_mid)))
        raw_1b = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == '1B') & (FieldingSeason.xch >= min_ch_mid)))
        raw_lf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'LF') & (FieldingSeason.xch >= min_ch_low)))
        raw_rf = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'RF') & (FieldingSeason.xch >= min_ch_low)))
        raw_ca = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'C') & (FieldingSeason.xch >= min_ch_mid)))
        raw_pi = (FieldingSeason
            .select(FieldingSeason.player, FieldingSeason.xch, FieldingSeason.xhit, FieldingSeason.error)
            .where(
            (FieldingSeason.pos == 'P') & (FieldingSeason.xch >= min_ch_pit)))
        top_err = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.error)
                   .order_by(-FieldingSeason.error, FieldingSeason.player)
                   .limit(10))
        top_hit = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.xhit)
                   .order_by(-FieldingSeason.xhit, FieldingSeason.player)
                   .limit(10))
        top_sbc = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.sbc)
                   .where((FieldingSeason.sbc > 0))
                   .order_by(-FieldingSeason.sbc, FieldingSeason.player)
                   .limit(10))
        top_csc = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.csc)
                   .where((FieldingSeason.csc > 0))
                   .order_by(-FieldingSeason.csc, FieldingSeason.player)
                   .limit(10))
        top_csp = (FieldingSeason
                   .select(FieldingSeason.player, FieldingSeason.sbc, FieldingSeason.csc)
                   .where((FieldingSeason.sbc >= min_ch_mid))
                   .order_by(-(FieldingSeason.csc / FieldingSeason.sbc), FieldingSeason.player)
                   .limit(10))
        top_pb = (FieldingSeason
                  .select(FieldingSeason.player, FieldingSeason.pb)
                  .where((FieldingSeason.pb > 0))
                  .order_by(-FieldingSeason.pb, FieldingSeason.player)
                  .limit(10))
        # Removed for season 5
        # top_roba = (FieldingSeason
        #             .select(FieldingSeason.player, FieldingSeason.roba)
        #             .where((FieldingSeason.roba > 0))
        #             .order_by(-FieldingSeason.roba, FieldingSeason.player)
        #             .limit(10))
        # top_robs = (FieldingSeason
        #             .select(FieldingSeason.player, FieldingSeason.robs)
        #             .where((FieldingSeason.robs > 0))
        #             .order_by(-FieldingSeason.robs, FieldingSeason.player)
        #             .limit(10))
        # top_raa = (FieldingSeason
        #            .select(FieldingSeason.player, FieldingSeason.raa)
        #            .where((FieldingSeason.raa > 0))
        #            .order_by(-FieldingSeason.raa, FieldingSeason.player)
        #            .limit(10))
        # top_rto = (FieldingSeason
        #            .select(FieldingSeason.player, FieldingSeason.rto)
        #            .where((FieldingSeason.rto > 0))
        #            .order_by(-FieldingSeason.rto, FieldingSeason.player)
        #            .limit(10))

        db.close()

        for x in top_xch:
            all_leaders['Fielding Chances']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': x.xch + x.sbc}
            )
        for x in top_err:
            all_leaders['Errors']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.error}
            )
        for x in top_sbc:
            all_leaders['Steal Attempts Against']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.sbc}
            )
        for x in top_csc:
            all_leaders['Runners Thrown Out (C)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.csc}
            )
        for x in top_csp:
            all_leaders['Caught Stealing % (Catchers)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{x.csc * 100 / x.sbc:.1f}'}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.xhit}
            )
        for x in top_pb:
            all_leaders['Passed Balls']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.pb}
            )
        # for x in top_roba:
        #     all_leaders['Rob Attempts']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.roba}
        #     )
        # for x in top_robs:
        #     all_leaders['Rob Successes']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.robs}
        #     )
        # for x in top_raa:
        #     all_leaders['Runner Advance Attempts']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.raa}
        #     )
        # for x in top_rto:
        #     all_leaders['Runners Thrown Out (OF)']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': x.rto}
        #     )

        for x in raw_ss:
            all_leaders['Weighted Fielding % (SS)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_cf:
            all_leaders['Weighted Fielding % (CF)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_2b:
            all_leaders['Weighted Fielding % (2B)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_3b:
            all_leaders['Weighted Fielding % (3B)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_1b:
            all_leaders['Weighted Fielding % (1B)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_lf:
            all_leaders['Weighted Fielding % (LF)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_rf:
            all_leaders['Weighted Fielding % (RF)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_ca:
            all_leaders['Weighted Fielding % (C)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_pi:
            all_leaders['Weighted Fielding % (P)']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for stat in all_leaders:
            if 'Weighted Fielding' in stat:
                all_leaders[stat]['leaders'] = sorted(
                    all_leaders[stat]['leaders'],
                    key=lambda leader: leader['stat'],
                    reverse=True
                )
                all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10]
    else:
        # return batting leaders
        all_leaders = {
            'Home Runs': {'abbrev': 'HR', 'leaders': []},
            'Batting Average': {'abbrev': 'AVG', 'leaders': []},
            'On Base Percentage': {'abbrev': 'OBP', 'leaders': []},
            'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []},
            'On Base Plus Slugging': {'abbrev': 'OPS', 'leaders': []},
            'Weighted On Base Avg': {'abbrev': 'wOBA', 'leaders': []},
            'Avg on Balls in Play': {'abbrev': 'BABIP', 'leaders': []},
            'Runs Batted In': {'abbrev': 'RBI', 'leaders': []},
            'Doubles': {'abbrev': '2B', 'leaders': []},
            'Triples': {'abbrev': '3B', 'leaders': []},
            'Hits': {'abbrev': 'H', 'leaders': []},
            'Plate Appearances': {'abbrev': 'PA', 'leaders': []},
            'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []},
            'Walks': {'abbrev': 'BB', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []},
            'Intentional Walks': {'abbrev': 'IBB', 'leaders': []},
            'Stolen Bases': {'abbrev': 'SB', 'leaders': []},
            'Caught Stealing': {'abbrev': 'CS', 'leaders': []},
            'Runs Scored': {'abbrev': 'R', 'leaders': []},
            'At Bats': {'abbrev': 'AB', 'leaders': []},
            'No-Doubt Home Runs': {'abbrev': 'NDHR', 'leaders': []},
            'Ballpark Home Runs': {'abbrev': 'BPHR', 'leaders': []},
            'Ballpark Flyouts': {'abbrev': 'BPFO', 'leaders': []},
            'Ballpark Singles': {'abbrev': 'BP1B', 'leaders': []},
            'Ballpark Lineouts': {'abbrev': 'BPLO', 'leaders': []},
            # 'Extra Base Attempts': {'abbrev': 'XBA', 'leaders': []},
            # 'Extra Bases Taken': {'abbrev': 'XBT', 'leaders': []},
        }

        top_avg = (BattingSeason
                   .select()
                   .where((BattingSeason.pa >= min_pa))
                   .order_by(-(BattingSeason.hit / BattingSeason.ab), BattingSeason.player)
                   .limit(10))
        top_hr = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.hr)
                  .order_by(-BattingSeason.hr, BattingSeason.player)
                  .limit(10))
        top_obp = (BattingSeason
                   .select()
                   .where(BattingSeason.pa >= min_pa)
                   .order_by(
            -((BattingSeason.hit + BattingSeason.bb + BattingSeason.hbp + BattingSeason.ibb) / BattingSeason.pa),
            BattingSeason.player)
                   .limit(10))
        top_slg = (BattingSeason
                   .select()
                   .where(BattingSeason.pa >= min_pa)
                   .order_by(-(((BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (BattingSeason.double * 2) + (
                BattingSeason.hit - BattingSeason.hr - BattingSeason.triple - BattingSeason.double)) / BattingSeason.ab))
                   .limit(10))
        top_ops = (BattingSeason
                   .select()
                   .where(BattingSeason.pa >= min_pa)
                   .order_by(-((BattingSeason.ab * (
                BattingSeason.hit + BattingSeason.bb + BattingSeason.ibb + BattingSeason.hbp) + (
                                        (BattingSeason.hr * 4) + (BattingSeason.triple * 3) + (
                                        BattingSeason.double * 2) + (
                                                BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr)) * (
                                        BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)) /
                               (BattingSeason.ab * (
                                       BattingSeason.ab + BattingSeason.bb + BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp))))
                   .limit(10))
        top_wob = (BattingSeason
                   .select()
                   .where(BattingSeason.pa >= min_pa)
                   .order_by(-(((BattingSeason.bb * .69) + (BattingSeason.hbp * .72) + (BattingSeason.hr * 2.1) + (
                BattingSeason.triple * 1.62) + (BattingSeason.double * 1.27) + ((
                                                                                        BattingSeason.hit - BattingSeason.double - BattingSeason.triple - BattingSeason.hr) * .89)) /
                               (
                                       BattingSeason.ab + BattingSeason.bb - BattingSeason.ibb + BattingSeason.sac + BattingSeason.hbp)))
                   .limit(10))
        top_bab = (BattingSeason
                   .select()
                   .where(BattingSeason.pa >= min_pa)
                   .order_by(-((BattingSeason.hit - BattingSeason.hr) /
                               (BattingSeason.ab - BattingSeason.so - BattingSeason.hr + BattingSeason.sac)))
                   .limit(10))
        top_rbi = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.rbi)
                   .order_by(-BattingSeason.rbi, BattingSeason.player)
                   .limit(10))
        top_dbl = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.double)
                   .order_by(-BattingSeason.double, BattingSeason.player)
                   .limit(10))
        top_trp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.triple)
                   .order_by(-BattingSeason.triple, BattingSeason.player)
                   .limit(10))
        top_hit = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.hit)
                   .order_by(-BattingSeason.hit, BattingSeason.player)
                   .limit(10))
        top_pa = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.pa)
                  .order_by(-BattingSeason.pa, BattingSeason.player)
                  .limit(10))
        top_gdp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.gidp)
                   .order_by(-BattingSeason.gidp, BattingSeason.player)
                   .limit(10))
        top_bb = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.bb)
                  .order_by(-BattingSeason.bb, BattingSeason.player)
                  .limit(10))
        top_so = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.so)
                  .order_by(-BattingSeason.so, BattingSeason.player)
                  .limit(10))
        top_hbp = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.hbp)
                   .order_by(-BattingSeason.hbp, BattingSeason.player)
                   .limit(10))
        top_ibb = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.ibb)
                   .order_by(-BattingSeason.ibb, BattingSeason.player)
                   .limit(10))
        top_sb = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.sb)
                  .order_by(-BattingSeason.sb, BattingSeason.player)
                  .limit(10))
        top_cs = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.cs)
                  .order_by(-BattingSeason.cs, BattingSeason.player)
                  .limit(10))
        top_run = (BattingSeason
                   .select(BattingSeason.player, BattingSeason.run)
                   .order_by(-BattingSeason.run, BattingSeason.player)
                   .limit(10))
        top_ab = (BattingSeason
                  .select(BattingSeason.player, BattingSeason.ab)
                  .order_by(-BattingSeason.ab, BattingSeason.player)
                  .limit(10))
        top_bphr = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bphr)
                    .where(BattingSeason.bphr > 0)
                    .order_by(-BattingSeason.bphr, BattingSeason.player)
                    .limit(10))
        top_bpfo = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bpfo)
                    .where(BattingSeason.bpfo > 0)
                    .order_by(-BattingSeason.bpfo, BattingSeason.player)
                    .limit(10))
        top_bp1b = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bp1b)
                    .where(BattingSeason.bp1b > 0)
                    .order_by(-BattingSeason.bp1b, BattingSeason.player)
                    .limit(10))
        top_bplo = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bplo)
                    .where(BattingSeason.bplo > 0)
                    .order_by(-BattingSeason.bplo, BattingSeason.player)
                    .limit(10))
        # top_xba = (BattingSeason
        #            .select(BattingSeason.player, BattingSeason.xba)
        #            .where(BattingSeason.xba > 0)
        #            .order_by(-BattingSeason.xba, BattingSeason.player)
        #            .limit(10))
        # top_xbt = (BattingSeason
        #            .select(BattingSeason.player, BattingSeason.xbt)
        #            .where(BattingSeason.xbt > 0)
        #            .order_by(-BattingSeason.xbt, BattingSeason.player)
        #            .limit(10))
        top_ndhr = (BattingSeason
                    .select(BattingSeason.player, BattingSeason.bphr, BattingSeason.hr)
                    .where(BattingSeason.hr > 0)
                    .order_by(-(BattingSeason.hr - BattingSeason.bphr), BattingSeason.player)
                    .limit(10))
        db.close()

        for x in top_avg:
            all_leaders['Batting Average']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{x.hit / x.ab:.3f}'}
            )
        for x in top_hr:
            all_leaders['Home Runs']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hr)}
            )
        for x in top_obp:
            all_leaders['On Base Percentage']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'}
            )
        for x in top_slg:
            all_leaders['Slugging Percentage']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'}
            )
        for x in top_ops:
            all_leaders['On Base Plus Slugging']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(x.ab * (x.hit + x.bb + x.ibb + x.hbp) + ((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.double - x.triple - x.hr)) * (x.ab + x.bb + x.ibb + x.sac + x.hbp)) / (x.ab * (x.ab + x.bb + x.ibb + x.sac + x.hbp)):.3f}'}
            )
        for x in top_wob:
            all_leaders['Weighted On Base Avg']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{(((x.bb * .69) + (x.hbp * .72) + (x.hr * 2.1) + (x.triple * 1.62) + (x.double * 1.27) + ((x.hit - x.double - x.triple - x.hr) * .89)) / (x.ab + x.bb - x.ibb + x.sac + x.hbp)):.3f}'}
            )
        for x in top_bab:
            all_leaders['Avg on Balls in Play']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{((x.hit - x.hr) / (x.ab - x.so - x.hr + x.sac)):.3f}'}
            )
        for x in top_rbi:
            all_leaders['Runs Batted In']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.rbi)}
            )
        for x in top_dbl:
            all_leaders['Doubles']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.double)}
            )
        for x in top_trp:
            all_leaders['Triples']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.triple)}
            )
        for x in top_hit:
            all_leaders['Hits']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hit)}
            )
        for x in top_pa:
            all_leaders['Plate Appearances']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.pa)}
            )
        for x in top_gdp:
            all_leaders['Ground Into Double Play']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.gidp)}
            )
        for x in top_bb:
            all_leaders['Walks']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.bb)}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.so)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitch']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.hbp)}
            )
        for x in top_ibb:
            all_leaders['Intentional Walks']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ibb)}
            )
        for x in top_sb:
            all_leaders['Stolen Bases']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.sb)}
            )
        for x in top_cs:
            all_leaders['Caught Stealing']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.cs)}
            )
        for x in top_run:
            all_leaders['Runs Scored']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.run)}
            )
        for x in top_ab:
            all_leaders['At Bats']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.ab)}
            )
        for x in top_bphr:
            all_leaders['Ballpark Home Runs']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.bphr)}
            )
        for x in top_bpfo:
            all_leaders['Ballpark Flyouts']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.bpfo)}
            )
        for x in top_bp1b:
            all_leaders['Ballpark Singles']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.bp1b)}
            )
        for x in top_bplo:
            all_leaders['Ballpark Lineouts']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': round(x.bplo)}
            )
        # for x in top_xba:
        #     all_leaders['Extra Base Attempts']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.xba)}
        #     )
        # for x in top_xbt:
        #     all_leaders['Extra Bases Taken']['leaders'].append(
        #         {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev, 'stat': round(x.xbt)}
        #     )
        for x in top_ndhr:
            all_leaders['No-Doubt Home Runs']['leaders'].append(
                {'player': x.player.name, 'season': x.player.season, 'team': x.player.team.abbrev,
                 'stat': f'{x.hr - x.bphr:.0f}'}
            )

    db.close()
    return all_leaders


@app.get('/api/v1/career-leaders')
async def v1_career_leaders_get(group: str = 'batting'):
    min_ip = 300
    min_pa = 1000
    min_ch_high = 200
    min_ch_mid = 80
    min_ch_low = 40
    all_leaders = None

    if group == 'pitching':
        all_leaders = {
            # 'Starter Wins': {'abbrev': 'W', 'leaders': []},
            'Wins': {'abbrev': 'W', 'leaders': []},
            'Earned Run Average': {'abbrev': 'ERA', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Innings Pitched': {'abbrev': 'IP', 'leaders': []},
            'Strikeouts per Nine': {'abbrev': 'K/9', 'leaders': []},
            'Walks + Hits / IP': {'abbrev': 'WHIP', 'leaders': []},
            'Saves': {'abbrev': 'SV', 'leaders': []},
            'Walks Allowed': {'abbrev': 'BB', 'leaders': []},
            'Runs Allowed': {'abbrev': 'R', 'leaders': []},
            'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []},
            'Blown Saves': {'abbrev': 'BSV', 'leaders': []},
            'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []},
            'Hits Allowed': {'abbrev': 'H', 'leaders': []},
            'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []},
            'Wild Pitches': {'abbrev': 'WP', 'leaders': []},
            'Holds': {'abbrev': 'HD', 'leaders': []},
            'Walks Per Nine': {'abbrev': 'BB/9', 'leaders': []},
            'Strikeouts Per Walk': {'abbrev': 'K/BB', 'leaders': []},
            'Losses': {'abbrev': 'L', 'leaders': []},
        }

        # top_swin = (PitchingStat
        #             .select(PitchingStat.player, fn.COUNT(PitchingStat.win).alias('swin'))
        #             .where(PitchingStat.gs == 1, PitchingStat.win == 1)
        #             .order_by(-fn.COUNT(PitchingStat.win), PitchingStat.player)
        #             .group_by(PitchingStat.player)
        #             .limit(10))
        top_win = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.win)
                   .order_by(-PitchingCareer.win, PitchingCareer.name)
                   .limit(10))
        top_ip = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.ip)
                  .order_by(-PitchingCareer.ip, PitchingCareer.name)
                  .limit(10))
        top_so = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.so)
                  .order_by(-PitchingCareer.so, PitchingCareer.name)
                  .limit(10))
        top_era = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.erun, PitchingCareer.ip)
                   .where(PitchingCareer.ip >= min_ip)
                   .order_by(((PitchingCareer.erun * 9) / PitchingCareer.ip), PitchingCareer.name)
                   .limit(10))
        top_kpn = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.so, PitchingCareer.ip)
                   .where(PitchingCareer.ip >= min_ip)
                   .order_by(-((PitchingCareer.so * 9) / PitchingCareer.ip), PitchingCareer.name)
                   .limit(10))
        top_whip = (PitchingCareer
                    .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.hit, PitchingCareer.ip)
                    .where(PitchingCareer.ip >= min_ip)
                    .order_by(+((PitchingCareer.bb + PitchingCareer.hit) / PitchingCareer.ip), PitchingCareer.name)
                    .limit(10))
        top_sv = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.sv)
                  .order_by(-PitchingCareer.sv, PitchingCareer.name)
                  .limit(10))
        top_bb = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.bb)
                  .order_by(-PitchingCareer.bb, PitchingCareer.name)
                  .limit(10))
        top_run = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.run)
                   .order_by(-PitchingCareer.run, PitchingCareer.name)
                   .limit(10))
        top_erun = (PitchingCareer
                    .select(PitchingCareer.name, PitchingCareer.erun)
                    .order_by(-PitchingCareer.erun, PitchingCareer.name)
                    .limit(10))
        top_bsv = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.bsv)
                   .order_by(-PitchingCareer.bsv, PitchingCareer.name)
                   .limit(10))
        top_hbp = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.hbp)
                   .order_by(-PitchingCareer.hbp, PitchingCareer.name)
                   .limit(10))
        top_hit = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.hit)
                   .order_by(-PitchingCareer.hit, PitchingCareer.name)
                   .limit(10))
        top_hr = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.hr)
                  .order_by(-PitchingCareer.hr, PitchingCareer.name)
                  .limit(10))
        top_wp = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.wp)
                  .order_by(-PitchingCareer.wp, PitchingCareer.name)
                  .limit(10))
        top_hd = (PitchingCareer
                  .select(PitchingCareer.name, PitchingCareer.hold)
                  .order_by(-PitchingCareer.hold, PitchingCareer.name)
                  .limit(10))
        top_bpn = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.ip)
                   .where(PitchingCareer.ip >= min_ip)
                   .order_by(((PitchingCareer.bb * 9) / PitchingCareer.ip), PitchingCareer.name)
                   .limit(10))
        top_kpw = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.bb, PitchingCareer.so)
                   .where(PitchingCareer.ip >= min_ip)
                   .order_by(-(PitchingCareer.so / PitchingCareer.bb), PitchingCareer.name)
                   .limit(10))
        top_los = (PitchingCareer
                   .select(PitchingCareer.name, PitchingCareer.loss)
                   .order_by(-PitchingCareer.loss, PitchingCareer.name)
                   .limit(10))

        db.close()

        # for x in top_swin:
        #     all_leaders['Starter Wins']['leaders'].append(
        #         {'player': x.player.name, 'team': x.player.team.abbrev, 'stat': round(x.swin)}
        #     )
        for x in top_win:
            all_leaders['Wins']['leaders'].append(
                {'player': x.name, 'stat': round(x.win)}
            )
        for x in top_ip:
            all_leaders['Innings Pitched']['leaders'].append(
                {'player': x.name, 'stat': f'{x.ip:.1f}'}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.name, 'stat': round(x.so)}
            )
        for x in top_era:
            all_leaders['Earned Run Average']['leaders'].append(
                {'player': x.name, 'stat': f'{((x.erun * 9) / x.ip):.2f}'}
            )
        for x in top_kpn:
            all_leaders['Strikeouts per Nine']['leaders'].append(
                {'player': x.name, 'stat': f'{((x.so * 9) / x.ip):.2f}'}
            )
        for x in top_whip:
            all_leaders['Walks + Hits / IP']['leaders'].append(
                {'player': x.name, 'stat': f'{((x.bb + x.hit) / x.ip):.2f}'}
            )
        for x in top_sv:
            all_leaders['Saves']['leaders'].append(
                {'player': x.name, 'stat': round(x.sv)}
            )
        for x in top_bb:
            all_leaders['Walks Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.bb)}
            )
        for x in top_run:
            all_leaders['Runs Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.run)}
            )
        for x in top_erun:
            all_leaders['Earned Runs Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.erun)}
            )
        for x in top_bsv:
            all_leaders['Blown Saves']['leaders'].append(
                {'player': x.name, 'stat': round(x.bsv)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitches Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.hbp)}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.hit)}
            )
        for x in top_hr:
            all_leaders['Home Runs Allowed']['leaders'].append(
                {'player': x.name, 'stat': round(x.hr)}
            )
        for x in top_wp:
            all_leaders['Wild Pitches']['leaders'].append(
                {'player': x.name, 'stat': round(x.wp)}
            )
        for x in top_hd:
            all_leaders['Holds']['leaders'].append(
                {'player': x.name, 'stat': round(x.hold)}
            )
        for x in top_bpn:
            all_leaders['Walks Per Nine']['leaders'].append(
                {'player': x.name, 'stat': f'{((x.bb * 9) / x.ip):.2f}'}
            )
        for x in top_kpw:
            all_leaders['Strikeouts Per Walk']['leaders'].append(
                {'player': x.name, 'stat': f'{(x.so / x.bb):.2f}'}
            )
        for x in top_los:
            all_leaders['Losses']['leaders'].append(
                {'player': x.name, 'stat': round(x.loss)}
            )
    elif group == 'fielding':
        all_leaders = {
            'Fielding Chances': {'abbrev': 'X-Ch', 'leaders': []},
            'Weighted Fielding % (RF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (CF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (LF)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (SS)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (3B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (2B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (1B)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (C)': {'abbrev': 'wF%', 'leaders': []},
            'Weighted Fielding % (P)': {'abbrev': 'wF%', 'leaders': []},
            'Errors': {'abbrev': 'E', 'leaders': []},
            'Hits Allowed': {'abbrev': 'X-Hit', 'leaders': []},
            'Passed Balls': {'abbrev': 'PB', 'leaders': []},
            'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []},
            'Runners Thrown Out': {'abbrev': 'CSc', 'leaders': []},
            'Caught Stealing % (Catchers)': {'abbrev': 'CS%', 'leaders': []},
        }

        top_xch = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.sbc)
                   .order_by(-(FieldingCareer.xch + FieldingCareer.sbc), FieldingCareer.name)
                   .limit(10))
        raw_ss = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'SS') & (FieldingCareer.xch >= min_ch_high)))
        raw_cf = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'CF') & (FieldingCareer.xch >= min_ch_mid)))
        raw_2b = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == '2B') & (FieldingCareer.xch >= min_ch_high)))
        raw_3b = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == '3B') & (FieldingCareer.xch >= min_ch_mid)))
        raw_1b = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == '1B') & (FieldingCareer.xch >= min_ch_mid)))
        raw_lf = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'LF') & (FieldingCareer.xch >= min_ch_low)))
        raw_rf = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'RF') & (FieldingCareer.xch >= min_ch_low)))
        raw_ca = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'C') & (FieldingCareer.xch >= min_ch_mid)))
        raw_pi = (FieldingCareer
            .select(FieldingCareer.name, FieldingCareer.xch, FieldingCareer.xhit, FieldingCareer.error)
            .where(
            (FieldingCareer.pos == 'P') & (FieldingCareer.xch >= (min_ch_low / 3))))
        top_err = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.error)
                   .order_by(-FieldingCareer.error, FieldingCareer.name)
                   .limit(10))
        top_hit = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.xhit)
                   .order_by(-FieldingCareer.xhit, FieldingCareer.name)
                   .limit(10))
        top_sbc = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.sbc)
                   .order_by(-FieldingCareer.sbc, FieldingCareer.name)
                   .limit(10))
        top_csc = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.csc)
                   .order_by(-FieldingCareer.csc, FieldingCareer.name)
                   .limit(10))
        top_csp = (FieldingCareer
                   .select(FieldingCareer.name, FieldingCareer.sbc, FieldingCareer.csc)
                   .where(FieldingCareer.sbc >= min_ch_mid)
                   .order_by(-((FieldingCareer.csc * 100) / FieldingCareer.sbc), FieldingCareer.name)
                   .limit(10))
        top_pb = (FieldingCareer
                  .select(FieldingCareer.name, FieldingCareer.pb)
                  .where(FieldingCareer.pb > 0)
                  .order_by(-FieldingCareer.pb, FieldingCareer.name)
                  .limit(10))

        db.close()

        for x in top_xch:
            all_leaders['Fielding Chances']['leaders'].append(
                {'player': x.name, 'stat': x.xch + x.sbc}
            )
        for x in top_err:
            all_leaders['Errors']['leaders'].append(
                {'player': x.name, 'stat': x.error}
            )
        for x in top_sbc:
            all_leaders['Steal Attempts Against']['leaders'].append(
                {'player': x.name, 'stat': x.sbc}
            )
        for x in top_csc:
            all_leaders['Runners Thrown Out']['leaders'].append(
                {'player': x.name, 'stat': x.csc}
            )
        for x in top_csp:
            all_leaders['Caught Stealing % (Catchers)']['leaders'].append(
                {'player': x.name, 'stat': f'{x.csc * 100 / x.sbc:.1f}'}
            )
        for x in top_hit:
            all_leaders['Hits Allowed']['leaders'].append(
                {'player': x.name, 'stat': x.xhit}
            )
        for x in top_pb:
            all_leaders['Passed Balls']['leaders'].append(
                {'player': x.name, 'stat': x.pb}
            )

        for x in raw_ss:
            all_leaders['Weighted Fielding % (SS)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_cf:
            all_leaders['Weighted Fielding % (CF)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_2b:
            all_leaders['Weighted Fielding % (2B)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_3b:
            all_leaders['Weighted Fielding % (3B)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_1b:
            all_leaders['Weighted Fielding % (1B)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_lf:
            all_leaders['Weighted Fielding % (LF)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_rf:
            all_leaders['Weighted Fielding % (RF)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_ca:
            all_leaders['Weighted Fielding % (C)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for x in raw_pi:
            all_leaders['Weighted Fielding % (P)']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.xch - (x.error * .5) - (x.xhit * .75)) / x.xch:.3f}'}
            )
        for stat in all_leaders:
            if 'Weighted Fielding' in stat:
                all_leaders[stat]['leaders'] = sorted(
                    all_leaders[stat]['leaders'],
                    key=lambda leader: leader['stat'],
                    reverse=True
                )
                all_leaders[stat]['leaders'] = all_leaders[stat]['leaders'][:10]
    else:
        # return batting leaders
        all_leaders = {
            'Home Runs': {'abbrev': 'HR', 'leaders': []},
            'Batting Average': {'abbrev': 'AVG', 'leaders': []},
            'On Base Percentage': {'abbrev': 'OBP', 'leaders': []},
            'Slugging Percentage': {'abbrev': 'SLG', 'leaders': []},
            'Runs Batted In': {'abbrev': 'RBI', 'leaders': []},
            'Doubles': {'abbrev': '2B', 'leaders': []},
            'Triples': {'abbrev': '3B', 'leaders': []},
            'Hits': {'abbrev': 'H', 'leaders': []},
            'Plate Appearances': {'abbrev': 'PA', 'leaders': []},
            'Ground Into Double Play': {'abbrev': 'GIDP', 'leaders': []},
            'Walks': {'abbrev': 'BB', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Hit By Pitch': {'abbrev': 'HBP', 'leaders': []},
            'Intentional Walks': {'abbrev': 'IBB', 'leaders': []},
            'Stolen Bases': {'abbrev': 'SB', 'leaders': []},
            'Caught Stealing': {'abbrev': 'CS', 'leaders': []},
            'Runs Scored': {'abbrev': 'R', 'leaders': []},
            'At Bats': {'abbrev': 'AB', 'leaders': []},
        }

        top_avg = (BattingCareer
                   .select()
                   .where(BattingCareer.pa >= min_pa)
                   .order_by(-(BattingCareer.hit / BattingCareer.ab), BattingCareer.name)
                   .limit(10))
        top_hr = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.hr)
                  .order_by(-BattingCareer.hr, BattingCareer.name)
                  .limit(10))
        top_obp = (BattingCareer
                   .select()
                   .where(BattingCareer.pa >= min_pa)
                   .order_by(
            -((BattingCareer.hit + BattingCareer.bb + BattingCareer.hbp + BattingCareer.ibb) / BattingCareer.pa),
            BattingCareer.name)
                   .limit(10))
        top_slg = (BattingCareer
                   .select()
                   .where(BattingCareer.pa >= min_pa)
                   .order_by(-(((BattingCareer.hr * 4) + (BattingCareer.triple * 3) + (BattingCareer.double * 2) + (
                BattingCareer.hit - BattingCareer.hr - BattingCareer.triple - BattingCareer.double)) / BattingCareer.ab))
                   .limit(10))
        top_rbi = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.rbi)
                   .order_by(-BattingCareer.rbi, BattingCareer.name)
                   .limit(10))
        top_dbl = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.double)
                   .order_by(-BattingCareer.double, BattingCareer.name)
                   .limit(10))
        top_trp = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.triple)
                   .order_by(-BattingCareer.triple, BattingCareer.name)
                   .limit(10))
        top_hit = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.hit)
                   .order_by(-BattingCareer.hit, BattingCareer.name)
                   .limit(10))
        top_pa = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.pa)
                  .order_by(-BattingCareer.pa, BattingCareer.name)
                  .limit(10))
        top_gdp = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.gidp)
                   .order_by(-BattingCareer.gidp, BattingCareer.name)
                   .limit(10))
        top_bb = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.bb)
                  .order_by(-BattingCareer.bb, BattingCareer.name)
                  .limit(10))
        top_so = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.so)
                  .order_by(-BattingCareer.so, BattingCareer.name)
                  .limit(10))
        top_hbp = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.hbp)
                   .order_by(-BattingCareer.hbp, BattingCareer.name)
                   .limit(10))
        top_ibb = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.ibb)
                   .order_by(-BattingCareer.ibb, BattingCareer.name)
                   .limit(10))
        top_sb = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.sb)
                  .order_by(-BattingCareer.sb, BattingCareer.name)
                  .limit(10))
        top_cs = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.cs)
                  .order_by(-BattingCareer.cs, BattingCareer.name)
                  .limit(10))
        top_run = (BattingCareer
                   .select(BattingCareer.name, BattingCareer.run)
                   .order_by(-BattingCareer.run, BattingCareer.name)
                   .limit(10))
        top_ab = (BattingCareer
                  .select(BattingCareer.name, BattingCareer.ab)
                  .order_by(-BattingCareer.ab, BattingCareer.name)
                  .limit(10))
        db.close()

        for x in top_avg:
            all_leaders['Batting Average']['leaders'].append(
                {'player': x.name, 'stat': f'{x.hit / x.ab:.3f}'}
            )
        for x in top_hr:
            all_leaders['Home Runs']['leaders'].append(
                {'player': x.name, 'stat': round(x.hr)}
            )
        for x in top_obp:
            all_leaders['On Base Percentage']['leaders'].append(
                {'player': x.name,
                 'stat': f'{(x.hit + x.bb + x.hbp + x.ibb) / x.pa:.3f}'}
            )
        for x in top_slg:
            all_leaders['Slugging Percentage']['leaders'].append(
                {'player': x.name,
                 'stat': f'{((x.hr * 4) + (x.triple * 3) + (x.double * 2) + (x.hit - x.hr - x.triple - x.double)) / x.ab:.3f}'}
            )
        for x in top_rbi:
            all_leaders['Runs Batted In']['leaders'].append(
                {'player': x.name, 'stat': round(x.rbi)}
            )
        for x in top_dbl:
            all_leaders['Doubles']['leaders'].append(
                {'player': x.name, 'stat': round(x.double)}
            )
        for x in top_trp:
            all_leaders['Triples']['leaders'].append(
                {'player': x.name, 'stat': round(x.triple)}
            )
        for x in top_hit:
            all_leaders['Hits']['leaders'].append(
                {'player': x.name, 'stat': round(x.hit)}
            )
        for x in top_pa:
            all_leaders['Plate Appearances']['leaders'].append(
                {'player': x.name, 'stat': round(x.pa)}
            )
        for x in top_gdp:
            all_leaders['Ground Into Double Play']['leaders'].append(
                {'player': x.name, 'stat': round(x.gidp)}
            )
        for x in top_bb:
            all_leaders['Walks']['leaders'].append(
                {'player': x.name, 'stat': round(x.bb)}
            )
        for x in top_so:
            all_leaders['Strikeouts']['leaders'].append(
                {'player': x.name, 'stat': round(x.so)}
            )
        for x in top_hbp:
            all_leaders['Hit By Pitch']['leaders'].append(
                {'player': x.name, 'stat': round(x.hbp)}
            )
        for x in top_ibb:
            all_leaders['Intentional Walks']['leaders'].append(
                {'player': x.name, 'stat': round(x.ibb)}
            )
        for x in top_sb:
            all_leaders['Stolen Bases']['leaders'].append(
                {'player': x.name, 'stat': round(x.sb)}
            )
        for x in top_cs:
            all_leaders['Caught Stealing']['leaders'].append(
                {'player': x.name, 'stat': round(x.cs)}
            )
        for x in top_run:
            all_leaders['Runs Scored']['leaders'].append(
                {'player': x.name, 'stat': round(x.run)}
            )
        for x in top_ab:
            all_leaders['At Bats']['leaders'].append(
                {'player': x.name, 'stat': round(x.ab)}
            )

    db.close()
    return all_leaders


@app.get('/api/v1/leaders/{stat}')  # Not implemented
async def v1_leaders_stat(
        stat: str, season: int = Current.get().season, team_abbrev: Optional[str] = None,
        league_abbrev: Optional[str] = None, division_abbrev: Optional[str] = None):
    db.close()
    raise HTTPException(501, detail='Function not implemented, yet')


@app.get('/api/v1/single-game-leaders')
async def v1_single_game_leaders_get(
        group: str = 'batting', season: Optional[int] = None, team_abbrev: Optional[str] = None):
    all_leaders = None

    if group == 'pitching':
        all_leaders = {
            'Perfect Games': {'abbrev': 'PG', 'leaders': []},
            'Complete Games': {'abbrev': 'PG', 'leaders': []},
            # 'Innings Pitched': {'abbrev': 'IP', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Scoreless Innings': {'abbrev': 'SI', 'leaders': []},
            'Zero Baserunners': {'abbrev': 'ZB', 'leaders': []},
            'Earned Runs Allowed': {'abbrev': 'ER', 'leaders': []},
            'Hit By Pitches Allowed': {'abbrev': 'HBP', 'leaders': []},
            'Hits Allowed': {'abbrev': 'H', 'leaders': []},
            'Home Runs Allowed': {'abbrev': 'HR', 'leaders': []},
            'Wild Pitches': {'abbrev': 'WP', 'leaders': []},
        }
        all_stats = PitchingStat.select().join(Team).where(
            (PitchingStat.team.abbrev != 'SRS') & (PitchingStat.ip <= 10)
        )
        if season:
            all_stats = all_stats.where(PitchingStat.season == season)
        if team_abbrev and season:
            this_team = Team.get_season(team_abbrev, season)
            if not this_team:
                raise HTTPException(404, f'Team {team_abbrev} not found in season {season}')
            all_stats = all_stats.where(PitchingStat.team == this_team)

        # top_ip = (all_stats
        #           .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player)
        #           .limit(10))
        top_so = (all_stats
                  .order_by(-PitchingStat.so, PitchingStat.season, PitchingStat.player)
                  .limit(10))
        top_si = (all_stats.where(PitchingStat.run == 0)
                  .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player)
                  .limit(10))
        top_zb = (all_stats
                  .where((PitchingStat.hit == 0) & (PitchingStat.bb == 0) & (PitchingStat.hbp == 0))
                  .order_by(-PitchingStat.ip, PitchingStat.season, PitchingStat.player)
                  .limit(10))
        top_erun = (all_stats
                    .order_by(-PitchingStat.erun, PitchingStat.season, PitchingStat.player)
                    .limit(10))
        top_hbp = (all_stats
                   .order_by(-PitchingStat.hbp, PitchingStat.season, PitchingStat.player)
                   .limit(10))
        top_hit = (all_stats
                   .order_by(-PitchingStat.hit, PitchingStat.season, PitchingStat.player)
                   .limit(10))
        top_hr = (all_stats
                  .order_by(-PitchingStat.hr, PitchingStat.season, PitchingStat.player)
                  .limit(10))
        top_wp = (all_stats
                  .order_by(-PitchingStat.wp, PitchingStat.season, PitchingStat.player)
                  .limit(10))

        db.close()
        return all_leaders

        # for x in top_ip:
        #     this_result = Result.select().where(
        #         (Result.season == x.season) & (Result.week == x.week) &
        #         ((Result.awayteam == x.team) | (Result.hometeam == x.team))
        #     ).limit(1)
        #
        #     opponent = None
        #     home_team = None
        #     away_team = None
        #     if this_result.count() > 0:
        #         # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
        #         # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
        #         #              f'This Team: {x.player.team}')
        #         home_team = this_result[0].hometeam
        #         away_team = this_result[0].awayteam
        #         if this_result[0].hometeam == x.player.team:
        #             opponent = this_result[0].awayteam
        #         else:
        #             opponent = this_result[0].hometeam
        #     # logging.info(f'Opponent: {opponent}')
        #
        #     all_leaders['Innings Pitched']['leaders'].append(
        #         {
        #             'player': x.player.name,
        #             'team': x.team.abbrev,
        #             'stat': f'{x.ip:.1f}',
        #             'season': x.player.season,
        #             'week': x.week,
        #             'game_num': x.game,
        #             'opponent': opponent.abbrev if opponent else '???',
        #             'home_team': home_team.abbrev if home_team else '???',
        #             'away_team': away_team.abbrev if away_team else '???'
        #         }
        #     )
        for x in top_so:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Strikeouts']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.so),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_si:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Scoreless Innings']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': f'{x.ip:.1f}',
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_zb:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Zero Baserunners']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': f'{x.ip:.1f}',
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_erun:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Earned Runs Allowed']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.erun),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_hbp:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hit By Pitches Allowed']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.hbp),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_hit:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hits Allowed']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.hit),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_hr:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Home Runs Allowed']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.hr),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_wp:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Wild Pitches']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': round(x.wp),
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )

    elif group == 'fielding':
        all_leaders = {
            'Fielding Chances': {'abbrev': 'xch', 'leaders': []},
            'Errors': {'abbrev': 'E', 'leaders': []},
            'Hits Allowed': {'abbrev': 'H', 'leaders': []},
            'Passed Balls': {'abbrev': 'PB', 'leaders': []},
            'Steal Attempts Against': {'abbrev': 'SBa', 'leaders': []},
            'Runners Thrown Out (C)': {'abbrev': 'CSc', 'leaders': []},
            # 'Rob Attempts': {'abbrev': 'ROBa', 'leaders': []},
            # 'Rob Successes': {'abbrev': 'ROBs', 'leaders': []},
        }
        all_stats = BattingStat.select().join(Team).where(BattingStat.team.abbrev != 'SRS')
        if season:
            all_stats = all_stats.where(BattingStat.season == season)
        if team_abbrev and season:
            this_team = Team.get_season(team_abbrev, season)
            if not this_team:
                raise HTTPException(404, f'Team {team_abbrev} not found in season {season}')
            all_stats = all_stats.where(BattingStat.team == this_team)

        top_xch = (all_stats
                   .where(BattingStat.xch > 1)
                   .order_by(-BattingStat.xch, BattingStat.season, BattingStat.week)
                   .limit(10))
        top_err = (all_stats
                   .where(BattingStat.error > 1)
                   .order_by(-BattingStat.error, BattingStat.season, BattingStat.week)
                   .limit(10))
        top_hit = (all_stats
                   .where(BattingStat.hit > 1)
                   .order_by(-BattingStat.xhit, BattingStat.season, BattingStat.week)
                   .limit(10))
        top_sbc = (all_stats
                   .where(BattingStat.sbc > 1)
                   .order_by(-BattingStat.sbc, BattingStat.season, BattingStat.week)
                   .limit(10))
        top_csc = (all_stats
                   .where(BattingStat.csc > 1)
                   .order_by(-BattingStat.csc, BattingStat.season, BattingStat.week)
                   .limit(10))
        top_pb = (all_stats
                  .where(BattingStat.pb > 1)
                  .order_by(-BattingStat.pb, BattingStat.season, BattingStat.week)
                  .limit(10))
        # top_roba = (all_stats
        #             .where(BattingStat.roba > 1)
        #             .order_by(-BattingStat.roba, BattingStat.season, BattingStat.week)
        #             .limit(10))
        # top_robs = (all_stats
        #             .where(BattingStat.robs > 1)
        #             .order_by(-BattingStat.robs, BattingStat.season, BattingStat.week)
        #             .limit(10))

        db.close()

        for x in top_xch:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Fielding Chances']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.xch,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_err:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Errors']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.error,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_sbc:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Steal Attempts Against']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.sbc,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_csc:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Runners Thrown Out (C)']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.csc,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_hit:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hits Allowed']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.xhit,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        for x in top_pb:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Passed Balls']['leaders'].append(
                {
                    'player': x.player.name,
                    'team': x.team.abbrev,
                    'stat': x.pb,
                    'season': x.player.season,
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???'
                }
            )
        # for x in top_roba:
        #     this_result = Result.select().where(
        #         (Result.season == x.season) & (Result.week == x.week) &
        #         ((Result.awayteam == x.team) | (Result.hometeam == x.team))
        #     ).limit(1)
        #
        #     opponent = None
        #     home_team = None
        #     away_team = None
        #     if this_result.count() > 0:
        #         # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
        #         # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
        #         #              f'This Team: {x.player.team}')
        #         home_team = this_result[0].hometeam
        #         away_team = this_result[0].awayteam
        #         if this_result[0].hometeam == x.player.team:
        #             opponent = this_result[0].awayteam
        #         else:
        #             opponent = this_result[0].hometeam
        #     # logging.info(f'Opponent: {opponent}')
        #
        #     all_leaders['Rob Attempts']['leaders'].append(
        #         {
        #             'player': x.player.name,
        #             'team': x.team.abbrev,
        #             'stat': x.roba,
        #             'season': x.player.season,
        #             'week': x.week,
        #             'game_num': x.game,
        #             'opponent': opponent.abbrev if opponent else '???',
        #             'home_team': home_team.abbrev if home_team else '???',
        #             'away_team': away_team.abbrev if away_team else '???'
        #         }
        #     )
        # for x in top_robs:
        #     this_result = Result.select().where(
        #         (Result.season == x.season) & (Result.week == x.week) &
        #         ((Result.awayteam == x.team) | (Result.hometeam == x.team))
        #     ).limit(1)
        #
        #     opponent = None
        #     home_team = None
        #     away_team = None
        #     if this_result.count() > 0:
        #         # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
        #         # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
        #         #              f'This Team: {x.player.team}')
        #         home_team = this_result[0].hometeam
        #         away_team = this_result[0].awayteam
        #         if this_result[0].hometeam == x.player.team:
        #             opponent = this_result[0].awayteam
        #         else:
        #             opponent = this_result[0].hometeam
        #     # logging.info(f'Opponent: {opponent}')
        #
        #     all_leaders['Rob Successes']['leaders'].append(
        #         {
        #             'player': x.player.name,
        #             'team': x.team.abbrev,
        #             'stat': x.robs,
        #             'season': x.player.season,
        #             'week': x.week,
        #             'game_num': x.game,
        #             'opponent': opponent.abbrev if opponent else '???',
        #             'home_team': home_team.abbrev if home_team else '???',
        #             'away_team': away_team.abbrev if away_team else '???'
        #         }
        #     )

    else:
        all_leaders = {
            'Home Runs': {'abbrev': 'HR', 'leaders': []},
            'Hits': {'abbrev': 'H', 'leaders': []},
            'RBI': {'abbrev': 'RBI', 'leaders': []},
            'Hit for Cycle': {'abbrev': 'H', 'leaders': []},
            'Stolen Bases': {'abbrev': 'SB', 'leaders': []},
            'Doubles': {'abbrev': '2B', 'leaders': []},
            'Triples': {'abbrev': '3B', 'leaders': []},
            'Walks': {'abbrev': 'BB', 'leaders': []},
            # 'Extra Bases Taken': {'abbrev': 'XBT', 'leaders': []},
            'Strikeouts': {'abbrev': 'K', 'leaders': []},
            'Hit by Pitch': {'abbrev': 'HBP', 'leaders': []},
        }
        all_stats = BattingStat.select().join(Team).where(BattingStat.team.abbrev != 'SRS')
        if season:
            all_stats = all_stats.where(BattingStat.season == season)
        if team_abbrev and season:
            this_team = Team.get_season(team_abbrev, season)
            if not this_team:
                raise HTTPException(404, f'Team {team_abbrev} not found in season {season}')
            all_stats = all_stats.where(BattingStat.team == this_team)

        top_hr = (all_stats.where(BattingStat.hr > 1).order_by(-BattingStat.hr, BattingStat.season).limit(10))
        logging.info(f'top_hr: {top_hr}')
        top_hit = (all_stats.where(BattingStat.hit > 2).order_by(
            -BattingStat.hit, BattingStat.season, BattingStat.week
        ).limit(10))
        top_rbi = (all_stats.where(BattingStat.rbi > 3).order_by(
            -BattingStat.rbi, BattingStat.season, BattingStat.week
        ).limit(10))
        top_sb = (all_stats.where(BattingStat.sb > 1).order_by(
            -BattingStat.sb, BattingStat.season, BattingStat.week
        ).limit(10))
        top_dbl = (all_stats.where(BattingStat.double > 1).order_by(
            -BattingStat.double, BattingStat.season, BattingStat.week
        ).limit(10))
        top_tri = (all_stats.where(BattingStat.triple > 1).order_by(
            -BattingStat.triple, BattingStat.season, BattingStat.week
        ).limit(10))
        top_bb = (all_stats.where(BattingStat.bb > 2).order_by(
            -BattingStat.bb, BattingStat.season, BattingStat.week
        ).limit(10))
        # top_xbt = (all_stats.where(BattingStat.xbt > 1).order_by(
        #     -BattingStat.xbt, BattingStat.season, BattingStat.week
        # ).limit(10))
        top_k = (all_stats.where(BattingStat.so > 2).order_by(
            -BattingStat.so, BattingStat.season, BattingStat.week
        ).limit(10))
        top_hbp = (all_stats.where(BattingStat.hbp > 1).order_by(
            -BattingStat.hbp, BattingStat.season, BattingStat.week
        ).limit(10))
        top_ccl = (all_stats.where(
            (BattingStat.hr > 0) & (BattingStat.triple > 0) & (BattingStat.double > 0) &
            ((BattingStat.hit - BattingStat.double - BattingStat.triple - BattingStat.hr) > 0)
        ).order_by(-BattingStat.hit, BattingStat.season, BattingStat.week).limit(10))

        for x in top_hr:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Home Runs']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.hr}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_hit:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hits']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.hit}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_rbi:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['RBI']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.rbi}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_sb:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Stolen Bases']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.sb}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_dbl:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Doubles']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.double}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_tri:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Triples']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.triple}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_bb:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Walks']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.bb}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        # for x in top_xbt:
        #     this_result = Result.select().where(
        #         (Result.season == x.season) & (Result.week == x.week) &
        #         ((Result.awayteam == x.team) | (Result.hometeam == x.team))
        #     ).limit(1)
        #
        #     opponent = None
        #     home_team = None
        #     away_team = None
        #     if this_result.count() > 0:
        #         # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
        #         # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
        #         #              f'This Team: {x.player.team}')
        #         home_team = this_result[0].hometeam
        #         away_team = this_result[0].awayteam
        #         if this_result[0].hometeam == x.player.team:
        #             opponent = this_result[0].awayteam
        #         else:
        #             opponent = this_result[0].hometeam
        #     # logging.info(f'Opponent: {opponent}')
        #
        #     all_leaders['Extra Bases Taken']['leaders'].append(
        #         {
        #             'player': x.player.name,
        #             'season': x.player.season,
        #             'team': x.team.abbrev,
        #             'stat': f'{x.xbt}',
        #             'week': x.week,
        #             'game_num': x.game,
        #             'opponent': opponent.abbrev if opponent else '???',
        #             'home_team': home_team.abbrev if home_team else '???',
        #             'away_team': away_team.abbrev if away_team else '???',
        #         }
        #     )
        for x in top_k:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Strikeouts']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.so}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_hbp:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hit by Pitch']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.hbp}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )
        for x in top_ccl:
            this_result = Result.select().where(
                (Result.season == x.season) & (Result.week == x.week) &
                ((Result.awayteam == x.team) | (Result.hometeam == x.team))
            ).limit(1)

            opponent = None
            home_team = None
            away_team = None
            if this_result.count() > 0:
                # logging.info(f'Query: {this_result} / Result: {this_result[0]}')
                # logging.info(f'Home Team: {this_result[0].hometeam} / Away Team: {this_result[0].awayteam} / '
                #              f'This Team: {x.player.team}')
                home_team = this_result[0].hometeam
                away_team = this_result[0].awayteam
                if this_result[0].hometeam == x.player.team:
                    opponent = this_result[0].awayteam
                else:
                    opponent = this_result[0].hometeam
            # logging.info(f'Opponent: {opponent}')

            all_leaders['Hit for Cycle']['leaders'].append(
                {
                    'player': x.player.name,
                    'season': x.player.season,
                    'team': x.team.abbrev,
                    'stat': f'{x.hit}',
                    'week': x.week,
                    'game_num': x.game,
                    'opponent': opponent.abbrev if opponent else '???',
                    'home_team': home_team.abbrev if home_team else '???',
                    'away_team': away_team.abbrev if away_team else '???',
                }
            )

    db.close()
    return all_leaders


@app.get('/api/v1/teamstats')
async def v1_teamstats(
        group: str = 'batting', season: int = SEASON_DEFAULT, team_abbrev: Optional[str] = None):
    team_stats = {}
    if team_abbrev:
        all_teams = Team.select_season(season).where(Team.abbrev == team_abbrev)
    else:
        all_teams = Team.select_season(season).where(Team.division.is_null(False)).order_by(Team.sname)

    if group == 'batting':
        for team in all_teams:
            if team.abbrev[:2] != 'FA':
                team_stats[team.abbrev] = {
                    'team': model_to_dict(team),
                    'stats': BattingStat.team_season(team, season)
                }
    elif group == 'pitching':
        for team in all_teams:
            if team.abbrev[:2] != 'FA':
                team_stats[team.abbrev] = {
                    'team': model_to_dict(team),
                    'stats': PitchingStat.team_season(team, season)
                }
    else:
        for team in all_teams:
            if team.abbrev[:2] != 'FA':
                team_stats[team.abbrev] = {
                    'team': model_to_dict(team),
                    'stats': BattingStat.team_fielding_season(team, season)
                }

    db.close()
    return team_stats


@app.get('/api/v1/managers')
async def v1_managers_get():
    all_managers = Manager.select()

    return_managers = {}
    for x in all_managers:
        return_managers[f'{x.id}'] = model_to_dict(x)

    db.close()
    return return_managers


@app.get('/api/v1/managers/{name_or_id}')
async def v1_managers_get_one(name_or_id):
    try:
        this_manager = Manager.get_by_id(name_or_id)
    except:
        this_manager = Manager.get_or_none(fn.Lower(Manager.name) == name_or_id.lower())

    if not this_manager:
        db.close()
        raise HTTPException(status_code=404, detail=f'Manager {name_or_id} not found')

    db.close()
    return model_to_dict(this_manager)


@app.patch('/api/v1/managers/{name_or_id}')
async def v1_managers_patch(
        name_or_id, image: Optional[str] = None, headline: Optional[str] = None, bio: Optional[str] = None,
        token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch managers')

    try:
        this_manager = Manager.get_by_id(name_or_id)
    except:
        this_manager = Manager.get_or_none(fn.Lower(Manager.name) == name_or_id.lower())

    if not this_manager:
        db.close()
        raise HTTPException(status_code=404, detail=f'Manager {name_or_id} not found')

    if image:
        this_manager.image = image

    if headline:
        this_manager.headline = headline

    if bio:
        this_manager.bio = bio

    saved = this_manager.save()
    db.close()

    if saved == 1:
        return model_to_dict(this_manager)
    else:
        raise HTTPException(status_code=500, detail='Manager update has failed')


@app.post('/api/v1/managers')
async def v1_managers_post(manager: ManagerModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post managers')

    this_manager = Manager(name=manager.name)
    this_manager.save()

    if manager.image:
        this_manager.image = manager.image
    if manager.headline:
        this_manager.headline = manager.headline
    if manager.bio:
        this_manager.bio = manager.bio

    this_manager.save()
    db.close()

    return model_to_dict(this_manager)


@app.get('/api/v1/awards')
async def v1_awards_get(
        name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None,
        manager_id: Optional[int] = None, player_id: Optional[int] = None, team_id: Optional[int] = None,
        player_name: Optional[str] = None):
    all_awards = Award.select()

    if name:
        all_awards = all_awards.where(Award.name == name)
    if season:
        all_awards = all_awards.where(Award.season == season)
    if timing:
        all_awards = all_awards.where(Award.timing == timing)
    if manager_id:
        try:
            this_manager = Manager.get_by_id(manager_id)
            all_awards = all_awards.where(
                ((Award.manager1 == this_manager) | (Award.manager2 == this_manager))
            )
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager id {manager_id} not found')
    if player_id or player_name:
        if player_id:
            try:
                this_player = Player.get_by_id(player_id)
                all_awards = all_awards.where(Award.player == this_player)
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')
        else:
            these_players = Player.select().where(fn.Lower(Player.name) == player_name.lower())
            if these_players.count() == 0:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player {player_name} not found')

            all_awards = all_awards.where(Award.player << these_players)
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            all_awards = all_awards.where(Award.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')

    return_awards = {}
    for x in all_awards:
        return_awards[f'{x.id}'] = model_to_dict(x)

    db.close()
    return return_awards


@app.get('/api/v1/awards/{award_id}')
async def v1_awards_get_one(award_id):
    try:
        this_award = Award.get_by_id(award_id)
    except:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award {award_id} not found')

    db.close()
    return model_to_dict(this_award)


@app.patch('/api/v1/awards/{award_id}')
async def v1_awards_patch(
        award_id, name: Optional[str] = None, season: Optional[int] = None, timing: Optional[str] = None,
        image: Optional[str] = None, manager1_id: Optional[int] = None, manager2_id: Optional[int] = None,
        player_id: Optional[int] = None, team_id: Optional[int] = None, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to patch awards')

    try:
        this_award = Award.get_by_id(award_id)
    except:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award {award_id} not found')

    if name:
        this_award.name = name
    if image:
        this_award.image = image
    if timing:
        this_award.timing = timing
    if season:
        this_award.season = season
    if manager1_id:
        try:
            this_manager = Manager.get_by_id(manager1_id)
            this_award.manager1 = this_manager
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager id {manager1_id} not found')
    if manager2_id:
        try:
            this_manager = Manager.get_by_id(manager2_id)
            this_award.manager2 = this_manager
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Manager id {manager2_id} not found')
    if player_id:
        try:
            this_player = Player.get_by_id(player_id)
            this_award.player = this_player
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player id {player_id} not found')
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            this_award.team = this_team
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team id {team_id} not found')

    saved = this_award.save()
    db.close()

    if saved == 1:
        return model_to_dict(this_award)
    else:
        raise HTTPException(status_code=500, detail='Award update has failed')


@app.post('/api/v1/awards')
async def v1_awards_post(awards: AwardModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post awards')

    all_awards = []

    for award in awards.awards:
        this_award = Award(
            name=award.name,
            season=award.season
        )

        if award.image:
            this_award.image = award.image
        if award.timing:
            this_award.timing = award.timing
        if award.manager1_id:
            try:
                this_manager = Manager.get_by_id(award.manager1_id)
                this_award.manager1 = this_manager
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'Manager id {award.manager1_id} not found')
        if award.manager2_id:
            try:
                this_manager = Manager.get_by_id(award.manager2_id)
                this_award.manager2 = this_manager
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'Manager id {award.manager2_id} not found')
        if award.player_id:
            try:
                this_player = Player.get_by_id(award.player_id)
                this_award.player = this_player
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'Player id {award.player_id} not found')
        if award.team_id:
            try:
                this_team = Team.get_by_id(award.team_id)
                this_award.team = this_team
            except:
                db.close()
                raise HTTPException(status_code=404, detail=f'Team id {award.team_id} not found')

        all_awards.append(this_award)

    with db.atomic():
        Award.bulk_create(all_awards, batch_size=15)
    db.close()

    raise HTTPException(status_code=200, detail=f'Created {len(all_awards)} awards')


@app.delete('/api/v1/awards/{id}')
async def v1_awards_delete(id):
    try:
        this_award = Award.get_by_id(id)
    except:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award {id} not found')

    count = this_award.delete_instance()
    db.close()
    if count == 1:
        raise HTTPException(status_code=200, detail=f'Award {id} has been deleted')
    else:
        raise HTTPException(status_code=500, detail=f'Award was not deleted')


@app.get('/api/v1/dice')
async def v1_dice_get(
        season: Optional[int] = None, week_start: Optional[int] = None, week_end: Optional[int] = None,
        team_id: Optional[int] = None, roller: Optional[int] = None):
    all_dice = DiceRoll.select()

    if season:
        all_dice = all_dice.where(DiceRoll.season == season)

    if (week_start and not week_end) or (week_end and not week_start):
        if week_start:
            all_dice = all_dice.where(DiceRoll.week >= week_start)
        else:
            all_dice = all_dice.where(DiceRoll.week <= week_start)
    if week_start and week_end:
        if week_end >= week_start:
            all_dice = all_dice.where((DiceRoll.week >= week_start) & (DiceRoll.week <= week_end))
        else:
            db.close()
            raise HTTPException(status_code=400, detail='Week end must be greater than or equal to week start')
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            all_dice = all_dice.where(DiceRoll.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found')
    if roller:
        all_dice = all_dice.where(DiceRoll.roller == roller)

    return_dice = {}
    for x in all_dice:
        return_dice[f'{x.id}'] = model_to_dict(x)

    db.close()
    return return_dice


@app.post('/api/v1/dice')
async def v1_dice_post(dice: DiceModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls')
    all_dice = []

    for x in dice.rolls:
        try:
            this_team = Team.get_by_id(x.team_id)
            this_die = DiceRoll(
                season=x.season,
                week=x.week,
                team=this_team,
                roller=x.roller,
                dsix=x.dsix,
                twodsix=x.twodsix,
                threedsix=x.threedsix,
                dtwenty=x.dtwenty
            )
            all_dice.append(this_die)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found')

    with db.atomic():
        DiceRoll.bulk_create(all_dice, batch_size=20)

    db.close()
    raise HTTPException(status_code=200, detail=f'Posted {len(all_dice)} dice rolls')


@app.delete('/api/v1/dice')
async def v1_dice_delete(
        season: int, week_start: Optional[int] = None, week_end: Optional[int] = None,
        team_id: Optional[int] = None, roller: Optional[int] = None):
    delete_query = DiceRoll.delete().where(DiceRoll.season == season)

    if (week_start and not week_end) or (week_end and not week_start):
        if week_start:
            delete_query = delete_query.where(DiceRoll.week >= week_start)
        else:
            delete_query = delete_query.where(DiceRoll.week <= week_start)
    if week_start and week_end:
        if week_end >= week_start:
            delete_query = delete_query.where((DiceRoll.week >= week_start) & (DiceRoll.week <= week_end))
        else:
            db.close()
            raise HTTPException(status_code=400, detail='Week end must be greater than or equal to week start')
    if team_id:
        try:
            this_team = Team.get_by_id(team_id)
            delete_query = delete_query.where(DiceRoll.team == this_team)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Team ID {team_id} not found')
    if roller:
        delete_query = delete_query.where(DiceRoll.roller == roller)

    print(f'delete_query: {delete_query}')

    count = delete_query.execute()
    db.close()
    if count > 0:
        raise HTTPException(status_code=200, detail=f'Removed {count} batting stat lines')
    else:
        raise HTTPException(status_code=418, detail=f'Well slap my ass and call me a teapot; '
                                                    f'I did not delete any records')


@app.post('/api/v1/draft-list')
async def v1_draft_list_post(draft_list: DraftListModel, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls')
    all_draft_list = []

    try:
        this_team = Team.get_by_id(draft_list.draft_list[0].team_id)
    except:
        db.close()
        raise HTTPException(status_code=404, detail=f'Team ID {draft_list.draft_list[0].team_id} not found')

    old_list = DraftList.delete().where(
        DraftList.team == this_team
    )
    old_list.execute()

    for x in draft_list.draft_list:
        try:
            this_player = Player.get_by_id(x.player_id)
        except:
            db.close()
            raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')

        this_list = DraftList(
            season=x.season,
            team=this_team,
            rank=x.rank,
            player=this_player
        )
        all_draft_list.append(this_list)

    with db.atomic():
        DraftList.bulk_create(all_draft_list, batch_size=20)

    db.close()
    raise HTTPException(status_code=200, detail=f'Posted {len(all_draft_list)} dice rolls')


@app.get('/api/v1/draft-list/{team_id}')
async def v1_draft_list_get(
        team_id, return_type: Optional[str] = 'json', csv: Optional[bool] = False, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logging.warning(f'Bad Token: {token}')
        db.close()
        raise HTTPException(status_code=401, detail='You are not authorized to post dice rolls')

    this_team = Team.get_by_id(team_id)
    team_list = DraftList.select().where(DraftList.team == this_team)

    if return_type == 'csv' or csv:
        return_list = [['season', 'team', 'rank', 'player']]
        for x in team_list:
            return_list.append([x.season, x.team.abbrev, x.rank, x.player.name])
    else:
        return_list = {}
        for x in team_list:
            return_list[f'{x.id}'] = model_to_dict(x)

    db.close()
    return return_list
