from uuid import uuid4

from aws_lambda_powertools import Logger
from aws_lambda_powertools.utilities.data_classes import (
    EventBridgeEvent,
    event_source,
)
from aws_lambda_powertools.utilities.typing import LambdaContext
from layercake.batch import BatchProcessor
from layercake.dateutils import now
from layercake.dynamodb import (
    DynamoDBPersistenceLayer,
    KeyChain,
    KeyPair,
    SortKey,
    TransactKey,
)

from boto3clients import dynamodb_client
from config import COURSE_TABLE, ENROLLMENT_TABLE, ORDER_TABLE
from enrollment import (
    Kind,
    LinkedEntity,
    enroll,
)
from schemas import Course, Enrollment, User

logger = Logger(__name__)
order_layer = DynamoDBPersistenceLayer(ORDER_TABLE, dynamodb_client)
course_layer = DynamoDBPersistenceLayer(COURSE_TABLE, dynamodb_client)
enrollment_layer = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client)
processor = BatchProcessor()


@event_source(data_class=EventBridgeEvent)
@logger.inject_lambda_context
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
    new_image = event.detail['new_image']
    order = order_layer.collection.get_items(
        TransactKey(new_image['id'])
        + SortKey('0')
        + SortKey('items', path_spec='items'),
    )
    items = {
        item['id']: int(item['quantity'])
        for item in order['items']
        # Ignore items with non-positive unit price;
        # negative values are treated as discounts
        if item['unit_price'] > 0
    }

    courses = _get_courses(set(items.keys()))
    user = User(
        id=order['user_id'],
        name=order['name'],
        email=order['email'],
        cpf=order['cpf'],
    )

    with processor(
        records=courses,
        handler=_handler,
        context={
            'user': user,
            'order_id': new_image['id'],
        },
    ) as fp:
        result = fp.process()
        logger.debug('Processed courses', result=result)

    return order_layer.update_item(
        key=KeyPair(new_image['id'], new_image['sk']),
        update_expr='SET #status = :status, updated_at = :updated_at',
        expr_attr_names={
            '#status': 'status',
        },
        expr_attr_values={
            ':status': 'SUCCESS',
            ':updated_at': now(),
        },
        cond_expr='attribute_exists(sk)',
    )


def _handler(record: Course, context: dict) -> Enrollment:
    enrollment = Enrollment(
        id=uuid4(),
        user=context['user'],
        course=record,
    )

    enroll(
        enrollment,
        persistence_layer=enrollment_layer,
        linked_entities=frozenset(
            {
                LinkedEntity(
                    id=context['order_id'],
                    kind=Kind.ORDER,
                    table_name=ORDER_TABLE,
                ),
            }
        ),
    )

    return enrollment


def _get_courses(ids: set) -> tuple[Course, ...]:
    pairs = tuple(KeyPair(idx, '0') for idx in ids)
    result = course_layer.collection.get_items(
        KeyChain(pairs),
        flatten_top=False,
    )
    return tuple(Course(id=idx, **obj) for idx, obj in result.items())  # type: ignore
