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

"""
The MIT License (MIT)

Copyright (c) 2015-present Noritem

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""

from .mixins import Hashable
from .utils import snowflake_time, parse_time
from .enums import (
    ScheduledEventEntityType,
    ScheduledEventStatus,
    ScheduledEventPrivacyLevel,
    try_enum,
)

__all__ = (
    'ScheduledEvent',
    'ScheduledEventUser',
)


class ScheduledEvent(Hashable):
    """Represents a guild scheduled event.

    Attributes
    ----------
    id: :class:`int`
        The scheduled event's ID.
    guild_id: :class:`int`
        The guild ID.
    channel_id: Optional[:class:`int`]
        The channel ID, or ``None`` if entity type is ``EXTERNAL``.
    creator_id: Optional[:class:`int`]
        The user ID who created the event.
    name: :class:`str`
        The name of the event.
    description: Optional[:class:`str`]
        The description.
    scheduled_start_time: :class:`datetime.datetime`
        When the event will start.
    scheduled_end_time: Optional[:class:`datetime.datetime`]
        When the event will end.
    privacy_level: :class:`ScheduledEventPrivacyLevel`
        The privacy level.
    status: :class:`ScheduledEventStatus`
        The status.
    entity_type: :class:`ScheduledEventEntityType`
        The entity type.
    entity_id: Optional[:class:`int`]
        The entity ID.
    entity_metadata: Optional[:class:`dict`]
        Additional metadata (e.g. location for external events).
    user_count: Optional[:class:`int`]
        The number of users subscribed.
    image: Optional[:class:`str`]
        The cover image hash.
    recurrence_rule: Optional[:class:`dict`]
        The recurrence rule data.
    """

    __slots__ = (
        '_state', 'id', 'guild_id', 'channel_id', 'creator_id', 'name',
        'description', 'scheduled_start_time', 'scheduled_end_time',
        'privacy_level', 'status', 'entity_type', 'entity_id',
        'entity_metadata', 'creator', 'user_count', 'image', 'recurrence_rule',
    )

    def __init__(self, *, state, data):
        self._state = state
        self._update(data)

    def _update(self, data):
        self.id = int(data['id'])
        self.guild_id = int(data['guild_id'])
        channel_id = data.get('channel_id')
        self.channel_id = int(channel_id) if channel_id else None
        creator_id = data.get('creator_id')
        self.creator_id = int(creator_id) if creator_id else None
        self.name = data['name']
        self.description = data.get('description')
        self.scheduled_start_time = parse_time(data.get('scheduled_start_time'))
        self.scheduled_end_time = parse_time(data.get('scheduled_end_time'))
        self.privacy_level = try_enum(ScheduledEventPrivacyLevel, data.get('privacy_level', 2))
        self.status = try_enum(ScheduledEventStatus, data.get('status', 1))
        self.entity_type = try_enum(ScheduledEventEntityType, data.get('entity_type', 1))
        entity_id = data.get('entity_id')
        self.entity_id = int(entity_id) if entity_id else None
        self.entity_metadata = data.get('entity_metadata')
        self.creator = data.get('creator')
        self.user_count = data.get('user_count')
        self.image = data.get('image')
        self.recurrence_rule = data.get('recurrence_rule')

    @property
    def created_at(self):
        """:class:`datetime.datetime`: Returns the event's creation time in UTC."""
        return snowflake_time(self.id)

    @property
    def location(self):
        """Optional[:class:`str`]: The location of an external event."""
        if self.entity_metadata:
            return self.entity_metadata.get('location')
        return None

    def __repr__(self):
        return '<ScheduledEvent id={0.id} name={0.name!r} guild_id={0.guild_id} status={0.status!r}>'.format(self)

    async def edit(self, *, reason=None, **fields):
        """|coro|

        Edits the scheduled event.

        Parameters
        ----------
        name: :class:`str`
            The new name.
        description: :class:`str`
            The new description.
        channel_id: Optional[:class:`int`]
            The new channel ID.
        scheduled_start_time: :class:`str`
            ISO8601 start time.
        scheduled_end_time: :class:`str`
            ISO8601 end time.
        privacy_level: :class:`ScheduledEventPrivacyLevel`
            The privacy level.
        entity_type: :class:`ScheduledEventEntityType`
            The entity type.
        status: :class:`ScheduledEventStatus`
            The event status.
        entity_metadata: :class:`dict`
            The entity metadata.
        image: :class:`str`
            The cover image (base64-encoded).
        reason: Optional[:class:`str`]
            The reason for editing.
        """
        payload = {}
        for key in ('name', 'description', 'channel_id', 'scheduled_start_time',
                     'scheduled_end_time', 'entity_metadata', 'image', 'recurrence_rule'):
            if key in fields:
                payload[key] = fields[key]

        if 'privacy_level' in fields:
            payload['privacy_level'] = fields['privacy_level'].value
        if 'entity_type' in fields:
            payload['entity_type'] = fields['entity_type'].value
        if 'status' in fields:
            payload['status'] = fields['status'].value

        data = await self._state.http.edit_scheduled_event(self.guild_id, self.id, reason=reason, **payload)
        self._update(data)

    async def delete(self):
        """|coro|

        Deletes the scheduled event.
        """
        await self._state.http.delete_scheduled_event(self.guild_id, self.id)

    async def fetch_users(self, *, limit=100, with_member=False, before=None, after=None):
        """|coro|

        Fetches users subscribed to this event.

        Returns
        -------
        List[:class:`dict`]
            A list of scheduled event user data.
        """
        return await self._state.http.get_scheduled_event_users(
            self.guild_id, self.id,
            limit=limit, with_member=with_member,
            before=before, after=after,
        )


class ScheduledEventUser:
    """Represents a user subscribed to a scheduled event.

    Attributes
    ----------
    guild_scheduled_event_id: :class:`int`
        The event ID.
    user: :class:`dict`
        The user data.
    member: Optional[:class:`dict`]
        The guild member data, if available.
    """

    __slots__ = ('guild_scheduled_event_id', 'user', 'member')

    def __init__(self, data):
        self.guild_scheduled_event_id = int(data['guild_scheduled_event_id'])
        self.user = data['user']
        self.member = data.get('member')

    def __repr__(self):
        return '<ScheduledEventUser event_id={0.guild_scheduled_event_id}>'.format(self)
