# -*- coding: utf-8 -*-
from __future__ import annotations

import datetime
import uuid

from hotpocket_backend.apps.saves.services import (
    AssociationsService as BackendAssociationsService,
)
from hotpocket_soa.dto.associations import (
    AssociationOut,
    AssociationsQuery,
    AssociationUpdateIn,
    AssociationWithTargetOut,
)
from hotpocket_soa.dto.saves import SaveOut

from .base import ProxyService, SOAError


class AssociationsService(ProxyService):
    class AssociationsServiceError(SOAError):
        pass

    class AssociationNotFound(AssociationsServiceError):
        pass

    class AssociationAccessDenied(AssociationsServiceError):
        pass

    def __init__(self):
        super().__init__()
        self.backend_associations_service = BackendAssociationsService()

    def wrap_exception(self, exception: Exception) -> Exception:
        new_exception_args = []
        if len(exception.args) > 0:
            new_exception_args = [exception.args[0]]

        return self.AssociationsServiceError(*new_exception_args)

    def create(self,
               *,
               account_uuid: uuid.UUID,
               target: SaveOut,
               pk: uuid.UUID | None = None,
               created_at: datetime.datetime | None = None,
               ) -> AssociationOut:
        return AssociationOut.model_validate(
            self.call(
                self.backend_associations_service,
                'create',
                account_uuid=account_uuid,
                save_uuid=target.pk,
                pk=pk,
                created_at=created_at,
            ),
            from_attributes=True,
        )

    def get(self,
            *,
            account_uuid: uuid.UUID | None,
            pk: uuid.UUID,
            with_target: bool = False,
            allow_archived: bool = False,
            ) -> AssociationOut | AssociationWithTargetOut:
        try:
            model: type[AssociationOut] = AssociationOut
            if with_target is True:
                model = AssociationWithTargetOut

            result = model.model_validate(
                self.call(
                    self.backend_associations_service,
                    'get',
                    pk=pk,
                    with_target=with_target,
                ),
                from_attributes=True,
            )

            if allow_archived is False and result.archived_at is not None:
                raise self.AssociationNotFound(f'pk=`{pk}`')

            if account_uuid is not None and result.account_uuid != account_uuid:
                raise self.AssociationAccessDenied(
                    f'account_uuid=`{account_uuid}` pk=`{pk}`',
                )

            return result
        except SOAError as exception:
            if isinstance(exception.__cause__, BackendAssociationsService.AssociationNotFound) is True:
                raise self.AssociationNotFound(*exception.args) from exception
            else:
                raise

    def search(self,
               *,
               query: AssociationsQuery,
               limit: int,
               ) -> list[AssociationWithTargetOut]:
        return [
            AssociationWithTargetOut.model_validate(row, from_attributes=True)
            for row
            in self.call(
                self.backend_associations_service,
                'search',
                query=query,
                limit=limit,
            )
        ]

    def archive(self, *, association: AssociationOut) -> bool:
        return self.call(
            self.backend_associations_service,
            'archive',
            pk=association.pk,
        )

    def star(self, *, association: AssociationOut) -> AssociationOut:
        return AssociationOut.model_validate(
            self.call(
                self.backend_associations_service,
                'star',
                pk=association.pk,
            ),
            from_attributes=True,
        )

    def unstar(self, *, association: AssociationOut) -> AssociationOut:
        return AssociationOut.model_validate(
            self.call(
                self.backend_associations_service,
                'unstar',
                pk=association.pk,
            ),
            from_attributes=True,
        )

    def update(self,
               *,
               association: AssociationOut,
               update: AssociationUpdateIn,
               ) -> AssociationOut:
        return AssociationOut.model_validate(
            self.call(
                self.backend_associations_service,
                'update',
                pk=association.pk,
                update=update,
            ),
            from_attributes=True,
        )

    def delete(self, *, association: AssociationOut) -> bool:
        return self.call(
            self.backend_associations_service,
            'delete',
            pk=association.pk,
        )
