import inspect
from importlib import import_module
from pathlib import Path

from starlette.endpoints import HTTPEndpoint
from starlette.responses import RedirectResponse
from starlette.routing import Route, Mount

from otree import common, settings
from otree.channels.routing import websocket_routes
from otree.common2 import static_files_app

ALWAYS_UNRESTRICTED = {
    # REST views don't need to be here because they don't use the
    # _login_required flag to begin with. they are automatically open.
    'AssignVisitorToRoom',
    'InitializeParticipant',
    'Login',
    'MTurkLandingPage',
    'MTurkStart',
    'JoinSessionAnonymously',
    'OutOfRangeNotification',
    'BrowserBotStartLink',
    'SaveDB',
    'WSSubsessionWaitPage',
    'WSGroupWaitPage',
    'LiveConsumer',
    'WSGroupByArrivalTime',
    'DetectAutoAdvance',
    'WSRoomParticipant',
    'WSBrowserBotsLauncher',
    'WSBrowserBot',
    'WSChat',
}


UNRESTRICTED_IN_DEMO_MODE = ALWAYS_UNRESTRICTED.union(
    {
        'AdminReport',
        'AdvanceSession',
        'CreateDemoSession',
        'DemoIndex',
        'SessionSplitScreen',
        'SessionDescription',
        'SessionMonitor',
        'SessionPayments',
        'SessionData',
        'SessionDataAjax',
        'SessionStartLinks',
        'WSCreateDemoSession',
        'WSSessionMonitor',
    }
)


def view_classes_from_module(module_name):
    views_module = import_module(module_name)

    return [
        ViewCls
        for _, ViewCls in inspect.getmembers(views_module)
        if hasattr(ViewCls, 'url_pattern')
        and inspect.getmodule(ViewCls) == views_module
    ]


def url_patterns_from_app_pages(app_name, name_in_url):
    pages_module = common.get_pages_module(app_name)
    is_noself = common.is_noself(app_name)

    page_urls = []
    for ViewCls in pages_module.page_sequence:
        ViewCls.is_noself = is_noself
        # don't set it back because this just happens on startup

        url_pattern = ViewCls.url_pattern(name_in_url)
        url_name = ViewCls.url_name()
        page_urls.append(Route(url_pattern, ViewCls, name=url_name))

    return page_urls


def url_patterns_from_builtin_module(module_name: str):

    all_views = view_classes_from_module(module_name)

    view_urls = []
    for ViewCls in all_views:
        # automatically assign URL name for reverse(), it defaults to the
        # class's name
        url_name = getattr(ViewCls, 'url_name', ViewCls.__name__)

        ViewCls._requires_login = {
            'STUDY': url_name not in ALWAYS_UNRESTRICTED,
            'DEMO': url_name not in UNRESTRICTED_IN_DEMO_MODE,
            '': False,
            None: False,
        }[settings.AUTH_LEVEL]

        url_pattern = ViewCls.url_pattern
        if callable(url_pattern):
            url_pattern = url_pattern()

        view_urls.append(Route(url_pattern, ViewCls, name=url_name))

    return view_urls


def get_urlpatterns():

    routes = []

    used_names_in_url = set()
    for app_name in settings.OTREE_APPS:
        Constants = common.get_constants(app_name)
        name_in_url = Constants.get_normalized('name_in_url')
        if name_in_url in used_names_in_url:
            msg = (
                "App {} has name_in_url='{}', " "which is already used by another app"
            ).format(app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)

        routes += url_patterns_from_app_pages(app_name, name_in_url)

    routes += url_patterns_from_builtin_module('otree.views.participant')
    routes += url_patterns_from_builtin_module('otree.views.demo')
    routes += url_patterns_from_builtin_module('otree.views.admin')
    routes += url_patterns_from_builtin_module('otree.views.room')
    routes += url_patterns_from_builtin_module('otree.views.mturk')
    routes += url_patterns_from_builtin_module('otree.views.export')
    routes += url_patterns_from_builtin_module('otree.views.rest')
    routes += websocket_routes
    routes += [
        Mount('/static', app=static_files_app, name="static",),
        Route("/favicon.ico", endpoint=Favicon),
        Route('/', endpoint=HomeRedirect),
    ]

    return routes


class Favicon(HTTPEndpoint):
    async def get(self, request):
        return RedirectResponse('/static/favicon.ico')


class HomeRedirect(HTTPEndpoint):
    async def get(self, request):
        return RedirectResponse('/demo')


routes = get_urlpatterns()
