"""
Contains standalone helper functions
"""

import os
import sys
import time
import datetime
import base64
from typing import List
import requests
import pytz
from cabinet import Cabinet, Mail


class RemindMailUtils:
    """
    A utility class for handling reminders and email operations.
    """

    # instance of the Cabinet class for configuration management
    cab = Cabinet()

    # file path for reminders
    path_remind_file = cab.get('path', 'remindmail', 'file')

    # for sending emails
    mail = Mail()

    # to store query trace information
    query_trace: List[str] = []

    def get_user_time(self):
        """
        A helper function to return the epoch seconds based on the user's timezone.

        Returns:
            int: The epoch seconds representing the current time in the user's timezone.
        """

        user_timezone = self.cab.get("remindmail", "timezone")
        if user_timezone is None:
            # user's timezone not saved in `cab`; get timezone based on IP
            print("Checking timezone...")
            response = requests.get('https://ipapi.co/json/', timeout=10)

            # parse the response JSON to get the user's timezone
            if response.status_code == 200:
                data = response.json()
                user_timezone = self.cab.put(
                    "remindmail", "timezone", data.get('timezone'))
                print(f"Set timezone: {user_timezone}")
            else:
                print("Could not parse timezone; using UTC")
                return datetime.datetime.utcnow()

        timezone = pytz.timezone(user_timezone)

        now = datetime.datetime.now(timezone)

        # get the UTC time
        utc_now = datetime.datetime.utcnow()

        # calculate the time difference between the specified timezone and UTC
        offset = timezone.utcoffset(utc_now).total_seconds()

        # subtract the offset from the current time
        utc_time = now + datetime.timedelta(seconds=offset)

        # convert the UTC time to the number of seconds since January 1, 1970
        unix_time = int(utc_time.timestamp())

        return unix_time

    def get_sent_today(self):
        """
        Returns the number of reminders that have been sent today,
        based on today's log file.

        "Today", for these purposes, starts at 4AM.
        """

        today = datetime.datetime.now().strftime("%Y-%m-%d")
        yesterday = (datetime.datetime.now() -
                     datetime.timedelta(days=1)).strftime("%Y-%m-%d")

        if datetime.datetime.now().hour < 4:
            today = yesterday

        today_log = self.cab.get_file_as_array(
            f"{today}/LOG_DAILY_{today}.log", self.cab.path_log)

        return sum(log.count(': Reminder - ') for log in today_log)

    def print_stats(self):
        """
        Prints RemindMail Usage Statistics
        """

        print("RemindMail Stats:\n")
        print(f"Sent Today: {self.get_sent_today()}")

    def print_reminders_file(self, param=None):
        """
        Displays the scheduled reminders in remind.py, formatted with line numbers
        Usage: remindmail ls
        Parameters:
        - param: string; currently unused

        Passing 'help' will only return the help information for this function.
        """

        if param == "help":
            return (f"Displays the scheduled reminders in remind.py"
                    f" (in {RemindMailUtils.path_remind_file}),"
                    " formatted with line numbers\n\nUsage: remindmail ls")

        remindmd_local = f"{RemindMailUtils.path_remind_file}/remind.md"

        if RemindMailUtils.path_remind_file is not None:
            os.system(f"cat -n {remindmd_local}")
        else:
            print(f"Could not find reminder path; in "
                  f"${self.cab.path_cabinet}/settings.json, "
                  f"set path -> remindmail -> file to the absolute path of "
                  f"the directory of your remind.md file.")
            print("\n")

    def mail_reminders_for_later(self):
        """Mails a summary of reminders with [any] at the start from remind.md in {PATH_LOCAL}"""

        remindmd_file = self.cab.get_file_as_array(
            "remind.md", "notes") or []
        reminders_for_later = []
        for item in remindmd_file:
            if item.startswith("[any] "):
                reminders_for_later.append(f"<li>{item.split('[any] ')[1]}")

        mail_summary = "Just a heads up, these reminders are waiting for you!<br><br>"
        mail_summary += "".join(reminders_for_later)
        mail_summary += "<br><br>To remove these, edit <b>remind.md</b>."

        date_formatted = datetime.datetime.today().strftime('%B %d, %Y')
        RemindMailUtils().send_email(
            f"Pending Reminder Summary: {date_formatted}", mail_summary, is_quiet=False)

    def offset(self, args):
        """
        Calculates the offset for a certain date (today by default)
        """

        def months_since_epoch(epoch):
            epoch_time = time.localtime(epoch)
            return ((epoch_time.tm_year - 1970) * 12) + epoch_time.tm_mon

        if len(args) < 2:
            print(f"""Error: 'remind -o {args[0] or ''}' missing required arguments:
    remind -o '<type (day,week,month)> <target date (YYYY-MM-DD), optional> <n>'\
    \n\nRun `remind -h` for details.""")
            return

        if len(args) >= 2:
            epoch_time = int(datetime.datetime.strptime(
                args[1], "%Y-%m-%d").timestamp())
            offset_n = args[2]
        else:
            offset_n = args[-1]
            epoch_time = int(time.time())

        try:
            if not offset_n.isnumeric():
                raise IndexError

            offset_n = int(offset_n)
            token_example = "d"
            if args[0] == "month":
                token_example = "m"
                return_val = months_since_epoch(epoch_time) % offset_n
            elif args[0] == "week":
                token_example = "w"
                return_val = int(epoch_time/60/60/24/7) % offset_n
            elif args[0] == "day":
                return_val = int(epoch_time/60/60/24) % offset_n
            else:
                print(f"'{args[0]}' must be 'day', 'week', or 'month'.")
                return

            print(return_val)
            print(f"This means you can add '[{token_example}%{offset_n}+{return_val}] <task name>' \
to {RemindMailUtils.path_remind_file}/remind.md to match the selected date.")

            if offset_n == 1:
                print((f"Note: Anything % 1 is always 0. This is saying "
                       f"'every single {args[0]}'.\nOffsets are used to make sure a task will run"
                       f" for a given {args[0]}. '%1' means it will always run, so no need for an"
                       f" offset.\nPlease see the README for details, or"
                       f" run 'remindmail help offset'."))
            elif return_val == 0:
                print(("Note: The offset is 0, so a task for this date in remind.md "
                       "will be added without an offset."))

        except ValueError:
            print("Date must be YYYY-MM-DD.")
        except IndexError:
            print((f"Missing <n>, as in 'every n {args[0]}s'\n"
                   f"Usage: remindmail offset {args[0]} {args[1]} n"
                   f"\nFor help: 'remindmail help offset'"))
            return

    def edit_reminders_file(self):
        """
        Edits the remind.md file
        You must configure the path to remind.md in

        cabinet -> settings.json -> path -> edit -> remind
        """

        try:
            self.cab.edit_file("remind")
        except FileNotFoundError:
            print((f"You must configure the path to remind.md in "
                   f"{self.cab.path_cabinet}/settings.json -> "
                   f"path -> edit -> remind.\n\n"))

            resp = ''
            while resp not in ['y', 'n']:
                resp = input((f"Would you like to set this to "
                             f"{RemindMailUtils.path_remind_file}/remind.md? y/n\n\n"))
                if resp == 'y':
                    self.cab.put("path", "edit", "remind", "value",
                                 f"{RemindMailUtils.path_remind_file}/remind.md")
                    print((f"\n\nSet. Open {self.cab.path_cabinet}/settings.json"
                           f" and set path -> edit -> remind -> sync to true"
                           f" to enable cloud syncing."))
        sys.exit()

    def send_email(self, subject, body, method="Terminal", is_quiet=False):
        """A helper function to call mail.send"""

        print(f"Sending: {subject}")

        if body:
            subject = f"{subject} [See Notes]"

        body += f"<br><br>Sent via {method}"

        RemindMailUtils.mail.send(
            f"Reminder - {subject}", body or "", is_quiet=is_quiet)
        print("\nSent! Check your inbox.")

    def create_jira_issue(self, summary, description, issue_type: str = None, label: str = None):
        """
        A barebones integration with Jira.

        Requires Cabinet to be configured as described in README.

        Parameters:
            summary(str): issue title
            description(str): issue description
            issue_type(str): issue type ("story", "task", "bug", "spike", "epic")
            label(str): label
        """

        api_url_base = self.cab.get('jira', 'project-url')

        if api_url_base is None:
            self.cab.log("RemindMail - Cannot create Jira issue; cabinet",
                         "-> jira -> project-url is unset",
                         level="warn")
            return

        api_url = f"{str(api_url_base).rstrip('/')}/rest/api/2/issue"

        # Set your Jira credentials
        jira_email = self.cab.get("jira", "email")
        jira_api_token = self.cab.get("keys", "jira")

        if not jira_api_token or not jira_email:
            self.cab.log("RemindMail - cannot create Jira issue; cabinet is missing "
                         "`keys -> jira` or `jira -> email` (see README)",
                         level="warn")
            return

        if not summary:
            self.cab.log("RemindMail - cannot create Jira issue; summary cannot be blank",
                         level="warn")
            return

        # Create the authentication header
        credentials = f'{jira_email}:{jira_api_token}'
        encoded_credentials = base64.b64encode(
            credentials.encode('utf-8')).decode('utf-8')
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Basic {encoded_credentials}'
        }

        # Create the issue payload
        payload = {
            'fields': {
                'project': {'key': self.cab.get("jira", "project-key")},
                'summary': f"RMMJ: {summary}",
                'issuetype': {'name': issue_type.capitalize() if issue_type else ''},
                'labels': [label] if label else None,
            }
        }

        # Prompt for missing fields
        if not issue_type:
            issue_type_input = input('Please enter an issue type:\n'
                                     'story\nbug\ntask\nspike\nepic\n\n').capitalize()
            payload['fields']['issuetype']['name'] = issue_type_input

        if description is None:
            description_input = input(
                '\nPlease enter a description (or Enter for none)\n')
            payload['fields']['description'] = description_input
        else:
            payload['fields']['description'] = description

        issue_type_lower = payload['fields']['issuetype']['name'].lower()
        if issue_type_lower in ['story', 'bug']:
            if issue_type_lower == 'story':
                a_c = input('\nPlease enter an AC:\n')
                payload['fields']['customfield_10035'] = a_c

        if label is None:
            label_input = input('\nPlease enter a Label:\n')
            payload['fields']['labels'] = [label_input]

        # Send the POST request to create the issue
        response = requests.post(api_url, headers=headers,
                                 json=payload, timeout=10)

        # Check the response
        if response.status_code == 201:
            created_issue = response.json()
            issue_key = created_issue['key']
            print(f"{issue_key} has been created.")
            print(f"{api_url_base}/browse/{issue_key}")
            self.cab.log(f"\n{issue_key} has been created.", is_quiet=True)
        else:
            print('Failed to create the issue.')
            print(f'Response: {response.text}')
            self.cab.log(
                f"RemindMail unable to create Jira issue: {response}", level="warn")
