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


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 Error as cverror:
        current_app.logger.error(f'Couldn\'t check if {user_id} already voted on \"{vote_name}\": {cverror.args[0]}')


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 Error as terror:
        current_app.logger.error(f'Couldn\'t add to \"{vote_name}\" vote: {terror.args[0]}')
    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 Error as qerror:
        current_app.logger.error(f'Couldn\'t add to reward \"{reward_name}\" for {user_id} queue: {qerror.args[0]}')
    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.args[0]}')
        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 Error as gerror:
        current_app.logger.error(f'Couldn\'t update goal: {gerror.args[0]}')
        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 Error as glerror:
        current_app.logger.error(f'Couldn\'t check progress for \"{reward_name}\" goal: {glerror.args[0]}')


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 Error as grerror:
        current_app.logger.error(f'Couldn\'t check if goal was met: {grerror.args[0]}')
        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()
                return ms_reward
            else:
                return False
        return False
    except Error as wmrerror:
        current_app.logger.error(f'Couldn\'t check if a milestone was reached: {wmrerror.args[0]}')
        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 Error as wgrerror:
        current_app.logger.error(f'Couldn\'t mark goal met: {wgrerror.args[0]}')
        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 Error as aterror:
        current_app.logger.error(f'Couldn\'t select all votes: {aterror.args[0]}')


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()
    except Error as rferror:
        current_app.logger.error(f'Couldn\'t refund reward id {reward_id}: {rferror.args[0]}')
        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()
    except Error as frerror:
        current_app.logger.error(f'Couldn\'t  fulfill reward id {reward_id}: {frerror.args[0]}')
        return False


def all_active_votes(db):  # Get only active votes
    votes = all_votes(db)
    all_active_votes = []
    for name, count, info in votes:
        if is_reward_active(name):
            all_active_votes.append((name, count, info))
    return all_active_votes


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


def all_active_goals(db):  # Get only active goals
    goals = all_goals(db)
    all_active_goals = []
    for name, progress, target, info in goals:
        if is_reward_active(name):
            all_active_goals.append((name, progress, target, info))
    return all_active_goals


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 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 f:
            f.write(f'REWARDS = {new_rewards}')
            f.close()
    except Exception as srerror:
        current_app.logger.error(f'Couldn\'t save rewards.py: {srerror.args[0]}')
        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\
SECRET_KEY = '{secret_key}'  # Needed for internal Flask stuff. DO NOT DELETE.\n\
POINTS_INTERVAL = {config_dict['POINTS_INTERVAL']}  # How long, in seconds, between points awards\n\
POINTS_AWARD = {config_dict['POINTS_AWARD']}  # How many points awarded each interval?\n\
GUNICORN = {config_dict['GUNICORN']}  # Integrate OwnchatBot logging into Gunicorn\n\
PREFIX = '{config_dict['PREFIX']}'  # Preceeds commands, so OwnchatBot knows what is a command\n\
KOFI_TOKEN = '{config_dict['KOFI_TOKEN']}'  # Needed to validate Ko-fi with OCB webhook. Get from Ko-fi Settings -> More -> API -> Webhooks -> Advanced - Verification Token.\n\
KOFI_INTEGRATION = {config_dict['KOFI_INTEGRATION']}  # Integrate OwnchatBot with Ko-fi"

    try:
        with open(settings_file, 'w') as f:
            f.write(new_settings)
        f.close
    except Exception as scerror:
        current_app.logger.error(f'Couldn\'t save config.py: {saerror.args[0]}')
        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 Error as rcerror:
        current_app.logger.error(f'Couldn\'t reread categories: {rcerror.args[0]}')


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 Error as acerror:
        current_app.logger.error(f'Couldn\'t activate {category}: {acerror.args[0]}')


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 Error as dcerror:
        current_app.logger.error(f'Couldn\'t deactivate {category}: {dcerror.args[0]}')


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 Error as gqerror:
        current_app.logger.error(f'Couldn\'t get queue: {gqerror.args[0]}')


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 Error as iraerror:
        current_app.logger.error(f'Couldn\'t check if {reward_name} is active: {iraerror.args[0]}')
