import os
from flask import current_app
from sqlite3 import Error
from ownchatbot.user_handlers import spend_points
import subprocess
import json
from ownchatbot.owncast_com import send_chat


def sort_key(item):  # Sort rewards by price
    price = item[1].get('price')
    return (price is None, price)


def check_vote(db, vote_name, user_id):  # Check if user has already voted on this vote
    try:
        cursor = db.execute(
            "SELECT voters FROM votes WHERE name = ?",
            (vote_name,)
        )
        row = cursor.fetchone()
        if row is None:
            current_app.logger.error(f'\"{vote_name}\" not found in vote table.')
            return False
        if row[0] == user_id:
            return True
    except Exception as cverror:
        current_app.logger.error(f'Couldn\'t check if {user_id} already voted on \"{vote_name}\": {cverror}')


def add_to_vote(db, vote_name, user_id):  # Add a count to a vote
    try:  # Check if vote exists in the database
        cursor = db.execute(
            "SELECT count FROM votes WHERE name = ?",
            (vote_name,)
        )
        vote = cursor.fetchone()
        if vote is None:
            current_app.logger.error(f'{vote_name} not found in vote table.')
            return False
        else:  # If vote exists, add a count
            db.execute(
                "UPDATE votes SET count = count + 1, voters = ? WHERE name = ?",
                (user_id, vote_name,)
            )
            db.commit()
            return True
    except Exception as terror:
        current_app.logger.error(f'Couldn\'t add to \"{vote_name}\" vote: {terror}')
    return False


def add_to_queue(db, user_id, reward_name):  # Add a reward to the queue
    try:
        db.execute(
            "INSERT INTO reward_queue(reward, user_id, fulfilled, refunded) VALUES(?, ?, ?, ?)",
            (reward_name, user_id, 0, 0)
            )
        db.commit()
        return True
    except Exception as qerror:
        current_app.logger.error(f'Couldn\'t add to reward \"{reward_name}\" for {user_id} queue: {qerror}')
    return False


def run_script(reward_name, script_cmd):  # Run a script form a special reward
    try:
        subprocess.check_call(script_cmd, shell=True)
    except Exception as scerror:
        current_app.logger.error(f'Couldn\'t run script \"{reward_name}\": {scerror}')
        return False
    return True


def add_to_goal(db, user_id, reward_name, points_contributed):  # Add a contribution to a goal
    try:
        cursor = db.execute(
            "SELECT progress, target FROM goals WHERE name = ?",
            (reward_name,)
        )
        row = cursor.fetchone()
        if row is None:
            current_app.logger.error(f'\"{reward_name}\" not found in goal table.')
            return False
        progress, target = row
        if progress + points_contributed > target:
            points_contributed = target - progress
            if points_contributed < 0:
                points_contributed = 0
        if spend_points(db, user_id, points_contributed):
            cursor = db.execute(
                "UPDATE goals SET progress = ? WHERE name = ?",
                (progress + points_contributed, reward_name)
            )
            db.commit()
            return True
    except Exception as gerror:
        current_app.logger.error(f'Couldn\'t update goal: {gerror}')
        return False


def goal_left(db, reward_name):
    try:
        cursor = db.execute(
            "SELECT progress, target FROM goals WHERE name = ?",
            (reward_name,)
        )
        row = cursor.fetchone()
        if row is None:
            current_app.logger.error(f'{reward_name} not found in Goal table.')
        else:
            progress, target = row
            left = target - progress
        return left
    except Exception as glerror:
        current_app.logger.error(f'Couldn\'t check progress for \"{reward_name}\" goal: {glerror}')


def goal_reached(db, reward_name):  # Set a goal as completed
    try:
        cursor = db.execute(
            "SELECT complete FROM goals WHERE name = ?",
            (reward_name,)
        )
        row = cursor.fetchone()
        if row is None:
            current_app.logger.error(f'{reward_name} not found in goal table.')
        else:
            return row[0]
    except Exception as grerror:
        current_app.logger.error(f'Couldn\'t check if goal was met: {grerror}')
        return False


def was_milestone_reached(db, reward_name):  # Check if a milestone was reached
    try:
        all_rewards = current_app.config['REWARDS']  # Get milestone numbers from REWARDS
        milestones_info = all_rewards[reward_name]['milestones']

        ms1_goal = ms2_goal = ms3_goal = float('inf')  # To avoid referencing unassigned variables, in case there aren't any milestones set
        ms1_reward = ms2_reward = ms3_reward = None  # Initialize to None

        if len(milestones_info['milestone1']) > 1 and milestones_info['milestone1'][1]:  # Check that there is a value for the milestone
            ms1_goal = milestones_info['milestone1'][1]
            ms1_reward = f'🚩 \"{milestones_info["milestone1"][0]}\"'

        if len(milestones_info['milestone2']) > 1 and milestones_info['milestone2'][1]:
            ms2_goal = milestones_info['milestone2'][1]
            ms2_reward = f'🚩🚩 \"{milestones_info["milestone2"][0]}\"'

        if len(milestones_info['milestone3']) > 1 and milestones_info['milestone3'][1]:
            ms3_goal = milestones_info['milestone3'][1]
            ms3_reward = f'🚩🚩🚩 \"{milestones_info["milestone3"][0]}\"'

        cursor = db.execute(
            "SELECT progress, milestones FROM goals WHERE name = ?",
            (reward_name,)
        )  # Get progress and milestones info from database
        row = cursor.fetchone()

        if row is None:
            current_app.logger.error(f'{reward_name} not found in Goal table.')
        else:
            progress = int(row['progress'])
            milestones = int(row['milestones'])

            if progress >= ms1_goal and progress < ms2_goal:
                new_milestones = 1
                ms_reward = ms1_reward
            elif progress >= ms2_goal and progress < ms3_goal:
                new_milestones = 2
                ms_reward = ms2_reward
            elif progress >= ms3_goal:
                new_milestones = 3
                ms_reward = ms3_reward
            else:
                new_milestones = 0

            if new_milestones > milestones:  # If we're passing a milestone, get the reward
                cursor = db.execute(
                    "UPDATE goals SET milestones = ? WHERE name = ?",
                    (new_milestones, reward_name)
                )
                db.commit()
                current_app.logger.info(f'new_milestone: {new_milestones}')  # TESTING
                if new_milestones < 3:  # If there is another milestone
                    next_milestone = new_milestones + 1  # Get the number of the next milestone
                    next_milestone = f'milestone{next_milestone}'  # Set up the dict entry name
                    if next_milestone:
                        next_milestone = milestones_info[next_milestone][0]  # Get the name of the next milestone
                else:
                    next_milestone = "Done"
                    current_app.logger.info(f'The next milestone is \"{next_milestone}\"')  # TESTING
                return ms_reward, next_milestone
            else:
                return False
        return False
    except Exception as wmrerror:
        current_app.logger.error(f'Couldn\'t check if a milestone was reached: {wmrerror}')
        return False


def was_goal_reached(db, reward_name):  # Check if a goal was reached
    try:
        cursor = db.execute(
            "SELECT progress, target FROM goals WHERE name = ?",
            (reward_name,)
        )
        row = cursor.fetchone()
        if row is None:
            current_app.logger.error(f'{reward_name} not found in Goal table.')
        else:
            progress, target = row
            if progress == target:
                cursor = db.execute(
                    "UPDATE goals SET complete = TRUE WHERE name = ?",
                    (reward_name,)
                )
                db.commit()
                return True
        return False
    except Exception as wgrerror:
        current_app.logger.error(f'Couldn\'t mark goal met: {wgrerror}')
        return False


def all_votes(db):  # Get all the votes
    try:
        cursor = db.execute(
            "SELECT votes.name, votes.count, votes.info FROM votes"
        )
        return cursor.fetchall()
    except Exception as aterror:
        current_app.logger.error(f'Couldn\'t select all votes: {aterror}')


def refund_reward(db, reward_id):  # Refund a user for a particular reward
    reward_id = reward_id
    try:
        cursor = db.execute(
            "UPDATE reward_queue SET refunded = 1 WHERE id = ?",
            (reward_id,)
        )
        db.commit()
        current_app.logger.info(f'Set {reward_id} as refunded in the database')
        return True
    except Exception as rferror:
        current_app.logger.error(f'Couldn\'t refund reward id {reward_id}: {rferror}')
        return False


def fulfill_reward(db, reward_id):  # Mark a reward as fulfilled in the database
    reward_id = reward_id
    try:
        cursor = db.execute(
            "UPDATE reward_queue SET fulfilled = 1 WHERE id = ?",
            (reward_id,)
        )
        db.commit()
        current_app.logger.info(f'{reward_id} marked fulfilled.')
        return True
    except Exception as frerror:
        current_app.logger.error(f'Couldn\'t  fulfill reward id {reward_id}: {frerror}')
        return False


def all_active_votes(db):  # Get only active votes
    votes = all_votes(db)
    all_active_votes = []
    try:
        for name, count, info in votes:
            if is_reward_active(name):
                all_active_votes.append((name, count, info))
        return all_active_votes
    except Exception as aaverror:
        current_app.logger.error(f'General exception: {aaverror}')


def all_goals(db):  # Get all the goals
    try:
        cursor = db.execute(
            "SELECT name, progress, target, info FROM goals"
        )
        return cursor.fetchall()
    except Exception as agerror:
        current_app.logger.error(f'Couldn\'t select all goals: {agerror}')


def all_active_goals(db):  # Get only active goals
    goals = all_goals(db)
    all_active_goals = []
    try:
        for name, progress, target, info in goals:
            if is_reward_active(name):
                all_active_goals.append((name, progress, target, info))
        return all_active_goals
    except Exception as aagerror:
        current_app.logger.error(f'General exception: {aagerror}')


def all_active_rewards():  # Get only active rewards
    rewards = current_app.config['REWARDS']
    all_active_rewards = {}
    for reward_name, reward_dict in rewards.items():
        if reward_dict.get('categories'):  # If reward has empty categories list
            for category in reward_dict['categories']:  # Compare each category to ACTIVE_CAT list
                if category in current_app.config['ACTIVE_CAT']:
                    all_active_rewards[reward_name] = reward_dict
                    break
    return all_active_rewards


def all_active_redeems():
    rewards = current_app.config['REWARDS']
    just_redeems = []
    for reward_name, details in rewards.items():
        if details.get("type") not in ["goal", "vote"]:
            just_redeems.append(reward_name)
    return just_redeems


def save_alerts(alerts_dict):  # Write alerts to alerts.py
    alerts_dict = json.dumps(alerts_dict, indent=4)
    alerts_file = os.path.join(current_app.instance_path, 'alerts.py')
    new_alerts = f"ALERTS = {alerts_dict}"

    try:
        with open(alerts_file, 'w') as af:
            af.write(new_alerts)
        af.close
        current_app.logger.info('Saved new alerts.py.')
    except Exception as saerror:
        current_app.logger.error(f'Couldn\'t save alerts.py: {saerror}')
        return False

    current_app.config.from_pyfile('alerts.py', silent=True)  # Reread alerts.py into the app
    return True


def del_alert_file(alert_file):
    filepath = os.path.join(current_app.config['ASSETS_FOLDER'], alert_file)
    try:
        os.remove(filepath)
        current_app.logger.info(f"Successfully removed \"{alert_file}\" from alerts folder.")
        return True
    except FileNotFoundError:
        current_app.logger.error(f"Couldn't delet \"{alert_file}\": File not found.")
        return False
    except PermissionError:
        current_app.logger.error(f"No permission to delete file: \"{alert_file}\"")
        return False
    except Exception as daferror:
        current_app.logger.error(f"An error occurred: {daferror}")
        return False


def save_rewards(reward_info):  # Write rewards to rewards.py
    sorted_rewards = dict(sorted(reward_info.items(), key=sort_key))

    new_rewards = json.dumps(sorted_rewards, indent=4)
    rewards_file = os.path.join(current_app.instance_path, 'rewards.py')

    try:
        with open(rewards_file, 'w') as rf:
            rf.write(f'REWARDS = {new_rewards}')
            rf.close()
    except Exception as srerror:
        current_app.logger.error(f'Couldn\'t save rewards.py: {srerror}')
        return False

    return True


def save_config(config_dict):  # Write settings to config.py
    settings_file = os.path.join(current_app.instance_path, 'config.py')
    secret_key = current_app.config['SECRET_KEY']
    new_settings = f"# Owncast stuff. Needed to interact with your Owncast instance\n\
ACCESS_ID = '{config_dict['ACCESS_ID']}'\n\
ACCESS_TOKEN = '{config_dict['ACCESS_TOKEN']}'\n\
OWNCAST_URL = '{config_dict['OWNCAST_URL']}'\n\
\n\
# OwnchatBot Configuration \n\
OCB_URL = '{config_dict['OCB_URL']}'\n\
SECRET_KEY = '{secret_key}'  # Needed for internal Flask stuff. DO NOT DELETE.\n\
POINTS_INTERVAL = {config_dict['POINTS_INTERVAL']}\n\
POINTS_AWARD = {config_dict['POINTS_AWARD']}\n\
PREFIX = '{config_dict['PREFIX']}'"

    try:
        with open(settings_file, 'w') as cf:
            cf.write(new_settings)
        cf.close
    except Exception as scerror:
        current_app.logger.error(f'Couldn\'t save config.py: {scerror}')
        return False

    current_app.config.from_pyfile('config.py', silent=True)  # Reread config.py into the app
    return True


def reread_categories():  # Read _CAT varaibles and write to categories.py
    categories_file = os.path.join(current_app.instance_path, 'categories.py')
    active_categories = current_app.config['ACTIVE_CAT']
    inactive_categories = current_app.config['INACTIVE_CAT']
    try:
        with open(categories_file, 'r', encoding='utf-8') as f:  # Read categories.py, and set up lines to change
            category_data = f.readlines()
            category_data[0] = f'ACTIVE_CAT = {active_categories}\n'
            category_data[1] = f'INACTIVE_CAT = {inactive_categories}\n'
        f.close

        with open(categories_file, 'w', encoding='utf-8') as f:  # Write changes to categories.py
            f.writelines(category_data)
        f.close

        current_app.config.from_pyfile('categories.py', silent=True)  # Reread categories into the app
    except Exception as rcerror:
        current_app.logger.error(f'Couldn\'t reread categories: {rcerror}')


def activate_category(category):  # Move an item from the ACTIVE_CAT list to the INACTIVE_CAT list
    try:
        categories_file = os.path.join(current_app.instance_path, 'categories.py')
        active_categories = current_app.config['ACTIVE_CAT']
        inactive_categories = current_app.config['INACTIVE_CAT']
        active_categories.append(category)  # Add to ACTIVE_CAT
        inactive_categories.remove(category)  # Remove from INACTIVE_CAT
        reread_categories()
    except Exception as acerror:
        current_app.logger.error(f'Couldn\'t activate {category}: {acerror}')


def deactivate_category(category):  # Move an item from the INACTIVE_CAT list to the ACTIVE_CAT list
    try:
        categories_file = os.path.join(current_app.instance_path, 'categories.py')
        active_categories = current_app.config['ACTIVE_CAT']
        inactive_categories = current_app.config['INACTIVE_CAT']
        active_categories.remove(category)  # Remove from ACTIVE_CAT
        inactive_categories.append(category)  # Add to INACTIVE_CAT
        reread_categories()
    except Exception as dcerror:
        current_app.logger.error(f'Couldn\'t deactivate {category}: {dcerror}')


def get_queue(db):  # Get the reward queue and the username
    try:
        cursor = db.execute(
            """SELECT reward_queue.id, reward_queue.created, reward_queue.reward, reward_queue.user_id, reward_queue.fulfilled, reward_queue.refunded, points.name
            FROM reward_queue
            INNER JOIN points
            on reward_queue.user_id = points.id"""
        )
        return cursor.fetchall()
    except Exception as gqerror:
        current_app.logger.error(f'Couldn\'t get queue: {gqerror}')


def is_reward_active(reward_name):  # Check if reward is active
    active_categories = current_app.config['ACTIVE_CAT']
    reward_dict = current_app.config['REWARDS'].get(reward_name, None)
    try:
        if reward_dict:
            if 'categories' in reward_dict:  # Is there a categories key at all?
                for category in reward_dict['categories']:  # Cycle through categories and compare to active_categories
                    if category in active_categories:
                        return True
                return False
            elif reward_dict['categories'] == []:  # If categories key is there but empty, return False
                return False
            else:
                return True
        return None
    except Exception as iraerror:
        current_app.logger.error(f'Couldn\'t check if {reward_name} is active: {iraerror}')
