from flask import Flask, request, json, Blueprint, current_app, render_template, jsonify, request, g
from ownchatbot.db import get_db, clear_fulfilled_rewards
from ownchatbot.owncast_com import send_chat, send_private_chat
from ownchatbot.user_handlers import add_user_to_points, change_name, get_users_points, remove_duplicates, get_email_code, set_email_code, award_chat_points, user_in_points, get_all_users_with_user_id
from ownchatbot.bot_messages import do_reward, help_message, porps
from ownchatbot.reward_handlers import all_active_goals, all_active_votes, all_active_rewards, save_alerts
from ownchatbot.donation_handlers import accept_donation, accept_kofi_sub
import json
import random
import hmac
import hashlib


ocb = Blueprint('webhooks', __name__)


def format(rawjson):  # Make data legible
    formatted_data = json.dumps(rawjson, indent=4)
    return formatted_data


@ocb.route('/ocbHook', methods=['POST'])
def chat_hook():
    prefix = current_app.config['PREFIX']
    data = request.json
    db = get_db()
    if data['type'] in ['CHAT', 'NAME_CHANGED', 'USER_JOINED']:  # Check if the viewer is in the chatbot database
        user_id = data['eventData']['user']['id']
        authed = data['eventData']['user']['authenticated']
        display_name = data['eventData']['user']['displayName']
        if add_user_to_points(db, user_id, display_name, authed):
            current_app.logger.debug(f'Added/updated {user_id} database.')
        current_app.logger.debug(f'{display_name}/{user_id}: {data["eventData"]}')  # Log all chat messages

    if data['type'] == 'STREAM_STARTED':
        current_app.logger.info('Starting a new stream.')
        if clear_fulfilled_rewards():
            current_app.logger.info('Cleared fulfilled rewards.')

    if data['type'] == 'USER_JOINED':  # Do username house cleaning when a viewer joins
        if data['eventData']['user']['authenticated']:
            remove_duplicates(db, user_id, display_name)
    elif data['type'] == 'FEDIVERSE_ENGAGEMENT_FOLLOW':
        alerts_dict = current_app.config['ALERTS']
        data = request.json
        current_app.logger.debug(f'\n\n_______________\n/followHook triggered!\n_______________')
        alerts_dict['follower'] = data['eventData']['name']
        save_alerts(alerts_dict)
        return jsonify({'status': 'success'}), 200
    elif data['type'] == 'NAME_CHANGE':
        user_id = data['eventData']['user']['id']
        new_name = data['eventData']['newName']
        change_name(db, user_id, new_name)
        if data['eventData']['user']['authenticated']:
            remove_duplicates(db, user_id, new_name)
    elif data['type'] == 'CHAT':  # If a chat message, sort out what command it is
        user_id = data['eventData']['user']['id']
        display_name = data['eventData']['user']['displayName']
        current_app.logger.info(f'{display_name}/{user_id}: {data["eventData"]["rawBody"]}')  # Log all chat messages

        lowercase_msg = data['eventData']['rawBody'].lower()  # Convert body to lower case to match reward case
        if lowercase_msg.startswith(f'{prefix}help'):  # Send the help message
            help_message(user_id)

        elif lowercase_msg.startswith(f'{prefix}points'):  # Get the viewer's current points
            points = get_users_points(db, user_id)
            if points is None:
                send_private_chat(user_id, f'{display_name}, couldn\'t get your points, for some highly technical reason.')
            else:
                send_private_chat(user_id, f'{display_name}, you have {porps(points)}.')

        elif lowercase_msg.startswith(f'{prefix}reg_mail'):  # Generate a code to verify users account for email registration
            if current_app.config['KOFI_SETTINGS']['integration'] or current_app.config['GB_SETTINGS']['integration']:
                mail_reg_code = get_email_code(db, user_id)
                if mail_reg_code:  # If the viewer already has a code waiting
                    send_private_chat(user_id, f'{display_name}, your code is {mail_reg_code}. Enter it into the form on the Stream Rewards Info page, with your email address, to enable donation perks!')
                else:  # if not
                    mail_reg_code = random.randint(100000, 999999)
                    if set_email_code(db, user_id, mail_reg_code):
                        send_private_chat(user_id, f'{display_name}, your code is {mail_reg_code}. Enter it into the form on the Stream Rewards Info page, with your email address, to enable donation perks!')
            else:
                send_chat(f'{display_name}, donation integration is not enabled on this stream.')

        elif lowercase_msg.startswith(f'{prefix}rewards'):  # Send rewards list
            if current_app.config['REWARDS']:
                rewards_msg = f'Currently active rewards:'
                for reward, details in current_app.config['REWARDS'].items():
                    if details.get('categories'):
                        if not (set(details['categories']) & set(current_app.config['ACTIVE_CAT'])):  # If there are no common categories, continue
                            continue
                    if 'type' in details and details['type'] == 'goal':
                        rewards_msg = f'{rewards_msg}<br>* {prefix}{reward} goal at {details["target"]} contributed {porps(details["target"])}.'
                    elif 'type' in details and details['type'] == 'vote':
                        rewards_msg = f'{rewards_msg}<br>* {prefix}{reward} vote for {porps(details["price"])}.'
                    else:
                        rewards_msg = f'{rewards_msg}<br>* {prefix}{reward} for {porps(details["price"])}.'
                    if 'info' in details:
                        rewards_msg = f'{rewards_msg}<br>{details["info"]}'
                    else:
                        rewards_msg = f'{rewards_msg}'
            else:
                rewards_msg = 'There are currently no active rewards.'
            send_private_chat(user_id, rewards_msg)

        elif lowercase_msg.startswith(f'{prefix}'):  # Send to handle rewards
            do_reward(lowercase_msg, user_id)
    return data


@ocb.route('/kofiHook', methods=["POST"])
def kofi_hook():
    current_app.logger.info(f'----------------------------------------------------------------------------')
    current_app.logger.info(f'Kofi request')
    if request.content_type == 'application/x-www-form-urlencoded':
        raw_data = request.form.get('data')  # Get the kofi data
        if current_app.config['KOFI_SETTINGS']['integration']:
            if raw_data:
                raw_data = json.loads(raw_data)
                is_authed = raw_data['verification_token']
                if is_authed == current_app.config['KOFI_SETTINGS']['token']:
                    type = raw_data['type']
                    is_public = raw_data['is_public']
                    new_sub = raw_data['is_first_subscription_payment']
                    message = raw_data['message']
                    shop_items = raw_data['shop_items']
                    from_name = raw_data['from_name']
                    email = raw_data['email']
                    amount = raw_data['amount']
                    sub_payment = raw_data['is_subscription_payment']
                    first_sub = raw_data['is_first_subscription_payment']
                    tier_name = raw_data['tier_name']
                    if type == 'Shop Order':
                        current_app.logger.info(f'{from_name} purchased {format(shop_items)}\nMessage: {message}\n')
                    if type == 'Donation':
                        donation_info = [is_public, from_name, email, amount, message]
                        donation_points = current_app.config['KOFI_SETTINGS']['donation_points']
                        if accept_donation(donation_info, donation_points, 'Kofi'):
                            current_app.logger.info('Donation processed.')
                    if type == 'Subscription':
                        if current_app.config['KOFI_SETTINGS']['subs']:  # Check that subscriptions are enabled
                            if first_sub:
                                if tier_name:
                                    current_app.logger.info(f'{from_name} <{email}> subscribed as a {tier_name} tier member.')
                                else:
                                    current_app.logger.info(f'{from_name} <{email}> subscribed.')
                            else:
                                if tier_name:
                                    current_app.logger.info(f'{from_name} <{email}> renewed their {tier_name} tier membership.')
                                else:
                                    current_app.logger.info(f'{from_name} <{email}> renewed their membership.')

                            sub_info = [is_public, from_name, email, amount, message, first_sub, tier_name]
                            sub_points = current_app.config['KOFI_SETTINGS']['sub_points']
                            if accept_kofi_sub(sub_info, sub_points):
                                current_app.logger.info('Subscription processed.')
                        else:
                            current_app.logger.info(f'Kofi membership received, but subscriptions are not enabled. Doing nothing.')
                    return jsonify({'status': 'success'}), 200
                else:
                    current_app.logger.info(f'Token invalid. Rejecting.')
                    return jsonify({'status': 'unauthorized'}), 401
            else:
                return jsonify({'status': 'Failed. No data'}), 400
            return jsonify({'status': 'success'}), 200
        else:
            current_app.logger.error(f'Kofi donation recieved, but Kofi integration is turned off. Doing nothing.')
            return jsonify({'status': 'Failed. Not currently accepting Kofi donations.'}), 400
    else:
        return jsonify({'status': 'Failed. Invalid content type'}), 400


def sign_payload(payload, secret):  # For TESTING purposes
    test_payload = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    current_app.logger.info(f'\n\nTest encoded payload output: {test_payload}\n\n')
    return test_payload


# Not currently in use by GB, but it was. Maybe in the future?
# Leaving it here just in case.
def verify_gbhook_signature(payload, signature, secret):
    current_app.logger.info(f'\n\nRecieved Signature: {signature}\n\n')
    expected_signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    do_sig_check = hmac.compare_digest(signature, expected_signature)
    current_app.logger.debug(f'\n\nExpected Signature: {expected_signature}\nResult: {do_sig_check}\n\n')
    return do_sig_check


@ocb.route('/gbHook', methods=['POST'])
def gb_hook():
    current_app.logger.info(f'----------------------------------------------------------------------------')
    current_app.logger.info(f'GiveButter request')

    headers = request.headers
    current_app.logger.debug('Headers:')
    for header, value in headers.items():
        current_app.logger.debug(f'> {header}: {value}')

    signature = request.headers.get('Signature')
    gb_secret = current_app.config['GB_SETTINGS']['secret']

    raw_data = request.get_data(as_text=True)

    current_app.logger.debug(f'\n\n{raw_data}\n\n')

    event = request.json

    if signature == gb_secret:
        if current_app.config['GB_SETTINGS']['integration']:
            try:
                event_type = event['event']
                if event_type == 'transaction.succeeded':
                    data = event['data']
                    custom_fields = data["custom_fields"]
                    if custom_fields:
                        for field in custom_fields:  # Get anonymous donation custom field, if it's there'
                            if field['title'].lower() == 'anonymous':
                                if field['value'].lower() == "yes":
                                    is_public = False
                                    current_app.logger.info(f'Donation is anonymous.')
                                else:
                                    is_public = True
                                    current_app.logger.info(f'Donation is not anonymous.')
                            else:  # If it's not there, set to anonymous
                                is_public = False
                                current_app.logger.info(f'No anoymous donation custom field found. Assuming anonymous')
                    else:  # No custom fields found, set to anonymous
                        is_public = False
                        current_app.logger.info(f'No custom fields found. Assuming anonymous')

                    from_name = f'{data["first_name"]} {data["last_name"]}'
                    email = data['email']
                    amount = data['amount']
                    current_app.logger.debug(f'\nFrom: {from_name}\nEmail: {email}\nAmount: {amount}\nAnonymous: {is_public}\n\n')
                    donation_info = [is_public, from_name, email, amount, '']
                    donation_points = current_app.config['GB_SETTINGS']['donation_points']
                    points_for_donations = current_app.config['GB_SETTINGS']['donations']
                    if points_for_donations:  # Are we giving points for donations?
                        if accept_donation(donation_info, donation_points, 'GiveButter'):
                            current_app.logger.info(f'Donation processed.')
                    else:
                        current_app.logger.info(f'Points for donations is disabled. Doing nothing.')
                else:
                    current_app.logger.info(f'Unhandled event type: {event_type}')
            except Exception as pgberror:
                current_app.logger.error(f'General exception processing gbhook: {pgberror}')

        else:
            current_app.logger.error(f'GiveButter donation recieved, but GiveButter integration is turned off. Doing nothing.')
            return jsonify({'status': 'Failed. Not currently accepting GiveButter donations.'}), 400

        return jsonify({'status': 'Success'}), 200  # If signature matched

    else:
        return jsonify({'status': 'Signature invalid'}), 401  # If signature didn't match


@ocb.route('/checkFollows')  # Polled by follower.html template to check for new followers
def check_follows():
    alerts_dict = current_app.config['ALERTS']
    follower = {'name': alerts_dict['follower']}
    if follower['name']:
        current_app.logger.info(f'New follower: \"{follower["name"]}\"')
        alerts_dict['follower'] = ''
        save_alerts(alerts_dict)

        return jsonify(follower)
    else:
        current_app.logger.debug(f'No new followers')
    return jsonify(None)


@ocb.route('/checkGoals')  # Polled by rgoal.html template to check for if a goal has been passed
def check_goals():
    alerts_dict = current_app.config['ALERTS']
    rgoals = {'name': alerts_dict['g_name']}
    if rgoals['name']:
        current_app.logger.info(rgoals)
        alerts_dict['g_name'] = ''
        alerts_dict['g_reward'] = ''
        save_alerts(alerts_dict)
        return jsonify(rgoals)
    else:
        current_app.logger.debug(f'No new goals reached')
    return jsonify(None)


@ocb.route('/checkMilestones')  # Polled by rmilestone.html template to check if a milestone has been passed
def check_milestones():
    alerts_dict = current_app.config['ALERTS']
    rmilestones = {'name': alerts_dict['m_name']}
    if rmilestones['name']:
        current_app.logger.info(rmilestones)
        alerts_dict['m_name'] = ''
        alerts_dict['m_reward'] = ''
        save_alerts(alerts_dict)
        return jsonify(rmilestones)
    else:
        current_app.logger.debug(f'No new milestones passed')
    return jsonify(None)


@ocb.route('/checkGBs')  # Polled by rgbdonation.html template to check for new givebutter donations
def check_gbs():
    alerts_dict = current_app.config['ALERTS']
    rgbs = {'name': alerts_dict['gb_name']}
    if rgbs['name']:
        current_app.logger.info(rgbs)
        alerts_dict['gb_name'] = ''
        save_alerts(alerts_dict)
        return jsonify(rgbs)
    else:
        current_app.logger.debug(f'No GiveButter donation')
    return jsonify(None)
