"""Model with high-level objects that represents our expectations after migration

It includes objects already existing on target panel, and objects that should
be created on target panel.
"""

from parallels.core.utils.common import default
from parallels.core.utils.entity import Entity


class TargetModelEntity(Entity):
    """Base class for all target data model entities"""
    yaml_prefix = 'target'


class Model(TargetModelEntity):
    def __init__(self, reseller_plans, plans, resellers, clients):
        self._reseller_plans = reseller_plans
        self._plans = plans
        self._resellers = resellers
        self._clients = clients

    @property
    def reseller_plans(self):
        return self._reseller_plans

    @property
    def plans(self):
        """Retrieve hosting plans owned by administrator

        :rtype: dict[str, parallels.core.target_data_model.Plan]
        """
        return self._plans

    @property
    def resellers(self):
        """
        :rtype: dict[str, parallels.core.target_data_model.Reseller]
        """
        return self._resellers

    @property
    def clients(self):
        """Retrieve customers owner by administrator

        :rtype: dict[str, parallels.core.target_data_model.Client]
        """
        return self._clients

    def iter_all_subscriptions(self):
        for client in self.iter_all_owners():
            for subscription in client.subscriptions:
                yield subscription

    def iter_all_owners(self):
        for owner in self.clients.values():
            yield owner
        for reseller in self.resellers.values():
            for owner in reseller.clients:
                yield owner

    def iter_all_customers(self):
        for owner in self.clients.values():
            if owner.login is None:
                # skip fake entries
                continue
            yield owner
        for reseller in self.resellers.values():
            for owner in reseller.clients:
                if owner.login == reseller.login:
                    # skip fake entries
                    continue
                yield owner


class HostingPlan(TargetModelEntity):
    def __init__(self, name, is_addon, source, settings):
        self._name = name
        self._is_addon = is_addon
        self._source = source
        self._settings = settings

    @property
    def name(self):
        return self._name

    @property
    def is_addon(self):
        return self._is_addon

    @property
    def source(self):
        return self._source

    @property
    def settings(self):
        """Object that can be used to create and configure the hosting plan in target panel
        """
        return self._settings


class ResellerPlan(TargetModelEntity):
    type = 'reseller'

    def __init__(self, name, overuse, oversell, limits, permissions, aps_bundle_filter, source):
        self._name = name
        self._overuse = overuse
        self._oversell = oversell
        self._limits = limits
        self._permissions = permissions
        self._aps_bundle_filter = aps_bundle_filter
        self._source = source

    @property
    def name(self):
        return self._name

    @property
    def overuse(self):
        return self._overuse

    @property
    def oversell(self):
        return self._oversell

    @property
    def limits(self):
        return self._limits

    @property
    def permissions(self):
        return self._permissions

    @property
    def aps_bundle_filter(self):
        return self._aps_bundle_filter

    @property
    def source(self):
        return self._source


class AuxiliaryUserRole(TargetModelEntity):
    def __init__(self, name, permissions):
        self._name = name
        self._permissions = permissions

    @property
    def name(self):
        return self._name

    @property
    def permissions(self):
        """Permissions of this panel user role

        :rtype: dict[str, bool]
        """
        return self._permissions


class LoginObject(TargetModelEntity):
    """
    :type _login: str|unicode
    :type _password: str|unicode
    :type _password_type: str|unicode
    :type _personal_info: parallels.core.target_data_model.PersonalInfo
    """
    def __init__(self, login, password_type, password, personal_info):
        self._login = login
        self._password = password
        self._password_type = password_type
        self._personal_info = personal_info

    @property
    def login(self):
        """Objects's login

        :rtype: str | unicode
        """
        return self._login

    @login.setter
    def login(self, value):
        self._login = value

    @property
    def password(self):
        """Object's password value

        :rtype: str | unicode
        """
        return self._password

    @password.setter
    def password(self, value):
        self._password = value

    @property
    def password_type(self):
        """Type of object's password: 'plain' or 'encrypted'

        :rtype: str | unicode
        """
        return self._password_type

    @property
    def personal_info(self):
        return self._personal_info

    @personal_info.setter
    def personal_info(self, value):
        self._personal_info = value


class Reseller(LoginObject):
    """
    :type _plan_name: str|unicode
    :type _clients: list[parallels.core.target_data_model.Client]
    :type _company: str|unicode
    :type _auxiliary_user_roles: list[parallels.core.target_data_model.AuxiliaryUserRole]
    :type _auxiliary_users: list[parallels.core.target_data_model.AuxiliaryUser]
    :type _is_enabled: bool
    :type _plans: dict[str|unicode, parallels.core.target_data_model.HostingPlan]
    :type _source: str|unicode
    :type _settings: parallels.plesk.models.target_data_model.ResellerSettings
    :type _creation_date: str|unicode
    """
    def __init__(
        self, login, password, personal_info, plan_name=None, clients=None, company='',
        auxiliary_user_roles=None, auxiliary_users=None, is_enabled=True, plans=None, source=None,
        settings=None, password_type='plain', creation_date=None, description=None
    ):
        super(Reseller, self).__init__(login, password_type, password, personal_info)
        self._plan_name = plan_name
        self._clients = default(clients, [])
        self._company = company
        self._auxiliary_user_roles = default(auxiliary_user_roles, [])
        self._auxiliary_users = default(auxiliary_users, [])
        self._is_enabled = is_enabled
        self._plans = default(plans, {})
        self._source = source
        self._settings = settings
        self._creation_date = creation_date
        self._description = description

    @property
    def plan_name(self):
        """Plan to which reseller is assigned to

        :rtype: str | unicode
        """
        return self._plan_name

    @property
    def clients(self):
        return self._clients

    @property
    def company(self):
        """
        :rtype: str | unicode
        """
        return self._company

    @property
    def auxiliary_user_roles(self):
        return self._auxiliary_user_roles

    @property
    def auxiliary_users(self):
        return self._auxiliary_users

    @property
    def is_enabled(self):
        return self._is_enabled

    @property
    def plans(self):
        """Retrieve hosting plans owned by this reseller

        :rtype: dict[str, parallels.core.target_data_model.Plan]
        """
        return self._plans

    @property
    def source(self):
        return self._source

    @property
    def settings(self):
        """Object that can be used by import API to create and configure the reseller, depends on target panel type
        """
        return self._settings

    @property
    def creation_date(self):
        """Date when reseller was initially created on the source server.

        Format is "YYYY-MM-DD", for example "2015-11-16" for 16 of November 2015

        :rtype: str | unicode
        """
        return self._creation_date

    @property
    def description(self):
        """Description of the client

        :rtype: str | unicode | None
        """
        return self._description


class AuxiliaryUser(LoginObject):
    _external_email = ''  # type: basestring

    def __init__(self, login, password, name, roles, personal_info, is_active, subscription_name, is_domain_admin, external_email):
        super(AuxiliaryUser, self).__init__(login, 'plain', password, personal_info)
        self._name = name
        self._roles = roles
        self._is_active = is_active
        self._subscription_name = subscription_name
        self._is_domain_admin = is_domain_admin
        self._external_email = external_email

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def roles(self):
        return self._roles

    @roles.setter
    def roles(self, value):
        self._roles = value

    @property
    def is_active(self):
        return self._is_active

    @is_active.setter
    def is_active(self, value):
        self._is_active = value

    @property
    def subscription_name(self):
        return self._subscription_name

    @subscription_name.setter
    def subscription_name(self, value):
        self._subscription_name = value

    @property
    def is_domain_admin(self):
        return self._is_domain_admin

    @is_domain_admin.setter
    def is_domain_admin(self, value):
        self._is_domain_admin = value

    @property
    def external_email(self):
        return self._external_email

    @external_email.setter
    def external_email(self, value):
        self._external_email = value


class TargetSubscriptionData(Entity):
    """Base class for target-specific data of subscription"""
    pass


class Subscription(TargetModelEntity):
    def __init__(
        self, name, plan_name, plan_addon_names, is_enabled, is_locked, source,
        is_windows, sysuser_login, admin_description=None, reseller_description=None,
        guid=None, creation_date=None,
        target_data=None, mail_service=True
    ):
        """Class constructor

        :type name: str | unicode
        :type plan_name: str | unicode | None
        :type plan_addon_names: list[str | unicode] | None
        :type is_enabled: bool | None
        :type is_locked: bool | None
        :type source: str | unicode
        :type is_windows: bool
        :type sysuser_login: str | unicode | None
        :type admin_description: str | unicode | None
        :type reseller_description: str | unicode | None
        :type guid: str | unicode | None
        :type creation_date: str | unicode | None
        :type target_data: parallels.core.target_data_model.TargetSubscriptionData | None
        :type mail_service: bool
        """
        self._name = name
        self._plan_name = plan_name
        self._plan_addon_names = plan_addon_names
        self._is_enabled = is_enabled
        self._is_locked = is_locked
        self._source = source
        self._is_windows = is_windows
        self._sysuser_login = sysuser_login
        self._admin_description = admin_description
        self._reseller_description = reseller_description
        self._guid = guid
        self._creation_date = creation_date
        self._target_data = target_data
        self._mail_service = mail_service

    @property
    def name(self):
        return self._name

    @property
    def name_canonical(self):
        return self.name.lower().encode('idna')

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def plan_name(self):
        return self._plan_name

    @plan_name.setter
    def plan_name(self, value):
        self._plan_name = value

    @property
    def plan_addon_names(self):
        return self._plan_addon_names

    @plan_addon_names.setter
    def plan_addon_names(self, value):
        self._plan_addon_names = value

    @property
    def is_enabled(self):
        return self._is_enabled

    @is_enabled.setter
    def is_enabled(self, value):
        self._is_enabled = value

    @property
    def is_locked(self):
        return self._is_locked

    @property
    def source(self):
        return self._source

    @source.setter
    def source(self, value):
        self._source = value

    @property
    def is_windows(self):
        return self._is_windows

    @is_windows.setter
    def is_windows(self, value):
        self._is_windows = value

    @property
    def sysuser_login(self):
        return self._sysuser_login

    @sysuser_login.setter
    def sysuser_login(self, value):
        self._sysuser_login = value

    @property
    def admin_description(self):
        """Admin's description of the subscription

        :rtype: str | unicode | None
        """
        return self._admin_description

    @property
    def reseller_description(self):
        """Resellers's description of the subscription

        :rtype: str | unicode | None
        """
        return self._reseller_description

    @property
    def guid(self):
        """Global Unique Identifier of subscription

        :rtype: str | unicode | None
        """
        return self._guid

    @property
    def creation_date(self):
        """Date of subscription creation (as string, YYYY-MM-DD)

        :rtype: str | unicode | None
        """
        return self._creation_date

    @property
    def target_data(self):
        """Target-panel specific data of subscription

        :rtype: parallels.core.target_data_model.TargetSubscriptionData
        """
        return self._target_data

    @target_data.setter
    def target_data(self, value):
        self._target_data = value

    @property
    def mail_service(self):
        return self._mail_service

    @mail_service.setter
    def mail_service(self, value):
        self._mail_service = value


class Client(LoginObject):
    """
    :type _subscriptions: list[parallels.core.target_data_model.Subscription]
    :type _company: str|unicode
    :type _auxiliary_user_roles: list[parallels.core.target_data_model.AuxiliaryUserRole]
    :type _auxiliary_users: list[parallels.core.target_data_model.AuxiliaryUser]
    :type _is_enabled: bool
    :type _source: str|unicode
    :type _target_client:
    :type _creation_date: str|unicode
    """
    def __init__(
        self, login, password, personal_info, subscriptions=None, company='',
        auxiliary_user_roles=None, auxiliary_users=None, is_enabled=True, source=None,
        target_client=None, password_type='plain', creation_date=None, description=None
    ):
        super(Client, self).__init__(login, password_type, password, personal_info)
        self._subscriptions = default(subscriptions, [])
        self._company = company
        self._auxiliary_user_roles = default(auxiliary_user_roles, [])
        self._auxiliary_users = default(auxiliary_users, [])
        self._is_enabled = is_enabled
        self._source = source
        self._target_client = target_client
        self._creation_date = creation_date
        self._description = description

    @property
    def subscriptions(self):
        return self._subscriptions

    @subscriptions.setter
    def subscriptions(self, value):
        self._subscriptions = value

    @property
    def company(self):
        return self._company

    @company.setter
    def company(self, value):
        self._company = value

    @property
    def auxiliary_user_roles(self):
        return self._auxiliary_user_roles

    @auxiliary_user_roles.setter
    def auxiliary_user_roles(self, value):
        self._auxiliary_user_roles = value

    @property
    def auxiliary_users(self):
        """
        :rtype: parallels.core.target_data_model.AuxiliaryUser
        """
        return self._auxiliary_users

    @auxiliary_users.setter
    def auxiliary_users(self, value):
        self._auxiliary_users = value

    @property
    def is_enabled(self):
        return self._is_enabled

    @is_enabled.setter
    def is_enabled(self, value):
        self._is_enabled = value

    @property
    def source(self):
        return self._source

    @source.setter
    def source(self, value):
        self._source = value

    @property
    def target_client(self):
        """Client created at the target panel

        :rtype: parallels.core.target_data_model.CreatedClient
        """
        return self._target_client

    @target_client.setter
    def target_client(self, value):
        """Set client created at the target panel

        :type value: parallels.core.target_data_model.CreatedClient
        """
        self._target_client = value

    def is_admin(self):
        return self.login is None

    @property
    def creation_date(self):
        """Date when reseller was initially created on the source server.

        Format is "YYYY-MM-DD", for example "2015-11-16" for 16 of November 2015

        :rtype: str | unicode
        """
        return self._creation_date

    @property
    def description(self):
        """Description of the client

        :rtype: str | unicode | None
        """
        return self._description


class CreatedClient(object):
    def __init__(self, id):
        self._id = id

    @property
    def id(self):
        return self._id


class PersonalInfo(TargetModelEntity):
    """
    :type _first_name: str|unicode
    :type _last_name: str|unicode
    :type _email: str|unicode
    :type _address: str|unicode
    :type _city: str|unicode
    :type _county: str|unicode
    :type _state: str|unicode
    :type _postal_code: str|unicode
    :type _language_code: str|unicode
    :type _locale: str|unicode
    :type _country_code: str|unicode
    :type _primary_phone: str|unicode
    :type _additional_phone: str|unicode
    :type _fax: str|unicode
    :type _country_code: str|unicode
    :type _mobile_phone: str|unicode
    :type _im: str|unicode
    :type _im_type: str|unicode
    """
    def __init__(
        self, email, first_name, last_name='', address='',
        city='', county='', state='', postal_code='', language_code='en', locale='en-EN',
        country_code='US', primary_phone='', additional_phone='', fax='', mobile_phone='', comment='', im='',
        im_type='Other'
    ):
        self._first_name = first_name
        self._last_name = last_name
        self._email = email
        self._address = address
        self._city = city
        self._county = county
        self._state = state
        self._postal_code = postal_code
        self._language_code = language_code
        self._locale = locale
        self._country_code = country_code
        self._primary_phone = primary_phone
        self._additional_phone = additional_phone
        self._fax = fax
        self._mobile_phone = mobile_phone
        self._comment = comment
        self._im = im
        self._im_type = im_type

    @property
    def first_name(self):
        return self._first_name

    @property
    def last_name(self):
        return self._last_name

    @property
    def email(self):
        return self._email

    @property
    def address(self):
        return self._address

    @property
    def city(self):
        return self._city

    @property
    def county(self):
        return self._county

    @property
    def state(self):
        return self._state

    @property
    def postal_code(self):
        return self._postal_code

    @property
    def language_code(self):
        return self._language_code

    @property
    def locale(self):
        return self._locale

    @property
    def country_code(self):
        return self._country_code

    @property
    def primary_phone(self):
        return self._primary_phone

    @property
    def additional_phone(self):
        return self._additional_phone

    @property
    def fax(self):
        return self._fax

    @property
    def mobile_phone(self):
        return self._mobile_phone

    @property
    def comment(self):
        return self._comment

    @property
    def im(self):
        return self._im

    @property
    def im_type(self):
        return self._im_type
