import os
import json
from odk_mailer.lib import parser, prompts, utils, globals, smtp
from odk_mailer.classes.mailer import Mailer
from odk_mailer.classes.config import Config
from odk_mailer.classes.Source import Source
from odk_mailer.classes.Fields import Fields
from odk_mailer.classes.Message import Message
from odk_mailer.classes.Schedule import Schedule
from odk_mailer.classes.Mailjob import Mailjob

def create(input_source, input_fields, input_message, input_schedule):
   
    # source
    if not input_source:
        source_data = prompts.prompt_source()
    else: 
        source_data = parser.parse_source(input_source)
    source = Source(source_data)

    # fields
    headers = source.get_headers()
    if not input_fields:
        fields_data = prompts.prompt_fields(headers)
    else: 
        fields_data = parser.parse_fields(input_fields, headers)
    fields = Fields(fields_data)

    # message
    if not input_message:
        message_data = prompts.prompt_message()
    else:
        message_data = parser.parse_message(input_message)
    message = Message(message_data)

    # schedule
    if not input_schedule:
        schedule_data = prompts.prompt_schedule()
    else:
        schedule_data = parser.parse_schedule(input_schedule)
    schedule = Schedule(schedule_data)        

    # Create Mailjob
    mailjob = Mailjob()
    created = mailjob.create(source, fields, message, schedule)
    print(created)
    
    ###
    # Features
    ###

    # 1. Reminders 
    # tbd: Reminders
    # reminders have two attributes:
    # total_amount, frequency, e.g. 3 times in total, every hour|day|week|custom frequency
    # after first scheduled send

    # reminders will be stored inside .odk-mailer/reminder/hash_[instance_id].json
    # having updated recipients from non-respondents (calculated from base recipients and respondents) 
    # on a per reminder case

    # https://github.com/tertek/zapier-odk-scripts/blob/main/get_non_responding.py
    # reminders require api connection and following inputs
    # form_register = "test_form_register" #name of form that is used for registration, given as api or csv
    # form_follow = "test_form_follow"  # name of form that is used for follow up
    # # if use_form_attachment
    # form_follow_attached = "test_form_follow_attached" # name of form that is used for follow up; 
    # form_attachment = "follow.csv" # name of form attachment attached to form_follow; 
    # # field config
    # field_email_register = "email_register" # name of email field for registration form, given
    # field_email_follow = "email_follow"
    # field_email_follow_attach = "email"

    # 2. Cron Jobs
    # add cron-job via https://pypi.org/project/python-crontab/ and https://stackabuse.com/scheduling-jobs-with-python-crontab/
    # We will can use a single job, that checks every hour if we have pending Mailjobs, running 'odk-mailer evaluate
    # process reminder tasks as follows: 
    # 1. Perform API request to calculate reminder recipients
    # 2. Send Emails based on calculated recipients
    # 3. Summarize progress

    # 3. Recipients Validation
    # Validate recipients and ignore (e.g. filter) invalid emails to proceed
    # code example
    # if not recipients.validate(email_field):
    #     utils.render_table(["id", "email_field", "error"], recipients.invalidEmails)
    #     ignore_invalid_emails = typer.confirm("Invalid emails found. Would you like to continue although you have invalid emails?")
    
    #     if not ignore_invalid_emails:
    #         raise typer.Exit("\nAborted.")

    # with typer.progressbar(self.data) as progress:
    #     for row in progress:
    #         total += 1
    #         email = row[email_field]
    #         try:
    #             if not email:
    #                 raise EmailNotValidError("Email address missing. Check for missing delimiters (',') in your CSV file.")
    #             # Disable DNS checks since this can be blocked for unknown reasons within network.
    #             validate_email(email, check_deliverability=True)
    #         except EmailNotValidError as e:
    #             invalid = [str(total) ,email, str(e)]
    #             invalid_emails.append(invalid)

def run(hash_or_id, dry=False, verbose=False):

    odk_mailer_config = Config()

    if not odk_mailer_config:
        utils.abort("Cannot send emails without Config")

    if not hash_or_id:
        utils.abort("ID/Hash is required")

    with open(globals.odk_mailer_jobs, "r") as f:
        jobs = json.load(f)

    found = next((obj for obj in jobs if obj["hash"].startswith(hash_or_id)), None)
    if not found:
        utils.abort("Job not found.")

    hash = found['hash']
     
    # check if ready to be sent
    # simple check: scheduled <= now
    # advan. check: hasReminders AND UnsentReminderTime <= now
    
    # if found["scheduled"] > utils.now():
    #     utils.abort("Schedule is in future")

    mailer = Mailer(hash, dry, verbose, odk_mailer_config)
    mailer.send()
    # in case we have a reminder case, generate reminder contents from reminders/hash_reminderId.json

def delete(hash):
    if not hash:
            utils.abort("ID is required")

    with open(globals.odk_mailer_jobs, "r") as f:
        jobs = json.load(f)

    found = next((obj for obj in jobs if obj["hash"].startswith(hash)), None)

    if not found:
        utils.abort("Job not found.")
   
    # tbd: confirm
    
    # deletion from /job/<hash>.json
    path_job = os.path.join(globals.odk_mailer_job, found['hash']+'.json')
    if os.path.exists(path_job):
        os.remove(path_job)

    # deletion from /jobs.json
    jobs_updated = list(filter(lambda x: x['hash']!=found['hash'], jobs))   
    
    with open(globals.odk_mailer_jobs, "w") as f:
        f.write(json.dumps(jobs_updated))

    print("Deleted " + found['hash'])    

def list_jobs():

    with open(globals.odk_mailer_jobs, "r+") as f:
        jobs = json.load(f)

    if len(jobs) > 0:
        utils.print_jobs(jobs)

    else: print("There are no mailjobs yet. Create one to see it listed.")


def evaluate(dry=False):

    with open(globals.odk_mailer_jobs, "r+") as f:
        jobs = json.load(f)

    evals = []

    for job in jobs:
        if job["scheduled"] <= utils.now():
            # simple evaluation: check if scheduled time is smaller/equal to now
            # if true, add to selected list
            evals.append(job["hash"])

        ###
        # untested code, since reminders are not yet implemented in create command
        elif "reminders" in job and len(job["reminders"]) > 0:
            # advanced evaluation: addtionally check if job has reminder times that are smaller/equal to now
            # if true, add to selected list
            print("Addiitonally checking if we have any valid reminders")
            for reminder in job["reminders"]:
                if reminder["timestamp"] <= utils.now():
                    evals.append(job["hash"])
                    break
        ###
                
        else:
            # skipping this job since not qualified to be run
            pass

    if dry:
        print(evals)
        print(len(evals))

    else:
        for eval in evals:
            run(eval)

def test(sender, recipient, host, port):
    print()
    print(f"Sending test mail from {sender} to:  {recipient} via: {host}:{port}")
    print()
    smtp.send_test_mail(sender, recipient, host, port)


def db():
    print("Testing db")