from collections import namedtuple
from functools import lru_cache
from typing import Dict

from otree.common import get_pages_module, get_models_module, get_constants
from otree.database import dbq
from otree.models import Session

PageLookup = namedtuple(
    'PageInfo',
    [
        'app_name',
        'page_class',
        'round_number',
        'subsession_id',
        'name_in_url',
        'session_pk',
    ],
)


@lru_cache(maxsize=32)
def _get_session_lookups(session_code) -> Dict[int, PageLookup]:
    session = dbq(Session).filter_by(code=session_code).one()
    pages = {}
    idx = 1
    for app_name in session.config['app_sequence']:
        models = get_models_module(app_name)
        Subsession = models.Subsession
        page_sequence = get_pages_module(app_name).page_sequence
        subsessions = {
            s[0]: s[1]
            for s in Subsession.objects_filter(session=session).with_entities(
                Subsession.round_number, Subsession.id
            )
        }

        Constants = get_constants(app_name)
        num_rounds = Constants.get_normalized('num_rounds')
        name_in_url = Constants.get_normalized('name_in_url')
        for rd in range(1, num_rounds + 1):
            for PageClass in page_sequence:
                pages[idx] = PageLookup(
                    app_name=app_name,
                    page_class=PageClass,
                    round_number=rd,
                    subsession_id=subsessions[rd],
                    session_pk=session.id,
                    name_in_url=name_in_url,
                )
                idx += 1
    return pages


def get_page_lookup(session_code, idx) -> PageLookup:
    cache = _get_session_lookups(session_code)
    return cache[idx]


def get_min_idx_for_app(session_code, app_name):
    '''for aatp'''
    for idx, info in _get_session_lookups(session_code).items():
        if info.app_name == app_name:
            return idx


def url_i_should_be_on(participant_code, session_code, index_in_pages) -> str:
    idx = index_in_pages
    lookup = get_page_lookup(session_code, idx)
    return lookup.page_class.get_url(
        participant_code=participant_code,
        name_in_url=lookup.name_in_url,
        page_index=idx,
    )
