# Path: animanager/db/query/collections.py
# class Anime:
#     aid: int = 0
#     title: str = ''
#     type: str = ''
#     episodecount: int = 0
#     startdate: int = 0
#     enddate: int = 0
#     watched_episodes: int = 0
#     complete: bool = False
#     regexp: str = ''
#     episodes: 'List[Episode]' = ()
# 
# class Episode:
#     aid: int = 0
#     type: str = ''
#     number: int = 0
#     title: str = ''
#     length: int = 0
#     user_watched: bool = False
# 
# Path: animanager/db/query/status.py
# STATUS_FIELDS = {
#     'complete': 'complete',
#     'watched_episodes': 'watched_episodes',
# }
# 
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)


import logging
from typing import Any, Iterable, Iterator, Mapping, Sequence, Union
from .collections import Anime, Episode
from .status import STATUS_FIELDS, cache_status
ALL = 1
"""Flag for selecting all fields."""

FieldsParam = Union[Iterable[str], int]


def _clean_fields(allowed_fields: dict, fields: FieldsParam) -> Iterable[str]:
    """Clean lookup fields and check for errors."""
    if fields == ALL:
        fields = allowed_fields.keys()
    else:
        fields = tuple(fields)
        unknown_fields = set(fields) - allowed_fields.keys()
        if unknown_fields:
            raise ValueError('Unknown fields: {}'.format(unknown_fields))
    return fields


ANIME_QUERY = """
    SELECT {}
    FROM anime
        LEFT JOIN cache_anime USING (aid)
        LEFT JOIN watching USING (aid)
    WHERE {}"""


def select(
        db,
        where_query: str,
        where_params: SQLParams,

# Path: animanager/db/query/collections.py
# class Anime:
#     aid: int = 0
#     title: str = ''
#     type: str = ''
#     episodecount: int = 0
#     startdate: int = 0
#     enddate: int = 0
#     watched_episodes: int = 0
#     complete: bool = False
#     regexp: str = ''
#     episodes: 'List[Episode]' = ()
# 
# class Episode:
#     aid: int = 0
#     type: str = ''
#     number: int = 0
#     title: str = ''
#     length: int = 0
#     user_watched: bool = False
# 
# Path: animanager/db/query/status.py
# STATUS_FIELDS = {
#     'complete': 'complete',
#     'watched_episodes': 'watched_episodes',
# }
# 
# def cache_status(db, aid, force=False):
#     """Calculate and cache status for given anime.
# 
#     Don't do anything if status already exists and force is False.
# 
#     """
#     with db:
#         cur = db.cursor()
#         if not force:
#             # We don't do anything if we already have this aid in our
#             # cache.
#             cur.execute('SELECT 1 FROM cache_anime WHERE aid=?', (aid,))
#             if cur.fetchone() is not None:
#                 return
# 
#         # Retrieve information for determining complete.
#         cur.execute(
#             'SELECT episodecount, enddate FROM anime WHERE aid=?', (aid,))
#         row = cur.fetchone()
#         if row is None:
#             raise ValueError('aid provided does not exist')
#         episodecount, enddate = row
# 
#         # Select all regular episodes in ascending order.
#         cur.execute("""
#             SELECT number, user_watched FROM episode
#             WHERE aid=? AND type=?
#             ORDER BY number ASC
#             """, (aid, get_eptype(db, 'regular').id))
# 
#         # We find the last consecutive episode that is user_watched.
#         number = 0
#         for number, watched in cur:
#             # Once we find the first unwatched episode, we set the last
#             # consecutive watched episode to the previous episode (or 0).
#             if watched == 0:
#                 number -= 1
#                 break
#         # We store this in the cache.
#         set_status(db, aid, enddate and episodecount <= number, number)


# Path: animanager/db/query/select.py
import logging
from typing import Any, Iterable, Iterator, Mapping, Sequence, Union
from .collections import Anime, Episode
from .status import STATUS_FIELDS, cache_status

ALL = 1
"""Flag for selecting all fields."""

FieldsParam = Union[Iterable[str], int]


def _clean_fields(allowed_fields: dict, fields: FieldsParam) -> Iterable[str]:
    """Clean lookup fields and check for errors."""
    if fields == ALL:
        fields = allowed_fields.keys()
    else:
        fields = tuple(fields)
        unknown_fields = set(fields) - allowed_fields.keys()
        if unknown_fields:
            raise ValueError('Unknown fields: {}'.format(unknown_fields))
    return fields


ANIME_QUERY = """
    SELECT {}
    FROM anime
        LEFT JOIN cache_anime USING (aid)
        LEFT JOIN watching USING (aid)
    WHERE {}"""


def select(
        db,
        where_query: str,
        where_params: SQLParams,

        fields: FieldsParam = ALL,