import functools
import time
import base64
import sys
import gspread
import os
import datetime
from dotenv import load_dotenv
from difflib import SequenceMatcher

DEBUG = True if os.getenv('DEBUG', False) else False

def colorize(text: str, color: str = 'bright_black'):
    color_codes = {
        'black': '30',
        'red': '31',
        'green': '32',
        'yellow': '33',
        'blue': '34',
        'magenta': '35',
        'cyan': '36',
        'white': '37',
        'bright_black': '90',
        'bright_red': '91',
        'bright_green': '92',
        'bright_yellow': '93',
        'bright_blue': '94',
        'bright_magenta': '95',
        'bright_cyan': '96',
        'bright_white': '97',
        'reset': '0'
    }
    color_code = color_codes.get(color.lower(), '90')  # default to '90' if color not found
    return f'\033[{color_code}m{text}\033[0m'

def debug_print(text: str, color: str = 'bright_black'):
    if DEBUG:
        output_text = f'DEBUG: {text}'
        print(colorize(output_text, color))

def print_color(text: str, color: str = 'bright_black'):
    print(colorize(text, color))

def timing_decorator(func):
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        start_time = time.time()
        result = await func(*args, **kwargs)
        end_time = time.time()
        debug_print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds")
        if result.__class__ == dict:
            result['timing'] = round(end_time - start_time, 4)
        return result
    return wrapper

def create_run_name(country: str):
    return f'{country}_{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'

def calculate_jaccard_similarity(drugs1, drugs2):
    drugs1_set = set(drug['registered_name'] for drug in drugs1)
    drugs2_set = set(drug['registered_name'] for drug in drugs2)

    active_ingredient1_set = set(drug['active_ingredient'] for drug in drugs1)
    active_ingredient2_set = set(drug['active_ingredient'] for drug in drugs2)

    intersection_drugs = drugs1_set.intersection(drugs2_set)
    union_drugs = drugs1_set.union(drugs2_set)
    if DEBUG:
        print(f'active_ingredient1_set: {active_ingredient1_set}')
        print(f'active_ingredient2_set: {active_ingredient2_set}')
    intersection_active_ingredient = active_ingredient1_set.intersection(active_ingredient2_set)
    union_active_ingredient = active_ingredient1_set.union(active_ingredient2_set)

    if len(union_drugs) == 0:
        jaccard_drugs = 0.0
    else:
        jaccard_drugs = round(len(intersection_drugs) / len(union_drugs), 2)

    if len(union_active_ingredient) == 0:
        jaccard_active_ingredient = 0.0
    else:
        jaccard_active_ingredient = round(len(intersection_active_ingredient) / len(union_active_ingredient), 2)

    return jaccard_drugs, jaccard_active_ingredient

def create_highlights(filtered_texts: list[str], drugs: list[str]):
    highlights = []
    for text in filtered_texts:
        for drug in drugs:
            print(f'(create_highlights) drug: {drug["drug_short"]} text: {text["text"]}')
            if drug['quote'].lower() in text['text'].lower():
                start_index = text['text'].lower().index(drug['quote'].lower())
                end_index = start_index + len(drug['quote'])

                # prompt: text['bounding_box'] is a list of alternating x,y coordinates specifying corners of a box. based on the position of drug['drug_short'] within text['text'] (counting characters) change the x,y coordinates to make the bounding box smaller, enveloping only the drug['drug_short']

                # Calculate the width of each character
                box_width = text['bounding_box'][2] - text['bounding_box'][0]
                box_height = text['bounding_box'][3] - text['bounding_box'][1]
                char_width = box_width / len(text['text'])

                # Adjust the bounding box to envelop only the drug['drug_short']
                # We're assuming that the bounding box is a rectangle and text is horizontal
                new_x1 = text['bounding_box'][0] + start_index * char_width
                new_x2 = text['bounding_box'][0] + end_index * char_width
                new_x3 = new_x2
                new_x4 = new_x1
                new_y1 = text['bounding_box'][1]
                new_y2 = text['bounding_box'][3]
                new_y3 = text['bounding_box'][5]
                new_y4 = text['bounding_box'][7]

                drug_bounding_box = [new_x1, new_y1, new_x2, new_y2, new_x3, new_y3, new_x4, new_y4]
                print(f'drug: {drug["registered_name"]}, text["bounding_box"]: {text["bounding_box"]} drug_bounding_box: {drug_bounding_box}')

                highlights.append({
                    'drug_name': drug['registered_name'],
                    'bounding_box_data': ','.join(map(str, drug_bounding_box)),
                    'uuid': drug['uuid']
                })

    return highlights

def display_image_to_terminal(image_path: str):
    with open(image_path, "rb") as image_file:
        image_data = base64.b64encode(image_file.read()).decode('utf-8')

        # iTerm2 escape sequence to display an image
        iterm2_escape_sequence = f"\033]1337;File=inline=1;width=auto;height=auto;preserveAspectRatio=1:{image_data}\a"

        # Print the escape sequence to the terminal
        sys.stdout.write(iterm2_escape_sequence)
        sys.stdout.write("\n")
        sys.stdout.flush()

def write_to_google_sheet(
        data: list[dict],
        credentials_filename: str = 'evaluation-log-sheet-credentials.json',
        sheet_id: str = os.getenv('EVALUATION_LOG_SHEET_ID'),
        sheet_name: str = 'Sheet1'):

    # Authorize the clientsheet
    gc = gspread.service_account(filename=credentials_filename)

    # Get the first sheet of the Spreadsheet
    sheet = gc.open_by_key(sheet_id)
    sheet_instance = sheet.worksheet(sheet_name)

    # Write the data
    results = []
    for entry in data:
        # convert optional comment to empty string
        if 'comment' in entry and entry['comment'] is None:
            entry['comment'] = ''
        # convert objects to strings
        for key,value in entry.items():
            if value.__class__ == dict or value.__class__ == list or value.__class__ == bool:
                entry[key] = str(value)
        r = [x for x in entry.values()]
        results.append(r)

    sheet_instance.append_rows(values=results, value_input_option="USER_ENTERED")
    return results

def get_short_drug_name(name: str):
    return name.split(' ')[0].upper()

def fuzzy_compare(str1, str2, min_ratio=0.8):
    str1 = str1.lower()
    str2 = str2.lower()
    return SequenceMatcher(None, str1, str2).ratio() > min_ratio or str1 in str2 or str2 in str1
