from langchain.tools import tool
from datetime import datetime, timedelta
from re import search
from dataclasses import dataclass
from typing import Union
import logging

from langchain_core.tools import Tool
from langchain_experimental.utilities import PythonREPL

@tool
def add(a: float, b: float) -> str:
    """Adds a+b and returns the sum"""
    af = float(a)
    bf = float(b)
    return f"{af} + {af} = {af+bf}"

@tool
def multiply(a: float, b: float) -> str:
    """Multiplies a*b and returns the product"""
    af = float(a)
    bf = float(b)
    return f"{af} * {bf} = {af*bf}"

@tool
def get_current_date_and_time() -> str:
    """Return current Date and time"""
    return "Thursday the 8th of August 2024 18:03"


@dataclass
class Entry:
    time: datetime
    content: str

note_entries = [
    Entry(
        time=datetime.strptime("2024/08/03 14:58", "%Y/%m/%d %H:%M"),
        content="Granny Petra says I should call Wolfgang to ask him when Susanne comes back when he comes back from his holidays."
    ),
    Entry(
        time=datetime.strptime("2024/08/07 09:15", "%Y/%m/%d %H:%M"),
        content="Mom says to buy some fresh flowers for the living room before Aunt Linda visits."
    ),
    Entry(
        time=datetime.strptime("2024/08/06 18:30", "%Y/%m/%d %H:%M"),
        content="Pick up the dry cleaning on Thursday; they close early on Fridays."
    ),
    Entry(
        time=datetime.strptime("2024/08/05 11:45", "%Y/%m/%d %H:%M"),
        content="Ask Dr. Mills about the side effects of the new medication he got me."
    ),
    Entry(
        time=datetime.strptime("2024/08/04 16:00", "%Y/%m/%d %H:%M"),
        content="Call the plumber to fix the leak in the upstairs bathroom."
    ),
    Entry(
        time=datetime.strptime("2024/08/03 08:00", "%Y/%m/%d %H:%M"),
        content="Schedule a car service appointment before the road trip to the mountains."
    ),
    Entry(
        time=datetime.strptime("2024/08/02 20:10", "%Y/%m/%d %H:%M"),
        content="Check if the library has a copy of the new mystery novel everyone is talking about."
    ),
    Entry(
        time=datetime.strptime("2024/08/01 14:30", "%Y/%m/%d %H:%M"),
        content="Send a thank-you note to Mrs. Jenkins for the lovely dinner last weekend."
    ),
    Entry(
        time=datetime.strptime("2024/07/31 12:05", "%Y/%m/%d %H:%M"),
        content="Email the project update to the team by the end of the week."
    ),
    Entry(
        time=datetime.strptime("2024/07/30 07:50", "%Y/%m/%d %H:%M"),
        content="Pick up a birthday card for Uncle George before the family gathering on Sunday."
    ),
    Entry(
        time=datetime.strptime("2024/07/29 15:20", "%Y/%m/%d %H:%M"),
        content="Research local yoga classes; consider signing up for the weekend session."
    ),
    Entry(
        time=datetime.strptime("2023/08/01 07:21", "%Y/%m/%d %H:%M"),
        content="Talk to Joffrey for the insurance!"
    ),
    Entry(
        time=datetime.strptime("2023/08/01 23:10", "%Y/%m/%d %H:%M"),
        content="Went out with Charlotte for our anniversary. Pizza at Cavalinos. She loved the Necklace!"
    )
]  

@tool
def get_notes_in_timespan(begin: str, to: str) -> str:
    """Recieves the Notes saved in a time span.
    
    aguments:
    begin: str # start of the time span (incluive) %Y/%m/%d
    to: str # end of the timespan (incluive) %Y/%m/%d
    
    exaples:
    {"begin": "2012/08/31", "to": "2012/09/06"} # 7 days from the 31st 00:00 till the 6th 23:59
    {"begin": "2019/04/14", "to": "2019/04/14"} # All notes from the 19th of April 2019"""

    try:
        begin_d = datetime.strptime(begin, "%Y/%m/%d")
        to_d = datetime.strptime(to+" 23:59", "%Y/%m/%d %H:%M")
    except ValueError:
        return "Error: Invalid input. Date format is %Y/%m/%d"

    try:
        assert begin_d < to_d
    except AssertionError:
        return "Error: from time has to be before to time."

    filtered_entries = [entry for entry in note_entries if begin_d <= entry.time <= to_d]

    if filtered_entries == []:
        return "No entries were found for that time period."

    ret = ""
    is_first = True
    for entry in filtered_entries:
        ret += '' if is_first else '\n\n'
        ret += f"{datetime.strftime(entry.time, '%Y/%m/%d %H:%M')} {entry.content}"
        is_first = False

    return ret

@tool
def get_notes_containing(patterns: Union[list[str], str]) -> str:
    """Recieves the Notes matching any of the RegEx patterns.
    
    aguments:
    patterns: Union[list[str], str] # case insensitive pattern(s) notes are to be mached against
    
    exaples:
    {"patterns": [ "Aunt(ie)?", "Sabine" ]} # Looks for Notes related to Aunt Sabine"""

    if isinstance(patterns, list):
        big_pattern = '|'.join(f"({s})" for s in patterns)
    elif isinstance(patterns, str):
        big_pattern = patterns
    else:
        return f"Error: Invalid Input type. `patterns` can either be a list of strings or a single string. But got {type(patterns)}."

    filtered_entries = [entry for entry in note_entries if search(big_pattern.lower(), entry.content.lower())]

    ret = ""
    is_first = True
    for entry in filtered_entries:
        ret += '' if is_first else '\n\n'
        ret += f"{datetime.strftime(entry.time, '%Y/%m/%d %H:%M')} {entry.content}"
        is_first = False

    if ret == "":
        ret = "No matching notes were found. Try diffrent patterns."

    return ret
                               
@tool
def write_note(note: str) -> str:
    """Write a not with the current time to the database."""
    return "Written."
                               
@tool
def save_python_repl(command: str):
    """Simulates the normal python repl but with certain patterns blocked for savety reasons"""
    python_repl = PythonREPL()
    blocked_patterns = [
        "^ *os\\.",
        "import os",
        "^ *subprocess\\.",
        "^ *(with)? ?open\\(",
        "^ *shutil\\.",
        "^ *requests\\.",
    ]
    valid = True
    for pattern in blocked_patterns:
        if search(pattern, command):
             valid = False
             break

    if valid:
        ret = python_repl.run(command)

        if ret == "":
            ret = python_repl.run(f'print(({command}), end="")')

        return ret
    else:
        return f"Command not executed, becaise the blocked pattern `{pattern}` was found in the command."
    


