from flask import Flask, Blueprint, render_template, request, Markup
import configparser
from twall.web import twitter_adapter
import os


__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))

blueprint = Blueprint('app', __name__)


def create_app(auth_filename, debug=False):
    """
    Launches the Flask web application running on localhost and using the Flask default port.

    :param auth_filename: The location of the file containing Twitter OAuth credentials for the application to use.
    :param debug: If set to ``True``, will launch Flask in debug mode.
    :return: The Flask web application object.
    """
    config = configparser.ConfigParser()
    if not os.path.exists(auth_filename):
        raise FileNotFoundError('Authentication file doesn\'t exist. I don\'t have Twitter OAuth data.')

    config.read([auth_filename, os.path.join(__location__, '../settings.cfg')])

    return create_app_with_config(config, debug)


def create_app_with_config(config, debug=False, twitter_session=None):
    """
    Launches the Flask web application running on localhost and using the Flask default port. This method doesn't load
    configuration data from files. Instead, it expects the loaded config to be passed as an argument. For testing
    purposes, an initialized Twitter API session may be provided. If it's omitted, the Twitter adapter will create its
    own session.

    :param config: A :mod:`configparser` instance containing the application's loaded config data.
    :param debug: If set to ``True``, will launch Flask in debug mode.
    :param twitter_session: A :mod:`requests` session initialized to use the correct Bearer token with the Twitter API (optional).
    :return: The Flask web application object.
    """
    app = Flask(__name__)
    app.debug = debug
    twitter_adapter.init(config, twitter_session)

    app.register_blueprint(blueprint)

    app.jinja_env.filters['render_image'] = render_media
    app.jinja_env.filters['render_tweet'] = render_tweet

    return app


@blueprint.route('/')
def _wall():
    ctx = {}
    query = request.args.get('q')
    if query is not None:
        result_type = request.args.get('mode').lower()
        ctx['mode'] = result_type
        ctx['query'] = query
        ctx['tweets'] = twitter_adapter.get_tweets(query, result_type=result_type)
    else:
        ctx['mode'] = 'recent'

    return render_template('wall.html', **ctx)


def _render_hashtag(hashtag, text):
    return '<a href="https://twitter.com/hashtag/{}" target="_blank">{}</a>'.format(hashtag, text)


def _render_mention(screen_name, text):
    return '<a href="https://twitter.com/{}" target="_blank">{}</a>'.format(screen_name, text)


def _render_link(url, text):
    return '<a href="{}" target="_blank">{}</a>'.format(url, text)


def render_media(media):
    """
    Renders a "media" entity. Currently, the only media type on Twitter is an image, so this method effectively
    produces an ``<img>`` tag

    :param media: The media object, extracted from a tweet returned from the Twitter API.
    :return: A :class:`Markup` instance containing the rendered HTML.
    """
    width = min(400, media['sizes']['medium']['w'])
    height = width * (media['sizes']['medium']['h'] / media['sizes']['medium']['w'])
    return Markup('<img src="{}" width="{}" height="{}"/>'.format(media['media_url'], width, height))


def render_tweet(tweet):
    """
    Enriches a tweet's text with mentions, hashtags and links.

    :param tweet: A tweet, as returned by the Twitter Search API
    :return: A :class:`Markup` instance containing the rendered HTML.
    """
    new_text = tweet['text']

    for mention in tweet['entities']['user_mentions']:
        new_text = _enrich_text(tweet['text'], new_text, mention['indices'], mention['screen_name'], _render_mention)

    for hashtag in tweet['entities']['hashtags']:
        new_text = _enrich_text(tweet['text'], new_text, hashtag['indices'], hashtag['text'], _render_hashtag)

    for link in tweet['entities']['urls']:
        new_text = _enrich_text(tweet['text'], new_text, link['indices'], link['expanded_url'], _render_link)

    return Markup(new_text)


def _enrich_text(original_text, constructed_text, indices, entity_info, render_method):
    original_text_chunk = original_text[indices[0]:indices[1]]
    new_text = constructed_text.replace(original_text_chunk, render_method(entity_info,
                                                                           original_text_chunk))
    return new_text
