#!/usr/bin/env python3
from plugpwrmgr import PlugPowerManager
from powerschedule import SimplePowerSchedule
from emailwatcher import EmailWatcher
from pyHS100 import SmartPlug
from testsmtpd import SSLSMTPServer, TestCredentialValidator
import logging
import sys
import os
import json
import argparse
import time
from datetime import datetime
import unittest
import warnings
import secure_smtpd
import shutil
import argparse

SETTINGS_FILE = 'settings.json'
POWER_SCHEDULE_FILE = 'power-schedule.json'
MAIL_SETTINGS_FILE = 'mail_settings.json'

class SmartPlugDummy:
    ON = 'ON'
    OFF = 'OFF'
    def __init__(self, initial_state, responsive = True):
        self._state = initial_state
        self._responsive = responsive
    @property
    def state(self):
        return self._state
    def turn_on(self):
        if self._responsive:
            self._state = SmartPlugDummy.ON
    def turn_off(self):
        if self._responsive:
            self._state = SmartPlugDummy.OFF

simulate_smart_plug = False
plug_list = {}

def create_smart_plug(address):
    global simulate_smart_plug
    if simulate_smart_plug:
        if not address in plug_list:
            plug_list[address] = SmartPlugDummy(SmartPlugDummy.OFF)
        return plug_list[address]
    return SmartPlug(address)

class SmartPlugPowerManagerSystemTest(unittest.TestCase):
    _smtp_server = None
    def __init__(self, *args, **kwargs):
        super(SmartPlugPowerManagerSystemTest, self).__init__(*args, **kwargs)
        with open(SETTINGS_FILE, 'rt') as settings_file:
            self._settings = json.loads(settings_file.read())

        if SmartPlugPowerManagerSystemTest._smtp_server == None:
            SmartPlugPowerManagerSystemTest._smtp_server = SSLSMTPServer(
                ('0.0.0.0', 1025),
                None,
                require_authentication=True,
                ssl=True,
                certfile='server.crt',
                keyfile='server.key',
                credential_validator=TestCredentialValidator(),
                maximum_execution_time = 1.0
                )
            SmartPlugPowerManagerSystemTest._smtp_server.start()

    def setUp(self):
        warnings.simplefilter('ignore', DeprecationWarning)

    def _gen_sched_file(self, days_of_week = [], hours_of_day = [], days_of_month = []):
        sched = {'days_of_week': days_of_week, 'hours_of_day': hours_of_day, 'days_of_month': days_of_month}
        with open(POWER_SCHEDULE_FILE, 'wt') as sched_file:
            sched_file.write(json.dumps(sched))

    def _apply_power_schedule(self, plug_address):
        plug = create_smart_plug(plug_address)
        plug.ON = 'ON'
        plug.OFF = 'OFF'

        schedule = SimplePowerSchedule(POWER_SCHEDULE_FILE)

        watcher = EmailWatcher(MAIL_SETTINGS_FILE, self._settings['NOTIFICATION_EMAIL_ADDRESS'], 'tester@testing.com')

        manager = PlugPowerManager(schedule, plug, watcher)

        manager.apply_schedule()

    def _setup_test(self, plug_address, scheduled_for_on = False, device_on_before_test = False):
        os.system('cp mail_settings.test.json mail_settings.json')
        plug = create_smart_plug(plug_address)
        if device_on_before_test:
            plug.turn_on()
        else:
            plug.turn_off()
        today = datetime.now().day
        if not scheduled_for_on:
            if today == 1:
                today = 28
            else:
                today = 1
        self._gen_sched_file(days_of_month = [today])

    def _validate_test(self, plug_address, smtp_server, scheduled_for_on, device_on_before_test):
        plug = create_smart_plug(plug_address)
        if scheduled_for_on:
            assert plug.state == 'ON'
        else:
            assert plug.state == 'OFF'

        messages = smtp_server.pop_messages()
        if scheduled_for_on == device_on_before_test:
            assert len(messages) == 0
        else:
            assert len(messages) == 1
            if scheduled_for_on:
                assert 'turned on' in messages[0]
            else:
                assert 'turned off' in messages[0]

    def run_apply_power_schedule(self, plug_address, smtp_server, scheduled_for_on = False, device_on_before_test = False):
        # print('===\nrunning system test: device=%s schedule=%s' % ('ON' if device_on_before_test else 'OFF', 'ON' if scheduled_for_on else 'OFF'))

        self._setup_test(self._settings['PLUG_ADDRESS'], scheduled_for_on=scheduled_for_on, device_on_before_test=device_on_before_test)

        self._apply_power_schedule(plug_address)

        self._validate_test(plug_address, smtp_server, scheduled_for_on, device_on_before_test)

    def test_apply_power_schedule_device_off_schedule_off(self):
        self.run_apply_power_schedule(self._settings['PLUG_ADDRESS'], SmartPlugPowerManagerSystemTest._smtp_server, scheduled_for_on=False, device_on_before_test=False)

    def test_apply_power_schedule_device_off_schedule_on(self):
        self.run_apply_power_schedule(self._settings['PLUG_ADDRESS'], SmartPlugPowerManagerSystemTest._smtp_server, scheduled_for_on=False, device_on_before_test=True)

    def test_apply_power_schedule_device_on_schedule_off(self):
        self.run_apply_power_schedule(self._settings['PLUG_ADDRESS'], SmartPlugPowerManagerSystemTest._smtp_server, scheduled_for_on=True, device_on_before_test=False)

    def test_apply_power_schedule_device_on_schedule_on(self):
        self.run_apply_power_schedule(self._settings['PLUG_ADDRESS'], SmartPlugPowerManagerSystemTest._smtp_server, scheduled_for_on=True, device_on_before_test=True)


if __name__ == '__main__':
    logger = logging.getLogger( secure_smtpd.LOG_NAME )
    logger.setLevel(logging.CRITICAL)
    logging.basicConfig(level=logging.CRITICAL)

    parser = argparse.ArgumentParser(description='Apply power schedule to KASA Smart Plugs')
    parser.add_argument('-s', '--simulate', action='store_true', help='simulat a plug with the given initial state')
    parser.add_argument('unittest_args', nargs='*')
    args = parser.parse_args()

    simulate_smart_plug = args.simulate


    # setup the test files
    shutil.copyfile('mail_settings.test.json', 'mail_settings.json')

    sys.argv[1:] = args.unittest_args
    unittest.main()

