# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_pingme_class.ipynb.

# %% auto 0
__all__ = ['Card', 'resolved_payload', 'PingMe', 'send_to_webhook', 'send_to_email', 'send_to_logfile', 'cli']

# %% ../nbs/01_pingme_class.ipynb 6
# That export there, it makes sure this code goes into the module.

# standard libs
import os
import re

# Common to template
# add into settings.ini, requirements, package name is python-dotenv, for conda build ensure `conda config --add channels conda-forge`
import dotenv  # for loading config from .env files, https://pypi.org/project/python-dotenv/
import envyaml  # Allows to loads env vars into a yaml file, https://github.com/thesimj/envyaml
import fastcore  # To add functionality related to nbdev development, https://github.com/fastai/fastcore/
from fastcore import (
    test,
)
from fastcore.script import (
    call_parse,
)  # for @call_parse, https://fastcore.fast.ai/script
import json  # for nicely printing json and yaml
from fastcore import test
from fastcore.utils import patch

# Project specific libraries
from pydantic import BaseModel
import fastcore


from pingme import (
    core,
)

import json
import os
from pathlib import Path  # for type hinting and file checking

import distutils.util  # to convert string to bool
import json
import sys
from sys import stderr

# %% ../nbs/01_pingme_class.ipynb 13
from pydantic import BaseModel


class Card(BaseModel):
    name: str
    context: dict

# %% ../nbs/01_pingme_class.ipynb 14
import json  # to manage json payloads
import re  # regular expression for parsing


@staticmethod
def resolved_payload(template: json, context: dict) -> json:
    """
    Resolves the payload by substituting variables in the `payload` with values from the `context` and ensures all variables are accounted for
    """
    if template is None:
        # Ensure there is a payload
        raise ValueError("Payload is None")
    str_temp = json.dumps(template)  # convert payload to string
    for key in context.keys():
        # Substitute all variables in payload with values from payload_context, it can also be set up that their are no variables in the payload
        str_temp = str_temp.replace("${" + key + "}", context[key])
    if re.search("${.*}", str_temp):
        # Check if there are any variables left, this is not allowed
        raise ValueError("Unresolved variables in payload")
    return json.loads(str_temp)

# %% ../nbs/01_pingme_class.ipynb 15
class PingMe:
    """
    PingMe class which notifies via either a webhook or email
    """

    def __init__(self, card: Card, config_file=None):  # Extension of card file

        # Resolve config variables from ENV vars
        if config_file is None:
            config_file = "./config/example.env"
        config = core.get_config(os.environ.get("CORE_CONFIG_FILE", config_file))
        if card.name not in config["pingme"]["cards"]:
            raise ValueError(
                f"Card name {card.name} not found in config file, check spelling"
            )
        self.card: dict = {}
        self.card = config["pingme"]["cards"][card.name]
        self.card["context"] = card.context

        # Set default values for card variables if not provided in context
        for item in self.card["variables"]:
            if item not in self.card["context"]:
                self.card["context"][item] = self.card["variables"][item]

        # Get title and text which are special variables
        self.title: str = self.card["context"].get("title", "")
        self.text: str = self.card["context"].get("text", "")

        # Set options
        self.email: dict = config["pingme"]["options"]["email"]
        self.webhook: dict = config["pingme"]["options"]["webhook"]
        self.logfile: dict = config["pingme"]["options"]["logfile"]

        # Resolve payload variables from card.context, defined below
        self.payload: json = resolved_payload(
            self.card["template"], self.card["context"]
        )

    def __str__(self) -> str:
        return f"""PingMe object with:
    card: {self.card}
    card_context: {self.card["context"]}
    payload: {self.payload}"""

    def __repr__(self) -> str:
        return self.__str__()

# %% ../nbs/01_pingme_class.ipynb 18
import requests  # to send requests to webhooks


@staticmethod
def send_to_webhook(
    url: str, payload: json, header: json = {"Content-Type": "application/json"}
) -> dict:
    if url is None:
        raise Exception("Webhook URL not set")
    # Send message to webhook
    try:
        response = requests.post(url, data=payload, headers=header)
    except Exception as e:
        raise Exception(f"Error sending message to webhook: {e}")
    return {"status_code": response.status_code, "response": response.text}

# %% ../nbs/01_pingme_class.ipynb 20
@patch
def send_webhook(self: PingMe) -> dict:
    return send_to_webhook(self.webhook["url"], json.dumps(self.payload))

# %% ../nbs/01_pingme_class.ipynb 23
import email.mime.text  # to format emails
import smtplib  # to send emails


@staticmethod
def send_to_email(
    payload: json,
    subject: str,
    from_: str,
    to: str,
    host: str,
    port: int = 25,
    user=None,
    password=None,
) -> dict:
    # NOTE: Wondering if I should do something more like https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http
    email_status = False
    html_content = """






            This is a sample body


    """
    msg = email.mime.text.MIMEText(html_content, "html")
    msg["Subject"] = subject
    msg["From"] = from_
    msg["To"] = to
    email_connection = smtplib.SMTP(host, port)
    try:
        email_connection.ehlo()
        email_connection.starttls()
        email_connection.ehlo()
        email_connection.login(user, password)
        email_connection.sendmail(from_, to, msg.as_string())
        email_status = True
    finally:
        email_connection.quit()
        return {"status_code": 200, "response": email_status}

# %% ../nbs/01_pingme_class.ipynb 24
@patch
def send_email(self: PingMe) -> dict:
    return send_to_email(
        self.payload,
        self.title,
        self.email["from"],
        self.email["to"],
        self.email["smtp"]["host"],
        self.email["smtp"]["port"],
        self.email["smtp"]["user"],
        self.email["smtp"]["password"],
    )

# %% ../nbs/01_pingme_class.ipynb 25
import datetime  # to get current date and time which is used in logging


@staticmethod
def send_to_logfile(logfile: str, title: str, text: str) -> dict:
    """
    Send message to logfile
    TODO: The log file only lods title and text right now (not payload), while payload can be included it is not good for parsing. Need to think of a solution for this. Could be to save each payload as a seperate file and the log is a list of files.
    """
    if logfile is None:
        raise Exception("Log file not set")
    with open(logfile, "a") as f:
        # Write the current time
        f.write(f"{datetime.datetime.now()}\t{title}\t{text}\n")
        f.write("\n")
    return {"status_code": 200, "response": True}

# %% ../nbs/01_pingme_class.ipynb 26
@patch
def send_logfile(self: PingMe) -> dict:
    return send_to_logfile(self.logfile["path"], self.title, self.text)

# %% ../nbs/01_pingme_class.ipynb 27
# Make a CLI function using `call_parse` to handle arguments


# Ensure settings.ini contains `console_scripts = pingme=pingmeme:cli`, this makes the call as `pingme` and calls the cli function found in package pingme.pingme


@call_parse
def cli(
    context: str = None,  # string denoting a json object with context variables (e.g. '{"title":"Test Title", "text":"Test Text"}')
    webhook: bool = None,  # attempts to send to webhook
    email: bool = None,  # attempts to send to email
    logfile: bool = None,  # attempts to send to logfile
    example: bool = None,  # Runs with example params, if it doesn't work config values haven't been set properly
    config_file: str = None,  # config file to set env vars from
):
    """
    PingMe send a notification to a webhook, email, or log file.\n\n
    Usage examples:
    - basic:
    pingme --context '{"title":"Test Title", "text":"Test Text"}' --webhook
    - advanced:
    pingme --config_file ./config/config.env --context '{"title":"Test Title", "text":"Test Text"}' --webhook --email --logfile --card_name default --card_dir ./cards/ --card_ext .yaml
    NOTE: Will require use of ./cards/default.yaml and ./config/config.default.env to be set up properly
    """
    config = core.get_config(config_file)

    card = Card
    card.name = config["pingme"]["user_input"]["card"]["name"]
    card.context = config["pingme"]["user_input"]["card"]["context"]
    print(card)
    pingme = PingMe(card, config_file)

    if not webhook and not email and not logfile:
        print("No destination provided, exiting", file=stderr)
        return False
    else:
        if webhook:
            pingme.send_webhook()
            print("Sent to webhook", file=sys.stderr)
        if email:
            pingme.send_email()
            print("Sent to email", file=sys.stderr)
        if logfile:
            pingme.send_logfile()
            print("Sent to logfile", file=sys.stderr)

    return True
