# -*- coding: utf-8 -*-
"""
blinkpy by Kevin Fronczak - A Blink camera Python library.

https://github.com/fronzbot/blinkpy
Original protocol hacking by MattTW :
https://github.com/MattTW/BlinkMonitorProtocol
Published under the MIT license - See LICENSE file for more details.
"Blink Wire-Free HS Home Monitoring & Alert Systems" is a trademark
owned by Immedia Inc., see www.blinkforhome.com for more information.
I am in no way affiliated with Blink, nor Immedia Inc.
"""

import time
import getpass
import logging
from requests.structures import CaseInsensitiveDict
import blinkpy.helpers.errors as ERROR
from blinkpy import api
from blinkpy.sync_module import BlinkSyncModule
from blinkpy.helpers.util import (
    create_session, merge_dicts, BlinkURLHandler,
    BlinkAuthenticationException)
from blinkpy.helpers.constants import (
    BLINK_URL, LOGIN_URL, LOGIN_BACKUP_URL)

REFRESH_RATE = 30

_LOGGER = logging.getLogger('blinkpy')


class Blink():
    """Class to initialize communication."""

    def __init__(self, username=None, password=None,
                 refresh_rate=REFRESH_RATE):
        """
        Initialize Blink system.

        :param username: Blink username (usually email address)
        :param password: Blink password
        :param refresh_rate: Refresh rate of blink information.
                             Defaults to 15 (seconds)
        """
        self._username = username
        self._password = password
        self._token = None
        self._auth_header = None
        self._host = None
        self.account_id = None
        self.network_ids = []
        self.urls = None
        self.sync = CaseInsensitiveDict({})
        self.region = None
        self.region_id = None
        self.last_refresh = None
        self.refresh_rate = refresh_rate
        self.session = None
        self.networks = []
        self.cameras = CaseInsensitiveDict({})
        self._login_url = LOGIN_URL

    @property
    def auth_header(self):
        """Return the authentication header."""
        return self._auth_header

    def start(self):
        """
        Perform full system setup.

        Method logs in and sets auth token, urls, and ids for future requests.
        Essentially this is just a wrapper function for ease of use.
        """
        if self._username is None or self._password is None:
            self.login()
        else:
            self.get_auth_token()

        networks = self.get_ids()
        for network_name, network_id in networks.items():
            sync_module = BlinkSyncModule(self, network_name, network_id)
            sync_module.start()
            self.sync[network_name] = sync_module
        self.cameras = self.merge_cameras()

    def login(self):
        """Prompt user for username and password."""
        self._username = input("Username:")
        self._password = getpass.getpass("Password:")
        if self.get_auth_token():
            _LOGGER.info("Login successful!")
            return True
        _LOGGER.warning("Unable to login with %s.", self._username)
        return False

    def get_auth_token(self):
        """Retrieve the authentication token from Blink."""
        if not isinstance(self._username, str):
            raise BlinkAuthenticationException(ERROR.USERNAME)
        if not isinstance(self._password, str):
            raise BlinkAuthenticationException(ERROR.PASSWORD)

        login_url = LOGIN_URL
        self.session = create_session()
        response = api.request_login(self,
                                     login_url,
                                     self._username,
                                     self._password)

        if response.status_code == 200:
            response = response.json()
            (self.region_id, self.region), = response['region'].items()
        else:
            _LOGGER.debug(
                ("Received response code %s "
                 "when authenticating, "
                 "trying new url"), response.status_code
            )
            login_url = LOGIN_BACKUP_URL
            response = api.request_login(self,
                                         login_url,
                                         self._username,
                                         self._password)
            self.region_id = 'piri'
            self.region = "UNKNOWN"

        self._host = "{}.{}".format(self.region_id, BLINK_URL)
        self._token = response['authtoken']['authtoken']
        self._auth_header = {'Host': self._host,
                             'TOKEN_AUTH': self._token}
        self.networks = response['networks']
        self.urls = BlinkURLHandler(self.region_id)
        self._login_url = login_url

        return self._auth_header

    def get_ids(self):
        """Set the network ID and Account ID."""
        response = api.request_networks(self)
        # Look for only onboarded network, flag warning if multiple
        # since it's unexpected
        all_networks = []
        network_dict = {}
        for network, status in self.networks.items():
            if status['onboarded']:
                all_networks.append('{}'.format(network))
                network_dict[status['name']] = network

        # For the first onboarded network we find, grab the account id
        for resp in response['networks']:
            if str(resp['id']) in all_networks:
                self.account_id = resp['account_id']
                break

        self.network_ids = all_networks
        return network_dict

    def refresh(self, force_cache=False):
        """
        Perform a system refresh.

        :param force_cache: Force an update of the camera cache
        """
        if self.check_if_ok_to_update() or force_cache:
            _LOGGER.debug("Attempting refresh of cameras.")
            for sync_module in self.sync:
                sync_module.refresh(force_cache=force_cache)

    def check_if_ok_to_update(self):
        """Check if it is ok to perform an http request."""
        current_time = int(time.time())
        last_refresh = self.last_refresh
        if last_refresh is None:
            last_refresh = 0
        if current_time >= (last_refresh + self.refresh_rate):
            self.last_refresh = current_time
            return True
        return False

    def merge_cameras(self):
        """Merge all sync camera dicts into one."""
        combined = CaseInsensitiveDict({})
        for sync in self.sync:
            combined = merge_dicts(combined, self.sync[sync].cameras)
        return combined

@@ -5745,97 +5745,130 @@
     
-_LOGGER.debug(%22Attempting refresh of cameras.%22)%0A            for sync_module in self.sync:
+for sync_name, sync_module in self.sync.items():%0A                _LOGGER.debug(%22Attempting refresh of sync %25s%22, sync_name)
 %0A   

Fix broken sync refresh