"""Client for storing events into the events DynamoDB table."""

from __future__ import absolute_import

import base64
import logging

import boto3
from boto3.dynamodb import types

from . import errors


logger = logging.getLogger(__name__)

MAX_ITEM_BYTES = 400 * 1024


class EventStore(object):
    """DynamoDB table where Event protobufs are stored.

    Args:
      event_table: string name of the DynamoDB table where events are stored
      large_event_store: LargeEventStore object used to store large events.
      region: string name of the AWS region where the table is located

    Properties:
      table: boto3 dynamodb Table resource
    """

    def __init__(self, event_table, large_event_store, region='us-west-2'):
        self.event_table = event_table
        self.dynamodb = boto3.resource('dynamodb', region_name=region)
        self._table = None
        self.large_event_store = large_event_store

    @property
    def table(self):
        if not self._table:
            self._table = self.dynamodb.Table(self.event_table)
        return self._table

    def PutEvents(self, events):
        """Store iterable of events into the datastore."""
        large_events = []
        events_in_batch = 0
        with self.table.batch_writer() as batch:
            for event in events:
                # TODO: use ConditionExpression to prevent overwrite
                encoded_uuid = base64.b64encode(event.uuid)
                logger.info('Putting item with uuid: %s', encoded_uuid)
                try:
                    item = self._MakeItem(event)
                    batch.put_item(Item=item)
                    events_in_batch += 1
                except errors.EventStoreItemTooLarge:
                    logger.info('Item too large: %s', encoded_uuid)
                    large_events.append(event)
        logger.info('Successfully wrote %d items', events_in_batch)
        for event in large_events:
            # TODO: store event with field indicating where event body is.
            self.large_event_store.PutEvent(event)

    def _MakeItem(self, event):
        item = {
            'uuid': types.Binary(event.uuid),
            'timestamp': types.Decimal(event.timestamp),
            'type': event.type,
            'body': types.Binary(event.SerializeToString()),
            'attributes': dict(
              (a.key, a.value) for a in event.attributes
            ),
        }
        size = 0
        for name, value in item.iteritems():
            size += len(name) + len(str(value))
        if size > MAX_ITEM_BYTES:
            raise errors.EventStoreItemTooLarge()
        return item
