# -*- coding: utf-8 -*-
import os
import httplib as http

from flask import request
from flask import send_from_directory

from framework import status
from framework import sentry
from framework.auth import cas
from framework.routing import Rule
from framework.flask import redirect
from framework.sessions import session
from framework.routing import WebRenderer
from framework.exceptions import HTTPError
from framework.auth import get_display_name
from framework.routing import xml_renderer
from framework.routing import json_renderer
from framework.routing import process_rules
from framework.auth import views as auth_views
from framework.routing import render_mako_string
from framework.auth.core import _get_current_user

from website import util
from website import settings
from website import language
from website.util import paths
from website.util import sanitize
from website import landing_pages as landing_page_views
from website import views as website_views
from website.citations import views as citation_views
from website.search import views as search_views
from website.oauth import views as oauth_views
from website.profile import views as profile_views
from website.project import views as project_views
from website.addons.base import views as addon_views
from website.discovery import views as discovery_views
from website.conferences import views as conference_views
from website.notifications import views as notification_views

def get_globals():
    """Context variables that are available for every template rendered by
    OSFWebRenderer.
    """
    user = _get_current_user()
    return {
        'user_name': user.username if user else '',
        'user_full_name': user.fullname if user else '',
        'user_id': user._primary_key if user else '',
        'user_url': user.url if user else '',
        'user_gravatar': profile_views.current_user_gravatar(size=25)['gravatar_url'] if user else '',
        'user_api_url': user.api_url if user else '',
        'display_name': get_display_name(user.fullname) if user else '',
        'use_cdn': settings.USE_CDN_FOR_CLIENT_LIBS,
        'piwik_host': settings.PIWIK_HOST,
        'piwik_site_id': settings.PIWIK_SITE_ID,
        'sentry_dsn_js': settings.SENTRY_DSN_JS if sentry.enabled else None,
        'dev_mode': settings.DEV_MODE,
        'allow_login': settings.ALLOW_LOGIN,
        'cookie_name': settings.COOKIE_NAME,
        'status': status.pop_status_messages(),
        'domain': settings.DOMAIN,
        'disk_saving_mode': settings.DISK_SAVING_MODE,
        'language': language,
        'web_url_for': util.web_url_for,
        'api_url_for': util.api_url_for,
        'api_v2_url': util.api_v2_url,  # URL function for templates
        'api_v2_base': util.api_v2_url(''),  # Base url used by JS api helper
        'sanitize': sanitize,
        'js_str': lambda x: x.replace("'", r"\'").replace('"', r'\"'),
        'sjson': lambda s: sanitize.safe_json(s),
        'webpack_asset': paths.webpack_asset,
        'waterbutler_url': settings.WATERBUTLER_URL,
        'login_url': cas.get_login_url(request.url, auto=True),
        'access_token': session.data.get('auth_user_access_token') or '',
        'auth_url': cas.get_login_url(request.url),
        'profile_url': cas.get_profile_url(),
    }


class OsfWebRenderer(WebRenderer):
    """Render a Mako template with OSF context vars.

    :param trust: Optional. If ``False``, markup-save escaping will be enabled
    """
    def __init__(self, *args, **kwargs):
        kwargs['data'] = get_globals
        super(OsfWebRenderer, self).__init__(*args, **kwargs)

#: Use if a view only redirects or raises error
notemplate = OsfWebRenderer('', render_mako_string)


# Static files (robots.txt, etc.)

def favicon():
    return send_from_directory(
        settings.STATIC_FOLDER,
        'favicon.ico',
        mimetype='image/vnd.microsoft.icon'
    )


def robots():
    """Serves the robots.txt file."""
    # Allow local robots.txt
    if os.path.exists(os.path.join(settings.STATIC_FOLDER,
                                   'robots.local.txt')):
        robots_file = 'robots.local.txt'
    else:
        robots_file = 'robots.txt'
    return send_from_directory(
        settings.STATIC_FOLDER,
        robots_file,
        mimetype='text/plain'
    )


def goodbye():
    # Redirect to dashboard if logged in
    if _get_current_user():
        return redirect(util.web_url_for('dashboard'))
    status.push_status_message(language.LOGOUT, 'success')
    return {}


def make_url_map(app):
    '''Set up all the routes for the OSF app.

    :param app: A Flask/Werkzeug app to bind the rules to.
    '''

    # Set default views to 404, using URL-appropriate renderers
    process_rules(app, [
        Rule('/<path:_>', ['get', 'post'], HTTPError(http.NOT_FOUND),
             OsfWebRenderer('', render_mako_string)),
        Rule('/api/v1/<path:_>', ['get', 'post'],
             HTTPError(http.NOT_FOUND), json_renderer),
    ])

    ### GUID ###
    process_rules(app, [

        Rule(
            [
                '/<guid>/',
                '/<guid>/<path:suffix>',
            ],
            ['get', 'post', 'put', 'patch', 'delete'],
            website_views.resolve_guid,
            notemplate,
        ),

        Rule(
            [
                '/api/v1/<guid>/',
                '/api/v1/<guid>/<path:suffix>',
            ],
            ['get', 'post', 'put', 'patch', 'delete'],
            website_views.resolve_guid,
            json_renderer,
        ),

    ])

    # Static files
    process_rules(app, [
        Rule('/favicon.ico', 'get', favicon, json_renderer),
        Rule('/robots.txt', 'get', robots, json_renderer),
    ])

    ### Base ###

    process_rules(app, [

        Rule('/dashboard/', 'get', website_views.dashboard, OsfWebRenderer('dashboard.mako')),
        Rule('/reproducibility/', 'get',
             website_views.reproducibility, OsfWebRenderer('', render_mako_string)),

        Rule('/about/', 'get', {}, OsfWebRenderer('public/pages/about.mako')),
        Rule('/howosfworks/', 'get', {}, OsfWebRenderer('public/pages/howosfworks.mako')),
        Rule('/faq/', 'get', {}, OsfWebRenderer('public/pages/faq.mako')),
        Rule('/getting-started/', 'get', {}, OsfWebRenderer('public/pages/getting_started.mako')),
        Rule('/explore/', 'get', {}, OsfWebRenderer('public/explore.mako')),
        Rule(['/messages/', '/help/'], 'get', {}, OsfWebRenderer('public/comingsoon.mako')),

        Rule(
            '/view/<meeting>/',
            'get',
            conference_views.conference_results,
            OsfWebRenderer('public/pages/meeting.mako'),
        ),

        Rule(
            '/view/<meeting>/plain/',
            'get',
            conference_views.conference_results,
            OsfWebRenderer('public/pages/meeting_plain.mako'),
            endpoint_suffix='__plain',
        ),

        Rule(
            '/api/v1/view/<meeting>/',
            'get',
            conference_views.conference_data,
            json_renderer,
        ),

        Rule(
            '/meetings/',
            'get',
            conference_views.conference_view,
            OsfWebRenderer('public/pages/meeting_landing.mako'),
        ),

        Rule(
            '/presentations/',
            'get',
            conference_views.redirect_to_meetings,
            json_renderer,
        ),

        Rule('/news/', 'get', {}, OsfWebRenderer('public/pages/news.mako')),

    ])

    # Site-wide API routes

    process_rules(app, [
        Rule(
            '/citations/styles/',
            'get',
            citation_views.list_citation_styles,
            json_renderer,
        ),
    ], prefix='/api/v1')

    process_rules(app, [
        Rule(
            [
                '/project/<pid>/<addon>/settings/disable/',
                '/project/<pid>/node/<nid>/<addon>/settings/disable/',
            ],
            'post',
            addon_views.disable_addon,
            json_renderer,
        ),

        Rule(
            '/profile/<uid>/<addon>/settings/',
            'get',
            addon_views.get_addon_user_config,
            json_renderer,
        ),
    ], prefix='/api/v1')

    # OAuth

    process_rules(app, [
        Rule(
            '/oauth/connect/<service_name>/',
            'get',
            oauth_views.oauth_connect,
            json_renderer,
        ),

        Rule(
            '/oauth/callback/<service_name>/',
            'get',
            oauth_views.oauth_callback,
            OsfWebRenderer('util/oauth_complete.mako'),
        ),
    ])

    process_rules(app, [
        Rule(
            [
                '/oauth/accounts/<external_account_id>/',
            ],
            'delete',
            oauth_views.oauth_disconnect,
            json_renderer,
        )
    ], prefix='/api/v1')

    process_rules(app, [
        Rule('/dashboard/get_nodes/', 'get', website_views.get_dashboard_nodes, json_renderer),
        Rule(
            [
                '/dashboard/<nid>',
                '/dashboard/',
            ],
            'get', website_views.get_dashboard, json_renderer),
    ], prefix='/api/v1')

    ### Metadata ###

    process_rules(app, [

        Rule(
            [
                '/project/<pid>/comments/',
                '/project/<pid>/node/<nid>/comments/',
            ],
            'get',
            project_views.comment.list_comments,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comments/discussion/',
                '/project/<pid>/node/<nid>/comments/discussion/',
            ],
            'get',
            project_views.comment.comment_discussion,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/',
                '/project/<pid>/node/<nid>/comment/',
            ],
            'post',
            project_views.comment.add_comment,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/<cid>/',
                '/project/<pid>/node/<nid>/comment/<cid>/',
            ],
            'put',
            project_views.comment.edit_comment,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/<cid>/',
                '/project/<pid>/node/<nid>/comment/<cid>/',
            ],
            'delete',
            project_views.comment.delete_comment,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/<cid>/undelete/',
                '/project/<pid>/node/<nid>/comment/<cid>/undelete/',
            ],
            'put',
            project_views.comment.undelete_comment,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comments/timestamps/',
                '/project/<pid>/node/<nid>/comments/timestamps/',
            ],
            'put',
            project_views.comment.update_comments_timestamp,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/<cid>/report/',
                '/project/<pid>/node/<nid>/comment/<cid>/report/',
            ],
            'post',
            project_views.comment.report_abuse,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/comment/<cid>/unreport/',
                '/project/<pid>/node/<nid>/comment/<cid>/unreport/',
            ],
            'post',
            project_views.comment.unreport_abuse,
            json_renderer,
        ),

        Rule(
            [
                '/project/<pid>/citation/',
                '/project/<pid>/node/<nid>/citation/',
            ],
            'get',
            citation_views.node_citation,
            json_renderer,
        ),

    ], prefix='/api/v1')

    ### Forms ###

    process_rules(app, [
        Rule('/forms/registration/', 'get', website_views.registration_form, json_renderer),
        Rule('/forms/signin/', 'get', website_views.signin_form, json_renderer),
        Rule('/forms/forgot_password/', 'get', website_views.forgot_password_form, json_renderer),
        Rule('/forms/reset_password/', 'get', website_views.reset_password_form, json_renderer),
    ], prefix='/api/v1')

    ### Discovery ###

    process_rules(app, [

        Rule('/explore/activity/', 'get', discovery_views.activity,
             OsfWebRenderer('public/pages/active_nodes.mako')),

    ])

    ### Auth ###

    # Web

    process_rules(app, [

        Rule(
            '/confirm/<uid>/<token>/',
            'get',
            auth_views.confirm_email_get,
            # View will either redirect or display error message
            OsfWebRenderer('error.mako', render_mako_string)
        ),

        Rule(
            '/resetpassword/<verification_key>/',
            ['get', 'post'],
            auth_views.reset_password,
            OsfWebRenderer('public/resetpassword.mako', render_mako_string)
        ),

        # Resend confirmation URL linked to in CAS login page
        Rule(
            '/resend/',
            ['get', 'post'],
            auth_views.resend_confirmation,
            OsfWebRenderer('resend.mako', render_mako_string)
        ),

        # TODO: Remove `auth_register_post`
        Rule('/register/', 'post', auth_views.auth_register_post,
             OsfWebRenderer('public/login.mako')),
        Rule('/api/v1/register/', 'post', auth_views.register_user, json_renderer),

        Rule(['/login/', '/account/'], 'get',
             auth_views.auth_login, OsfWebRenderer('public/login.mako')),
        Rule('/login/first/', 'get', auth_views.auth_login,
             OsfWebRenderer('public/login.mako'),
             endpoint_suffix='__first', view_kwargs={'first': True}),
        Rule('/logout/', 'get', auth_views.auth_logout, notemplate),
        Rule('/forgotpassword/', 'get', auth_views.forgot_password_get,
             OsfWebRenderer('public/forgot_password.mako')),
        Rule('/forgotpassword/', 'post', auth_views.forgot_password_post,
             OsfWebRenderer('public/login.mako')),

        Rule([
            '/midas/', '/summit/', '/accountbeta/', '/decline/'
        ], 'get', auth_views.auth_registerbeta, OsfWebRenderer('', render_mako_string)),

        Rule('/login/connected_tools/',
             'get',
             landing_page_views.connected_tools,
             OsfWebRenderer('public/login_landing.mako')),

        Rule('/login/enriched_profile/',
             'get',
             landing_page_views.enriched_profile,
             OsfWebRenderer('public/login_landing.mako')),

    ])

    ### Profile ###

    # Web

    process_rules(app, [
        Rule('/profile/', 'get', profile_views.profile_view, OsfWebRenderer('profile.mako')),
        Rule('/profile/<uid>/', 'get', profile_views.profile_view_id,
             OsfWebRenderer('profile.mako')),
        Rule(["/user/merge/"], 'get', auth_views.merge_user_get,
             OsfWebRenderer("merge_accounts.mako")),
        Rule(["/user/merge/"], 'post', auth_views.merge_user_post,
             OsfWebRenderer("merge_accounts.mako")),
        # Route for claiming and setting email and password.
        # Verification token must be querystring argument
        Rule(['/user/<uid>/<pid>/claim/'], ['get', 'post'],
             project_views.contributor.claim_user_form, OsfWebRenderer('claim_account.mako')),
        Rule(['/user/<uid>/<pid>/claim/verify/<token>/'], ['get', 'post'],
             project_views.contributor.claim_user_registered,
             OsfWebRenderer('claim_account_registered.mako')),


        Rule(
            '/settings/',
            'get',
            profile_views.user_profile,
            OsfWebRenderer('profile/settings.mako'),
        ),

        Rule(
            '/settings/account/',
            'get',
            profile_views.user_account,
            OsfWebRenderer('profile/account.mako'),
        ),

        Rule(
            '/settings/account/password',
            'post',
            profile_views.user_account_password,
            OsfWebRenderer('profile/account.mako'),
        ),

        Rule(
            '/settings/addons/',
            'get',
            profile_views.user_addons,
            OsfWebRenderer('profile/addons.mako'),
        ),

        Rule(
            '/settings/notifications/',
            'get',
            profile_views.user_notifications,
            OsfWebRenderer('profile/notifications.mako'),
        ),

        # TODO: Uncomment once outstanding issues with this feature are addressed
        # Rule(
        #     '/@<twitter_handle>/',
        #     'get',
        #     profile_views.redirect_to_twitter,
        #     OsfWebRenderer('error.mako', render_mako_string)
        # ),

    ])

    # API

    process_rules(app, [

        Rule('/profile/', 'get', profile_views.profile_view, json_renderer),
        Rule('/profile/', 'put', profile_views.update_user, json_renderer),
        Rule('/resend/', 'put', profile_views.resend_confirmation, json_renderer),
        Rule('/profile/<uid>/', 'get', profile_views.profile_view_id, json_renderer),

        # Used by profile.html
        Rule('/profile/<uid>/edit/', 'post', profile_views.edit_profile, json_renderer),
        Rule('/profile/<uid>/public_projects/', 'get',
             profile_views.get_public_projects, json_renderer),
        Rule('/profile/<uid>/public_components/', 'get',
             profile_views.get_public_components, json_renderer),

        Rule('/profile/<user_id>/summary/', 'get',
             profile_views.get_profile_summary, json_renderer),
        Rule('/user/<uid>/<pid>/claim/email/', 'post',
             project_views.contributor.claim_user_post, json_renderer),

        Rule(
            '/profile/export/',
            'post',
            profile_views.request_export,
            json_renderer,
        ),

        Rule(
            '/profile/deactivate/',
            'post',
            profile_views.request_deactivation,
            json_renderer,
        ),

        Rule(
            [
                '/profile/gravatar/',
                '/users/gravatar/',
                '/profile/gravatar/<size>',
                '/users/gravatar/<size>',
            ],
            'get',
            profile_views.current_user_gravatar,
            json_renderer,
        ),

        Rule(
            [
                '/profile/<uid>/gravatar/',
                '/users/<uid>/gravatar/',
                '/profile/<uid>/gravatar/<size>',
                '/users/<uid>/gravatar/<size>',
            ],
            'get',
            profile_views.get_gravatar,
            json_renderer,
        ),


        # Rules for user profile configuration
        Rule('/settings/names/', 'get', profile_views.serialize_names, json_renderer),
        Rule('/settings/names/', 'put', profile_views.unserialize_names, json_renderer),
        Rule('/settings/names/impute/', 'get', profile_views.impute_names, json_renderer),

        Rule(
            [
                '/settings/social/',
                '/settings/social/<uid>/',
            ],
            'get',
            profile_views.serialize_social,
            json_renderer,
        ),

        Rule(
            [
                '/settings/jobs/',
                '/settings/jobs/<uid>/',
            ],
            'get',
            profile_views.serialize_jobs,
            json_renderer,
        ),

        Rule(
            [
                '/settings/schools/',
                '/settings/schools/<uid>/',
            ],
            'get',
            profile_views.serialize_schools,
            json_renderer,
        ),

        Rule(
            [
                '/settings/social/',
                '/settings/social/<uid>/',
            ],
            'put',
            profile_views.unserialize_social,
            json_renderer
        ),

        Rule(
            [
                '/settings/jobs/',
                '/settings/jobs/<uid>/',
            ],
            'put',
            profile_views.unserialize_jobs,
            json_renderer
        ),

        Rule(
            [
                '/settings/schools/',
                '/settings/schools/<uid>/',
            ],
            'put',
            profile_views.unserialize_schools,
            json_renderer
        ),

    ], prefix='/api/v1',)

    ### Search ###

    # Web

    process_rules(app, [

        Rule('/search/', 'get', {}, OsfWebRenderer('search.mako')),
        Rule('/share/', 'get', {}, OsfWebRenderer('share_search.mako')),
        Rule('/share/registration/', 'get', {'register': settings.SHARE_REGISTRATION_URL}, OsfWebRenderer('share_registration.mako')),
        Rule('/share/help/', 'get', {'help': settings.SHARE_API_DOCS_URL}, OsfWebRenderer('share_api_docs.mako')),
        Rule('/share_dashboard/', 'get', {}, OsfWebRenderer('share_dashboard.mako')),
        Rule('/share/atom/', 'get', search_views.search_share_atom, xml_renderer),
        Rule('/api/v1/user/search/', 'get', search_views.search_contributor, json_renderer),

        Rule(
            '/api/v1/search/node/',
            'post',
            project_views.node.search_node,
            json_renderer,
        ),

    ])

    # API

    process_rules(app, [

        Rule(['/search/', '/search/<type>/'], ['get', 'post'], search_views.search_search, json_renderer),
        Rule('/search/projects/', 'get', search_views.search_projects_by_title, json_renderer),
        Rule('/share/search/', ['get', 'post'], search_views.search_share, json_renderer),
        Rule('/share/stats/', 'get', search_views.search_share_stats, json_renderer),
        Rule('/share/providers/', 'get', search_views.search_share_providers, json_renderer),

    ], prefix='/api/v1')

    # Project

    # Web

    process_rules(app, [

        Rule('/', 'get', website_views.index, OsfWebRenderer('index.mako')),
        Rule('/goodbye/', 'get', goodbye, OsfWebRenderer('index.mako')),

        Rule([
            '/project/<pid>/',
            '/project/<pid>/node/<nid>/',
        ], 'get', project_views.node.view_project, OsfWebRenderer('project/project.mako')),

        # Create a new subproject/component
        Rule('/project/<pid>/newnode/', 'post', project_views.node.project_new_node,
             OsfWebRenderer('', render_mako_string)),

        # # TODO: Add API endpoint for tags
        # Rule('/tags/<tag>/', 'get', project_views.tag.project_tag, OsfWebRenderer('tags.mako')),
        Rule('/api/v1/folder/<nid>', 'post', project_views.node.folder_new_post, json_renderer),
        Rule('/project/new/<pid>/beforeTemplate/', 'get',
             project_views.node.project_before_template, json_renderer),

        Rule(
            [
                '/project/<pid>/contributors/',
                '/project/<pid>/node/<nid>/contributors/',
            ],
            'get',
            project_views.node.node_contributors,
            OsfWebRenderer('project/contributors.mako'),
        ),

        Rule(
            [
                '/project/<pid>/settings/',