from http import HTTPStatus
from typing import Annotated

from aws_lambda_powertools.event_handler.api_gateway import Router
from aws_lambda_powertools.event_handler.exceptions import (
    NotFoundError,
    ServiceError,
)
from aws_lambda_powertools.event_handler.openapi.params import Body
from layercake.dateutils import now, ttl
from layercake.dynamodb import (
    DynamoDBPersistenceLayer,
    KeyPair,
    SortKey,
    TransactKey,
)
from layercake.extra_types import CpfStr, NameStr

from api_gateway import JSONResponse
from boto3clients import dynamodb_client
from config import USER_TABLE

from .emails import router as emails
from .orgs import router as orgs
from .password import router as password

__all__ = ['emails', 'orgs', 'password']

router = Router()
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)


class UserNotFoundError(NotFoundError): ...


class CPFConflictError(ServiceError):
    def __init__(self, msg: str | dict):
        super().__init__(HTTPStatus.CONFLICT, msg)


class RateLimitExceededError(ServiceError):
    def __init__(self, msg: str | dict):
        super().__init__(HTTPStatus.TOO_MANY_REQUESTS, msg)


@router.get('/<user_id>')
def get_user(user_id: str):
    user = dyn.collection.get_items(
        TransactKey(user_id)
        + SortKey('0')
        + SortKey(
            sk='RATE_LIMIT_EXCEEDED',
            rename_key='rate_limit_exceeded',
        )
        + SortKey(
            sk='CREATED_BY',
            rename_key='created_by',
        ),
    )

    if not user:
        raise UserNotFoundError('User not found')

    return user


@router.patch('/<user_id>')
def update(
    user_id: str,
    name: Annotated[NameStr, Body(embed=True)],
    new_cpf: Annotated[CpfStr, Body(embed=True, alias='cpf')],
):
    now_ = now()
    old_cpf = dyn.collection.get_item(
        KeyPair(
            pk=user_id,
            sk=SortKey('0', path_spec='cpf'),
        ),
    )

    with dyn.transact_writer() as transact:
        transact.put(
            item={
                'id': user_id,
                'sk': 'RATE_LIMIT_EXCEEDED',
                'ttl': ttl(start_dt=now_, hours=24),
                'created_at': now_,
            },
            cond_expr='attribute_not_exists(sk)',
            exc_cls=RateLimitExceededError,
        )
        transact.update(
            key=KeyPair(user_id, '0'),
            update_expr='SET #name = :name, cpf = :cpf, updated_at = :now',
            expr_attr_names={
                '#name': 'name',
            },
            expr_attr_values={
                ':name': name,
                ':cpf': new_cpf,
                ':now': now_,
            },
            cond_expr='attribute_exists(sk)',
        )

        if old_cpf != new_cpf:
            transact.put(
                item={
                    'id': 'cpf',
                    'cpf': new_cpf,
                    'user_id': user_id,
                    'created_at': now_,
                },
                cond_expr='attribute_not_exists(sk)',
                exc_cls=CPFConflictError,
            )

            if old_cpf:
                transact.delete(key=KeyPair('cpf', old_cpf))

    return JSONResponse(status_code=HTTPStatus.OK)
