from schematics import types
from schematics.models import Model

from .. import api, entities as en


class _Update(Model):
    """Update base model"""
    id = types.StringType(required=True)
    creator_id = types.StringType(required=True)
    item_id = types.StringType(required=True)
    body = types.StringType()
    created_at = types.StringType()
    text_body = types.StringType()
    updated_at = types.StringType()


class Update(_Update):
    """An update
    
        Properties

            assets : `list[moncli.entities.Asset]`
                The update's assets/files.
            body: `str`
                The update's html formatted body.
            created_at: `str`
                The update's creation date.
            creator : `moncli.entities.User`
                The update's creator.
            creator_id : `str`
                The unique identifier of the update creator.
            id : `str`
                The update's unique identifier.
            item_id : `str`
                The update's item ID.
            replies : `list[moncli.entities.Reply]
                The update's replies.
            text_body : `str`
                The update's text body.
            updated_at : `str`
                The update's last edit date.
        
        Methods

            get_creator : `moncli.entities.User`
                Get the update's creator.
            add_reply : `moncli.entities.Reply`
                Add reply to update.
            get_replies : `list[moncli.entities.Reply]`
                Get update replies.
            add_file : `moncli.entities.Asset`
                Add a file to update.
            get_files : `list[moncli.entities.Asset]
                Get update's files.
            delete : `moncli.entity.Update`
                Delete an update.
    """

    def __init__(self, **kwargs):
        self.__creds = kwargs.pop('creds')
        self.__creator = None
        creator = kwargs.pop('creator', None)
        if creator:
            self.__creator = en.User(cred=self.__creds, **creator)
        self.__replies = None
        replies = kwargs.pop('replies', None)
        if replies:
            self.__replies = [Reply(creds=self.__creds, item_id=kwargs['item_id'], **reply) for reply in replies]
        self.__assets = None
        assets = kwargs.pop('assets', None)
        if assets:
            self.__assets = [en.Asset(creds=self.__creds, **asset) for asset in assets]
        super(Update, self).__init__(kwargs)

    def __repr__(self):
        o = self.to_primitive()
        if self.__replies and len(self.__replies) > 0:
            o['replies'] = self.__replies
        return str(o)

    @property
    def creator(self):
        """The update's creator."""
        if not self.__creator:
            self.__creator = self.get_creator()
        return self.__creator

    @property
    def replies(self):
        """The update's replies."""
        if not self.__replies:
            self.__replies = self.get_replies()
        return self.__replies

    @property
    def assets(self):
        """The update's assets/files."""
        return self.__assets

    def get_creator(self, *args):
        """Get the update's creator.
        
            Parameters

                args : `tuple`
                    The list of user fields to return.

            Returns

                user : `moncli.entities.User`
                    The update's creator.

            Return Fields

                account : `moncli.entities.Account`
                    The user's account.
                birthday : `str`
                    The user's birthday.
                country_code : `str`
                    The user's country code.
                created_at : `str`
                    The user's creation date.
                email : `str`
                    The user's email.
                enabled : `bool`
                    Is the user enabled or not.
                id : `str`
                    The user's unique identifier.
                is_admin: `bool`
                    Is the user a admin or not.
                is_guest : `bool`
                    Is the user a guest or not.
                is_pending : `bool`
                    Is the user a pending user.
                is_verified: `bool`
                    Is the user is verified
                is_view_only : `bool`
                    Is the user a view only user or not.
                join_date : `str`
                    The date the user joined the account.
                location : `str`
                    The user' location.
                mobile_phone : `str`
                    The user's mobile phone number.
                name : `str`
                    The user's name.
                phone : `str`
                    The user's phone number.
                photo_original : `str`
                    The user's photo in the original size.
                photo_small : `str`
                    The user's photo in small size (150x150).
                photo_thumb : `str`
                    The user's photo in thumbnail size (100x100).
                photo_thumb_small : `str`
                    The user's photo in small thumbnail size (50x50).
                photo_tiny : `str`
                    The user's photo in tiny size (30x30).
                teams : `list[moncli.entities.Team]`
                    The teams the user is a member in.
                time_zone_identifier : `str`
                    The user's time zone identifier.
                title : `str`
                    The user's title.
                url : `str`
                    The user's profile url.
                utc_hours_diff : `int`
                    The user's UTC hours difference.
        """

        user_data = api.get_users(
            *args,
            api_key=self.__creds.api_key_v2,
            ids=[int(self.creator_id)])[0]
        return en.User(creds=self.__creds, **user_data)


    def add_reply(self, body: str, *args):
        """Add reply to update.

            Parameters

                body : `str`
                    The text body of the reply.
                args : `tuple`
                    The list of reply fields to return.

            Returns

                update : `moncli.entities.Update`
                    The updated update.

            Return Fields

                assets : `list[moncli.entities.Asset]`
                    The update's assets/files.
                body: `str`
                    The update's html formatted body.
                created_at: `str`
                    The update's creation date.
                creator : `moncli.entities.User`
                    The update's creator
                creator_id : `str`
                    The unique identifier of the update creator.
                id : `str`
                    The update's unique identifier.
                item_id : `str`
                    The update's item ID.
                replies : `list[moncli.entities.Reply]
                    The update's replies.
                text_body : `str`
                    The update's text body.
                updated_at : `str`
                    The update's last edit date.
        """

        update_data = api.create_update(
            body,
            self.item_id,
            *args,
            api_key=self.__creds.api_key_v2,
            parent_id=self.id)
        return en.Update(creds=self.__creds, **update_data)


    def get_replies(self, *args):
        """Get update replies.
    
            Parameters

                args : `tuple`
                    The list of update fields to return.

            Returns

                replies : `list[moncli.entities.Reply]`
                    The update's replies.

            Return Fields

                body : `str`
                    The reply's html formatted body.
                created_at : `str`
                    The reply's creation date.
                creator : `moncli.entities.User`
                    The reply's creator.
                creator_id : `str`
                    The unique identifier of the reply creator.
                id : `str`
                    The reply's unique identifier.
                text_body : `str`
                    The reply's text body.
                updated_at : `str`
                    The reply's last edit date.
        """

         # Hard configure the pagination rate.
        page = 1
        page_limit = 500
        record_count = 500

        while record_count >= page_limit:
            updates_data = api.get_updates(
                'id', 'item_id', 
                *api.get_field_list(api.DEFAULT_REPLY_QUERY_FIELDS, 'replies', *args),
                api_key=self.__creds.api_key_v2,
                limit=page_limit,
                page=page,
                )
            
            try:
                target_update = [update for update in updates_data if update['id'] == self.id][0]
                return [Reply(creds=self.__creds, item_id=target_update['item_id'], **reply_data) for reply_data in target_update['replies']]
            except KeyError:
                if len(target_update) == 0:
                    page += 1
                    record_count = len(updates_data)
                    continue
        return [] 


    def add_file(self, file_path: str, *args):
        """Add a file to update.
    
            Parameters

                file_path : `str`
                    The path to the file to upload.
                args : `tuple`
                    The list of update fields to return.

            Returns

                asset : `moncli.entities.Asset`
                    The newly created asset.

            Return Fields

                created_at : `str`
                    The file's creation date.
                file_extension : `str`
                    The file's extension.
                file_size : `int`
                    The file's size in bytes.
                id : `str`
                    The file's unique identifier.
                name : `str`
                    The file's name.
                public_url : `str`
                    Public url to the asset, valid for 1 hour.
                uploaded_by : `moncli.entities.user.User`
                    The user who uploaded the file
                url : `str`
                    The user who uploaded the file
                url_thumbnail : `str`
                    Url to view the asset in thumbnail mode. Only available for images.  
        """

        asset_data = api.add_file_to_update(
            self.id,
            file_path,
            *args,
            api_key=self.__creds.api_key_v2)
        return en.Asset(**asset_data)


    def get_files(self, *args):
        """Get update's files.

            Parameters

                args : `tuple`
                    The list of asset fields to return.

            Returns

                assets : `list[moncli.entities.Asset]`
                    The update's files.

            Return Fields

                created_at : `str`
                    The file's creation date.
                file_extension : `str`
                    The file's extension.
                file_size : `int`
                    The file's size in bytes.
                id : `str`
                    The file's unique identifier.
                name : `str`
                    The file's name.
                public_url : `str`
                    Public url to the asset, valid for 1 hour.
                uploaded_by : `moncli.entities.user.User`
                    The user who uploaded the file
                url : `str`
                    The user who uploaded the file
                url_thumbnail : `str`
                    Url to view the asset in thumbnail mode. Only available for images.  
        """

         # Hard configure the pagination rate.
        page = 1
        page_limit = 500
        record_count = 500

        while record_count >= page_limit:
            updates_data = api.get_updates(
                'id', 
                *api.get_field_list(api.DEFAULT_ASSET_QUERY_FIELDS, 'assets', *args),
                api_key=self.__creds.api_key_v2, 
                limit=page_limit,
                page=page)
            
            try:
                target_update = [update for update in updates_data if update['id'] == self.id][0]
                return [en.Asset(creds=self.__creds, **asset_data) for asset_data in target_update['assets']]
            except KeyError:
                if len(target_update) == 0:
                    page += 1
                    record_count = len(updates_data)
                    continue
        return []   


    def delete(self, *args):
        """Delete the selected update.
    
            Parameters

                args : `tuple`
                    The list of update fields to return.
        
            Returns

                update : `moncli.entities.Update`
                    The deleted update.
        
            Return Fields

                assets : `list[moncli.entities.asset.Asset]`
                    The update's assets/files.
                body: `str`
                    The update's html formatted body.
                created_at: `str`
                    The update's creation date.
                creator : `moncli.entities.user.User`
                    The update's creator
                creator_id : `str`
                    The unique identifier of the update creator.
                id : `str`
                    The update's unique identifier.
                item_id : `str`
                    The update's item ID.
                replies : `list[moncli.entities.reply.Reply]
                    The update's replies.
                text_body : `str`
                    The update's text body.
                updated_at : `str`
                    The update's last edit date.
        """

        update_data = api.delete_update(
            self.id,
            *args,
            api_key=self.__creds.api_key_v2)
        return Update(creds=self.__creds, **update_data)


class _Reply(Model):
    """Reply base model"""
    id = types.StringType(required=True)
    creator_id = types.StringType(required=True)
    body = types.StringType()
    created_at = types.StringType()
    text_body = types.StringType()
    updated_at = types.StringType()


class Reply(_Reply):
    """A reply for an update

        Properties

            body : `str`
                The reply's html formatted body.
            created_at : `str`
                The reply's creation date.
            creator : `moncli.entities.User`
                The reply's creator.
            creator_id : `str`
                The unique identifier of the reply creator.
            id : `str`
                The reply's unique identifier.
            text_body : `str`
                The reply's text body.
            updated_at : `str`
                The reply's last edit date.

        Methods

            get_creator : `moncli.entities.User`
                Get the reply's creator.
    """

    def __init__(self, **kwargs):
        self.__creds = kwargs.pop('creds')
        self.__item_id = kwargs.pop('item_id')
        self.__creator = None
        super(Reply, self).__init__(kwargs)

    def __repr__(self):
        return str(self.to_primitive())

    @property
    def creator(self):
        """The reply's creator."""
        if not self.__creator:
            self.__creator = self.get_creator()
        return self.__creator
    
    def get_creator(self, *args):
        """Get the reply's creator.

            Parameters

                args : `tuple`
                    List of user return fields.

            Returns
            
                creator : `moncli.entities.User`
                    The creator of this update.

            Return Fields

                account : `moncli.entities.Account`
                    The user's account.
                birthday : `str`
                    The user's birthday.
                country_code : `str`
                    The user's country code.
                created_at : `str`
                    The user's creation date.
                email : `str`
                    The user's email.
                enabled : `bool`
                    Is the user enabled or not.
                id : `str`
                    The user's unique identifier.
                is_admin: `bool`
                    Is the user a admin or not.
                is_guest : `bool`
                    Is the user a guest or not.
                is_pending : `bool`
                    Is the user a pending user.
                is_view_only : `bool`
                    Is the user a view only user or not.
                join_date : `str`
                    The date the user joined the account.
                location : `str`
                    The user' location.
                mobile_phone : `str`
                    The user's mobile phone number.
                name : `str`
                    The user's name.
                phone : `str`
                    The user's phone number.
                photo_original : `str`
                    The user's photo in the original size.
                photo_small : `str`
                    The user's photo in small size (150x150).
                photo_thumb : `str`
                    The user's photo in thumbnail size (100x100).
                photo_thumb_small : `str`
                    The user's photo in small thumbnail size (50x50).
                photo_tiny : `str`
                    The user's photo in tiny size (30x30).
                teams : `list[moncli.entities.Team]`
                    The teams the user is a member in.
                time_zone_identifier : `str`
                    The user's time zone identifier.
                title : `str`
                    The user's title.
                url : `str`
                    The user's profile url.
                utc_hours_diff : `int`
                    The user's UTC hours difference.
        """

        user_data = api.get_users(
            *args,
            api_key=self.__creds.api_key_v2,
            ids=[int(self.creator_id)])[0]
        return en.User(creds=self.__creds, **user_data)
