import unittest
from unittest.mock import patch

from Firefly.automation.triggers import Trigger, Triggers
from Firefly.const import EVENT_ACTION_ANY, EVENT_ACTION_OFF, EVENT_ACTION_ON, EVENT_SUNRISE, EVENT_SUNSET, EVENT_TYPE_BROADCAST, SOURCE_LOCATION, STATE, TEMPERATURE, TIME
from Firefly.helpers.events import Event
from Firefly.helpers.subscribers import Subscriptions


class TestTriggers(unittest.TestCase):
  @patch('Firefly.core.core.Firefly')
  def setUp(self, firefly):
    self.firefly = firefly
    self.firefly.subscriptions = Subscriptions()
    self.device = 'fake_device'
    self.device_b = 'fake_device_b'
    self.app = 'subscriber_app'
    self.app_b = 'subscriber_app_b'
    self.trigger_id = 'test_automation_trigger'

  def test_trigger_simple(self):
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    self.assertListEqual(trigger.trigger_action, [{
      STATE: [EVENT_ACTION_ON]
    }])
    self.assertEquals(trigger.listen_id, self.device)

  def test_trigger_any(self):
    trigger = Trigger(self.device)
    self.assertListEqual(trigger.trigger_action, [{
      EVENT_ACTION_ANY: [EVENT_ACTION_ANY]
    }])
    self.assertEquals(trigger.listen_id, self.device)

  def test_trigger_any_property(self):
    trigger = Trigger(self.device, {
      EVENT_ACTION_ANY: [EVENT_ACTION_ON]
    })
    self.assertListEqual(trigger.trigger_action, [{
      EVENT_ACTION_ANY: [EVENT_ACTION_ON]
    }])
    self.assertEquals(trigger.listen_id, self.device)

  def test_trigger_any_source(self):
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    self.assertListEqual(trigger.trigger_action, [{
      STATE: [EVENT_ACTION_ON]
    }])
    self.assertEquals(trigger.listen_id, self.device)

  def test_add_one_trigger(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger(trigger)
    self.assertTrue(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])

    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })
    self.assertSetEqual(triggers.trigger_sources, {self.device})

  def test_add_one_trigger_twice(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger(trigger)
    self.assertTrue(trigger_added)
    trigger_added = triggers.add_trigger(trigger)
    self.assertFalse(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])
    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })
    self.assertSetEqual(triggers.trigger_sources, {self.device})

  def test_add_one_trigger_list(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger2 = Trigger(self.device_b, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger([trigger, trigger2])
    self.assertTrue(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }, {
      'listen_id':      self.device_b,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])
    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device:   {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      },
      self.device_b: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })
    self.assertSetEqual(triggers.trigger_sources, {self.device, self.device_b})

  def test_add_one_trigger_list_twice(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger2 = Trigger(self.device_b, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger([trigger, trigger2])
    self.assertTrue(trigger_added)
    trigger_added = triggers.add_trigger([trigger, trigger2])
    self.assertFalse(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id'
:      self.device,
      'trigger_action': [{
        STAT
E: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }, {
      'listen_id':      self.device_b,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])
    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device:   {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      },
      self.device_b: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })
    self.assertSetEqual(triggers.trigger_sources, {self.device, self.device_b})

  def test_add_one_trigger_list_twice_reorder(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger2 = Trigger(self.device_b, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger([trigger, trigger2])
    self.assertTrue(trigger_added)
    trigger_added = triggers.add_trigger([trigger2, trigger])
    self.assertFalse(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }, {
      'listen_id':      self.device_b,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])
    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device:   {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      },
      self.device_b: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })

  def test_add_one_trigger_case_1(self):
    '''
    This test adding a trigger that's a subset of another trigger.
    '''
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger2 = Trigger(self.device_b, {
      STATE: [EVENT_ACTION_ON]
    })
    trigger_added = triggers.add_trigger([trigger, trigger2])
    self.assertTrue(trigger_added)
    trigger_added = triggers.add_trigger([trigger])
    self.assertTrue(trigger_added)
    self.assertListEqual(triggers.triggers, [[{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }, {
      'listen_id':      self.device_b,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }], [{
      'listen_id':      self.device,
      'trigger_action': [{
        STATE: [EVENT_ACTION_ON]
      }],
      'source':         'SOURCE_TRIGGER'
    }]])
    self.assertDictEqual(self.firefly.subscriptions.subscriptions, {
      self.device:   {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      },
      self.device_b: {
        STATE: {
          EVENT_ACTION_ON: [self.trigger_id]
        }
      }
    })
    self.assertSetEqual(triggers.trigger_sources, {self.device, self.device_b})

  def test_add_any_trigger(self):
    triggers = Triggers(self.firefly, self.trigger_id)
    trigger = Trigger(self.device)
    trigger_added = triggers.add_trigger([trigger])
   