# -*- coding: utf-8 -*-

"""Accounts for Third Party music services."""

from __future__ import (
    absolute_import, unicode_literals
)

import logging
import weakref

import requests

from .. import discovery
from ..xml import XML

log = logging.getLogger(__name__)  # pylint: disable=C0103


# pylint: disable=too-many-instance-attributes
class Account(object):

    """An account for a Music Service.

    Each service may have more than one account: see
    http://www.sonos.com/en-gb/software/release/5-2
    """

    _all_accounts = weakref.WeakValueDictionary()

    def __init__(self):
        """Constructor.

        Args:
            service_type (str): A unique identifier for the music service to
                which this account relates, eg '2311' for Spotify.
            serial_number: (str): A unique identifier for this account
            nickname (str): The account's nickname
            deleted (bool): True if this account has been deleted
            username (str): The username used for logging into the music
                service
            metadata (str): Metadata for the account
            oa_device_id (str): Used for OpenAuth id for some services
            key (str): Used for OpenAuthid for some services
        """

        super(Account, self).__init__()
        self.service_type = ''
        self.serial_number = ''
        self.nickname = ''
        self.deleted = False
        self.username = ''
        self.metadata = ''
        self.oa_device_id = ''
        self.key = ''

    def __repr__(self):
        return "<{0} '{1}:{2}:{3}' at {4}>".format(
            self.__class__.__name__,
            self.serial_number,
            self.service_type,
            self.nickname,
            hex(id(self))
        )

    def 
(self):
        return self.__repr__()

    @staticmethod
    def _get_account_xml(soco):
        """Fetch the account data from a Sonos device.

        Args:
            soco (SoCo): a SoCo instance to query. If soco is none, a
                random device will be used.

        Returns:
            (str): a byte string containing the account data xml
        """
        # It is likely that the same information is available over UPnP as well
        # via a call to
        # systemProperties.GetStringX([('VariableName','R_SvcAccounts')]))
        # This returns an encrypted string, and, so far, we cannot decrypt it
        device = soco or discovery.any_soco()
        log.debug("Fetching account data from %s", device)
        settings_url = "http://{0}:1400/status/accounts".format(
            device.ip_address)
        result = requests.get(settings_url).content
        log.debug("Account data: %s", result)
        return result

    @classmethod
    def get_accounts(cls, soco=None):
        """Get a dict containing all accounts known to the Sonos system.

        Args:
            soco (SoCo, optional): a SoCo instance to query. If None, a random
            instance is used. Defaults to None

        Returns:
            dict: A dict containing account instances. Each key is the
            account's serial number, and each value is the related Account
            instance. Accounts which have been marked as deleted are excluded.

        Note:
            Any existing Account instance will have its attributes updated
            to those currently stored on the Sonos system.
        """

        root = XML.fromstring(cls._get_account_xml(soco))
        # _get_account_xml returns an ElementTree element like this:

        # <ZPSupportInfo type="User">
        #   <Accounts
        #   LastUpdateDevice="RINCON_000XXXXXXXX400"
        #   Version="8" NextSerialNum="5">
        #     <Account Type="2311" SerialNum="1">
        #         <UN>12345678</UN>
        #         <MD>1</MD>
        #         <NN></NN>
        #         <OADevID></OADevID>
        #         <Key></Key>
        #     </Account>
        #     <Account Type="41735" SerialNum="3" Deleted="1">
        #         <UN></UN>
        #         <MD>1</MD>
        #         <NN>Nickname</NN>
        #         <OADevID></OADevID>
        #         <Key></Key>
        #     </Account>
        # ...
        #   <Accounts />

        xml_accounts = root.findall('.//Account')
        result = {}
        for xml_account in xml_accounts:
            serial_number = xml_account.get('SerialNum')
            is_deleted = True if xml_account.get('Deleted') == '1' else False
            # cls._all_accounts is a weakvaluedict keyed by serial number.
            # We use it as a database to store details of the accounts we
            # know about. We need to update it with info obtained from the
            # XML just obtained, so (1) check to see if we already have an
            # entry in cls._all_accounts for the account we have found in
            # XML; (2) if so, delete it if the XML says it has been deleted;
            # and (3) if not, create an entry for it
            if cls._all_accounts.get(serial_number):
                # We have an existing entry in our database. Do we need to
                # delete it?
                if is_deleted:
                    # Yes, so delete it and move to the next XML account
                    del cls._all_accounts[serial_number]
                    continue
                else:
                    # No, so load up its details, ready to update them
                    account = cls._all_accounts.get(serial_number)
            else:
                # We have no existing entry for this account
                if is_deleted:
                    # but it is marked as deleted, so we don't need one
                    continue
                # If it is not marked as deleted, we need to create an entry
                account = Account()
                account.serial_number = serial_number
                cls._all_accounts[serial_number] = account

            # Now, update the entry in our database with the details from XML
            account.service_type = xml_account.get('Type')
            account.deleted = is_deleted
            account.username = xml_account.findtext('UN')
            # Not sure what 'MD' stands for.  Metadata? May Delete?
            account.metadata = xml_account.findtext('MD')
            account.nickname = xml_account.findtext('NN')
            account.oa_device_id = xml_account.findtext('OADevID')
            account.key = xml_account.findtext('Key')
            result[serial_number] = account
            # There is always a TuneIn account, but it is handled separately
            #  by Sonos, and does not appear in the xml account data. We
            # need to add it ourselves.
            tunein = Account()
            tunein.service_type = '65031'  # Is this always the case?
            tunein.deleted = False
            tunein.username = ''
            tunein.metadata = ''
            tunein.nickname = ''
            tunein.oa_device_id = ''
            tunein.key = ''
            tunein.serial_number = '0'
            result['0'] = tunein

        return result

    @classmethod
    def get_accounts_for_service(cls, service_type):
        """Get a list of accounts for a given music service.

        Args:
            service_type (str): The service_type to use

        Returns:
            (list): A list of MusicAccount instances
        """
        return [
            a for a in cls.get_accounts().values()
            if a.service_type == service_type
        ]

__str__