import html
import random
import re
from datetime import datetime

import tweepy

from cloudbot import hook
from cloudbot.bot import bot
from cloudbot.util import timeformat

TWITTER_RE = re.compile(r"(?:(?:www.twitter.com|twitter.com)/(?:[-_a-zA-Z0-9]+)/status/)([0-9]+)", re.I)


def _get_conf_value(conf, field):
    return conf['plugins']['twitter'][field]


def get_config(conn, field, default):
    """
    :type conn: cloudbot.client.Client
    :type field: str
    :type default: Any
    """
    try:
        return _get_conf_value(conn.config, field)
    except LookupError:
        try:
            return _get_conf_value(conn.bot.config, field)
        except LookupError:
            return default


def get_tweet_mode(conn, default='extended'):
    return get_config(conn, 'tweet_mode', default)


def make_api():
    consumer_key = bot.config.get_api_key("twitter_consumer_key")
    consumer_secret = bot.config.get_api_key("twitter_consumer_secret")

    oauth_token = bot.config.get_api_key("twitter_access_token")
    oauth_secret = bot.config.get_api_key("twitter_access_secret")

    if not all((consumer_key, consumer_secret, oauth_token, oauth_secret)):
        return None

    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(oauth_token, oauth_secret)

    return tweepy.API(auth)


class APIContainer:
    api = None


container = APIContainer()

IGNORE_ERRORS = [
    # User not found
    50,
    # User has been suspended
    63,
    # No status found with that ID
    144,
    # Private tweet
    179,
]


@hook.on_start
def set_api():
    container.api = make_api()


@hook.regex(TWITTER_RE)
def twitter_url(match, conn):
    # Find the tweet ID from the URL
    tweet_id = match.group(1)

    # Get the tweet using the tweepy API
    tw_api = container.api
    if tw_api is None:
        return

    try:
        tweet = tw_api.get_status(tweet_id, tweet_mode=get_tweet_mode(conn))
    except tweepy.TweepError as e:
        if e.api_code in IGNORE_ERRORS:
            return

        raise

    user = tweet.user

    return format_tweet(tweet, user)


@hook.command("twitter", "tw", "twatter")
def twitter(text, reply, conn):
    """<user> [n] - Gets last/[n]th tweet from <user>"""

    tw_api = container.api
    if tw_api is None:
        return "This command requires a twitter API key."

    tweet_mode = get_tweet_mode(conn)

    if re.match(r'^\d+$', text):
        # user is getting a tweet by id

        try:
            # get tweet by id
            tweet = tw_api.get_status(text, tweet_mode=tweet_mode)
        except tweepy.error.TweepError as e:
            if "404" in e.reason:
                reply("Could not find tweet.")
            else:
                reply("Error: {}".format(e.reason))

            raise

        user = tweet.user

    elif re.match(r'^\w{1,15}$', text) or re.match(r'^\w{1,15}\s+\d+$', text):
        # user is getting a tweet by name

        if text.find(' ') == -1:
            username = text
            tweet_number = 0
        else:
            username, tweet_number = text.split()
            tweet_number = int(tweet_number) - 1

        if tweet_number > 200:
            return "This command can only find the last \x02200\x02 tweets."

        try:
            # try to get user by username
            user = tw_api.get_user(username, tweet_mode=tweet_mode)
        except tweepy.error.TweepError as e:
            if "404" in e.reason:
                reply("Could not find user.")
            else:
                reply("Error: {}".format(e.reason))
            raise

        # get the users tweets
        user_timeline = tw_api.user_timeline(
            id=user.id, count=tweet_number + 1, tweet_mode=tweet_mode
        )

        # if the timeline is empty, return an error
        if not user_timeline:
            return "The user \x02{}\x02 has no tweets.".format(user.screen_name)

        # grab the newest tweet from the users timeline
        try:
            tweet = user_timeline[tweet_number]
        except IndexError:
            tweet_count = len(user_timeline)
            return "The user \x02{}\x02 only has \x02{}\x02 tweets.".format(user.screen_name, tweet_count)

    elif re.match(r'^#\w+$', text):
        # user is searching by hashtag
        search = tw_api.search(text, tweet_mode=tweet_mode)

        if not search:
            return "No tweets found."

        tweet = random.choice(search)
        user = tweet.user
    else:
        # ???
        return "Invalid Input"

    return format_tweet(tweet, user)


# Format the return the text of the tweet
def format_tweet(tweet, user):
    try:
        text = tweet.full_text
    except AttributeError:
        text = tweet.text

    text = " ".join(text.split())

    if user.verified:
        prefix = "\u2713"
    else:
        prefix = ""

    time = timeformat.time_since(tweet.created_at, datetime.utcnow())

    return "{}@\x02{}\x02 ({}): {} ({} ago)".format(prefix, user.screen_name, user.name, html.unescape(text), time)


@hook.command("twuser", "twinfo")
def twuser(text, reply):
    """<user> - Get info on the Twitter user <user>"""

    tw_api = container.api
    if tw_api is None:
        return

    try:
        # try to get user by username
        user = tw_api.get_user(text)
    except tweepy.error.TweepError as e:
        if "404" in e.reason:
            reply("Could not find user.")
        else:
            reply("Error: {}".format(e.reason))
        raise

    if user.verified:
        prefix = "\u2713"
    else:
        prefix = ""

    if user.location:
        loc_str = " is located in \x02{}\x02 and".format(user.location)
    else:
        loc_str = ""

    if user.description:
        desc_str = " The users description is \"{}\"".format(user.description)
    else:
        desc_str = ""

    return "{}@\x02{}\x02 ({}){} has \x02{:,}\x02 tweets and \x02{:,}\x02 followers.{}" \
           "".format(prefix, user.screen_name, user.name, loc_str, user.statuses_count, user.followers_count,
                     desc_str)