import argparse
import configparser
import os.path
import csv
import delorean
import requests
from twilio.rest import Client


def send_phone_notification(entry, config):
    ACCOUNT_SID = config['TWILIO']['ACCOUNT_SID']
    AUTH_TOKEN = config['TWILIO']['AUTH_TOKEN']
    FROM = config['TWILIO']['FROM']
    coupon = entry['Kod']
    TO = entry['AdresTelefon']
    text = f'Gratulacje! Oto kupon rabatowy: {coupon}'

    try:
        client = Client(ACCOUNT_SID, AUTH_TOKEN)
        client.messages.create(body=text, from_=FROM, to=TO)
    except Exception as err:
        return 'BŁĄD'

    return 'WYSŁANY'


def send_email_notification(entry, config):
    KEY = config['MAILGUN']['KEY']
    DOMAIN = config['MAILGUN']['DOMAIN']
    FROM = config['MAILGUN']['FROM']
    TO = entry['AdresTelefon']
    name = entry['Nazwisko']
    auth = ('api', KEY)
    coupon = entry['Kod']
    text = f'Gratulacje! Oto kupon rabatowy: {coupon}'

    data = {
        'from': f'Sender <{FROM}>',
        'to': f'{name} <{TO}>',
        'subject': 'Otrzymałeś kupon rabatowy!',
        'text': text,
    }
    response = requests.post(f"https://api.mailgun.net/v3/{DOMAIN}/messages",
                             auth=auth, data=data)
    if response.status_code == 200:
        return 'WYSŁANY'

    return 'BŁĄD'


def send_notification(entry, send, config):
    if not send:
        return entry

    # Wybór funkcji wysyłających powiadomienia.
    METHOD = {
        'TELEFON': send_phone_notification,
        'E-MAIL': send_email_notification,
    }
    try:
        method = METHOD[entry['TypKontaktu']]
        result = method(entry, config)
    except KeyError:
        result = 'NIEPOPRAWNY_TYP_KONTAKTU'

    entry['Czas'] = delorean.utcnow().datetime.isoformat()
    entry['Status'] = result
    return entry


def save_file(notif_file, data):
    '''
    Zastępowanie danych w pliku nowymi informacjami.
    '''

    # Należy zacząć od początku pliku.
    notif_file.seek(0)

    header = data[0].keys()
    writer = csv.DictWriter(notif_file, fieldnames=header)
    writer.writeheader()
    writer.writerows(data)

    # Koniecznie zapisz dane na dysku.
    notif_file.flush()


def main(data, codes, notif_file, config, send):
    # Przetwarzanie każdego wiersza z niewysłanym kodem.
    for index, entry in enumerate(data):
        if entry['Status'] == 'WYSŁANY':
            continue

        if not entry['Kod']:
            if not codes:
                msg = ('W pliku brakuje kodów, a plik z kodami '
                       'nie został zdefiniowany')
                raise Exception(msg)
            entry['Kod'] = codes.pop()

        entry = send_notification(entry, send, config)
        data[index] = entry

        # Zapisywanie danych w pliku.
        save_file(notif_file, data)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(type=argparse.FileType('r+'), dest='notif_file',
                        help='Plik z powiadomieniami')
    parser.add_argument('-c', '--codes', type=argparse.FileType('r'),
                        help='Opcjonalny plik z kodami. Jeśli jest podany, '
                             'zostanie zapełniony kodami. '
                             'Na razie nie są one wysyłane')
    parser.add_argument('--config', type=str, dest='config_file',
                        default='config.ini',
                        help='Plik konfiguracyjny (domyślnie config.ini)')
    args = parser.parse_args()

    # Wczytywanie konfiguracji
    if not os.path.isfile(args.config_file):
        print(f'Brak pliku konfiguracyjnego {args.config_file}. Program kończy pracę')
        exit(1)
    with open(args.config_file) as fp:
        config = configparser.ConfigParser()
        config.read_file(fp)

    # Wczytywanie danych.
    reader = csv.DictReader(args.notif_file)
    data = list(reader)

    codes = None
    send = True
    if args.codes:
        codes = [code_line[0] for code_line in csv.reader(args.codes)]
        send = False

    main(data=data, codes=codes, notif_file=args.notif_file,
         config=config, send=send)
