from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Optional
import logging
import pydantic

from ..db_engine import db, Award, Team, Player, Manager, model_to_dict, chunked, fn
from ..dependencies import oauth2_scheme, valid_token, PRIVATE_IN_SCHEMA, handle_db_errors

logger = logging.getLogger('discord_app')

router = APIRouter(
    prefix='/api/v3/awards',
    tags=['awards']
)


class AwardModel(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 AwardList(pydantic.BaseModel):
    count: int
    awards: List[AwardModel]


@router.get('')
@handle_db_errors
async def get_awards(
        name: list = Query(default=None), season: Optional[int] = None, timing: Optional[str] = None,
        manager_id: list = Query(default=None), player_id: list = Query(default=None),
        team_id: list = Query(default=None), short_output: Optional[bool] = False,
        player_name: list = Query(default=None)):
    all_awards = Award.select()

    if name is not None:
        name_list = [x.lower() for x in name]
        all_awards = all_awards.where(fn.Lower(Award.name) == name_list)
    if season is not None:
        all_awards = all_awards.where(Award.season == season)
    if timing is not None:
        all_awards = all_awards.where(fn.Lower(Award.timing) == timing.lower())
    if manager_id is not None:
        managers = Manager.select().where(Manager.id << manager_id)
        all_awards = all_awards.where(
            (Award.manager1 << managers) | (Award.manager2 << managers)
        )
    if player_id is not None:
        all_awards = all_awards.where(Award.player_id << player_id)
    if team_id is not None:
        all_awards = all_awards.where(Award.team_id << team_id)
    if player_name is not None:
        pname_list = [x.lower() for x in player_name]
        all_players = Player.select().where(fn.Lower(Player.name) << pname_list)
        all_awards = all_awards.where(Award.player << all_players)

    return_awards = {
        'count': all_awards.count(),
        'awards': [model_to_dict(x, recurse=not short_output) for x in all_awards]
    }
    db.close()
    return return_awards


@router.get('/{award_id}')
@handle_db_errors
async def get_one_award(award_id: int, short_output: Optional[bool] = False):
    this_award = Award.get_or_none(Award.id == award_id)
    if this_award is None:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')

    db.close()
    return model_to_dict(this_award, recurse=not short_output)


@router.patch('/{award_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def patch_award(
        award_id: int, 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):
        logger.warning(f'patch_player - Bad Token: {token}')
        raise HTTPException(status_code=401, detail='Unauthorized')

    this_award = Award.get_or_none(Award.id == award_id)
    if this_award is None:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')

    if name is not None:
        this_award.name = name
    if season is not None:
        this_award.season = season
    if timing is not None:
        this_award.timing = timing
    if image is not None:
        this_award.image = image
    if manager1_id is not None:
        this_award.manager1_id = manager1_id
    if manager2_id is not None:
        this_award.manager2_id = manager2_id
    if player_id is not None:
        this_award.player_id = player_id
    if team_id is not None:
        this_award.team_id = team_id

    if this_award.save() == 1:
        r_award = model_to_dict(this_award)
        db.close()
        return r_award
    else:
        db.close()
        raise HTTPException(status_code=500, detail=f'Unable to patch award {award_id}')


@router.post('', include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def post_award(award_list: AwardList, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logger.warning(f'patch_player - Bad Token: {token}')
        raise HTTPException(status_code=401, detail='Unauthorized')

    new_awards = []
    for x in award_list.awards:
        if x.manager1_id is not None and Manager.get_or_none(Manager.id == x.manager1_id) is None:
            raise HTTPException(status_code=404, detail=f'Manager ID {x.manager1_id} not found')
        if x.manager2_id is not None and Manager.get_or_none(Manager.id == x.manager2_id) is None:
            raise HTTPException(status_code=404, detail=f'Manager ID {x.manager2_id} not found')
        if x.player_id is not None and Player.get_or_none(Player.id == x.player_id) is None:
            raise HTTPException(status_code=404, detail=f'Player ID {x.player_id} not found')
        if x.team_id is not None and Team.get_or_none(Team.id == x.team_id) is None:
            raise HTTPException(status_code=404, detail=f'Team ID {x.team_id} not found')

        new_awards.append(x.dict())

    with db.atomic():
        for batch in chunked(new_awards, 15):
            Award.insert_many(batch).on_conflict_ignore().execute()
    db.close()

    return f'Inserted {len(new_awards)} awards'


@router.delete('/{award_id}', include_in_schema=PRIVATE_IN_SCHEMA)
@handle_db_errors
async def delete_award(award_id: int, token: str = Depends(oauth2_scheme)):
    if not valid_token(token):
        logger.warning(f'patch_player - Bad Token: {token}')
        raise HTTPException(status_code=401, detail='Unauthorized')

    this_award = Award.get_or_none(Award.id == award_id)
    if this_award is None:
        db.close()
        raise HTTPException(status_code=404, detail=f'Award ID {award_id} not found')

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

    if count == 1:
        return f'Award {award_id} has been deleted'
    else:
        raise HTTPException(status_code=500, detail=f'Award {award_id} could not be deleted')




