from typing import Iterable, List, Optional

from benchling_api_client.api.containers import (
    archive_containers,
    bulk_create_containers,
    bulk_get_containers,
    bulk_update_containers,
    checkin_containers,
    checkout_containers,
    create_container,
    delete_container_content,
    get_container,
    get_container_content,
    list_container_contents,
    list_containers,
    print_labels,
    reserve_containers,
    transfer_into_container,
    transfer_into_containers,
    unarchive_containers,
    update_container,
    update_container_content,
)
from benchling_api_client.models.async_task_link import AsyncTaskLink
from benchling_api_client.models.checkout_status import CheckoutStatus
from benchling_api_client.models.container import Container
from benchling_api_client.models.container_archive_request import ContainerArchiveRequest
from benchling_api_client.models.container_archive_response import ContainerArchiveResponse
from benchling_api_client.models.container_bulk_create_request_body import ContainerBulkCreateRequestBody
from benchling_api_client.models.container_bulk_update_item import ContainerBulkUpdateItem
from benchling_api_client.models.container_bulk_update_request_body import ContainerBulkUpdateRequestBody
from benchling_api_client.models.container_checkin_request import ContainerCheckinRequest
from benchling_api_client.models.container_checkout_request import ContainerCheckoutRequest
from benchling_api_client.models.container_content import ContainerContent
from benchling_api_client.models.container_content_update import ContainerContentUpdate
from benchling_api_client.models.container_create_request import ContainerCreateRequest
from benchling_api_client.models.container_transfer_request import ContainerTransferRequest
from benchling_api_client.models.container_unarchive_request import ContainerUnarchiveRequest
from benchling_api_client.models.container_update_request import ContainerUpdateRequest
from benchling_api_client.models.containers_transfers_request import ContainersTransfersRequest
from benchling_api_client.models.measurement import Measurement
from benchling_api_client.models.multiple_containers_transfer_request import MultipleContainersTransferRequest
from benchling_api_client.models.paginated_containers_list import PaginatedContainersList
from benchling_api_client.models.print_labels_request import PrintLabelsRequest
from benchling_api_client.models.reason import Reason
from benchling_api_client.models.sort1 import Sort1
from benchling_api_client.types import Response

from benchling_sdk.errors import raise_for_status
from benchling_sdk.helpers.decorators import api_method
from benchling_sdk.helpers.pagination_helpers import NextToken, PageIterator
from benchling_sdk.helpers.response_helpers import model_from_detailed
from benchling_sdk.helpers.serialization_helpers import optional_array_query_param
from benchling_sdk.services.base_service import BaseService


class ContainerService(BaseService):
    @api_method
    def get_by_id(self, container_id: str) -> Container:
        response = get_container.sync_detailed(client=self.client, container_id=container_id)
        return model_from_detailed(response)

    @api_method
    def containers_page(
        self,
        *,
        page_size: Optional[int] = 50,
        next_token: Optional[str] = None,
        sort: Optional[Sort1] = None,
        schema_id: Optional[str] = None,
        modified_at: Optional[str] = None,
        name: Optional[str] = None,
        ancestor_storage_id: Optional[str] = None,
        storage_contents_id: Optional[str] = None,
        storage_contents_ids: Optional[List[str]] = None,
        parent_storage_schema_id: Optional[str] = None,
        checkout_status: Optional[CheckoutStatus] = None,
        ids: Optional[str] = None,
        barcodes: Optional[str] = None,
        archive_reason: Optional[str] = None,
    ) -> Response[PaginatedContainersList]:
        return list_containers.sync_detailed(  # type: ignore
            client=self.client,
            sort=sort,
            schema_id=schema_id,
            modified_at=modified_at,
            name=name,
            ancestor_storage_id=ancestor_storage_id,
            storage_contents_id=storage_contents_id,
            storage_contents_ids=storage_contents_ids,
            parent_storage_schema_id=parent_storage_schema_id,
            checkout_status=checkout_status,
            ids=ids,
            barcodes=barcodes,
            archive_reason=archive_reason,
            next_token=next_token,
            page_size=page_size,
        )

    def list(
        self,
        *,
        sort: Optional[Sort1] = None,
        schema_id: Optional[str] = None,
        modified_at: Optional[str] = None,
        name: Optional[str] = None,
        ancestor_storage_id: Optional[str] = None,
        storage_contents_id: Optional[str] = None,
        storage_contents_ids: Optional[List[str]] = None,
        parent_storage_schema_id: Optional[str] = None,
        checkout_status: Optional[CheckoutStatus] = None,
        ids: Optional[str] = None,
        barcodes: Optional[str] = None,
        archive_reason: Optional[str] = None,
        page_size: Optional[int] = None,
    ) -> PageIterator[Container]:
        def api_call(next_token: NextToken) -> Response[PaginatedContainersList]:
            return self.containers_page(
                sort=sort,
                schema_id=schema_id,
                modified_at=modified_at,
                name=name,
                ancestor_storage_id=ancestor_storage_id,
                storage_contents_id=storage_contents_id,
                storage_contents_ids=storage_contents_ids,
                parent_storage_schema_id=parent_storage_schema_id,
                checkout_status=checkout_status,
                ids=ids,
                barcodes=barcodes,
                archive_reason=archive_reason,
                next_token=next_token,
                page_size=page_size,
            )

        def results_extractor(body: PaginatedContainersList) -> Optional[List[Container]]:
            return body.containers

        return PageIterator(api_call, results_extractor)

    @api_method
    def bulk_get(
        self, *, container_ids: Optional[Iterable[str]] = None, barcodes: Optional[Iterable[str]] = None
    ) -> Optional[List[Container]]:
        container_id_string = optional_array_query_param(container_ids)
        barcode_string = optional_array_query_param(barcodes)
        response = bulk_get_containers.sync_detailed(
            client=self.client, container_ids=container_id_string, barcodes=barcode_string
        )
        containers_list = model_from_detailed(response)
        return containers_list.containers

    @api_method
    def bulk_create(self, containers: Iterable[ContainerCreateRequest]) -> AsyncTaskLink:
        body = ContainerBulkCreateRequestBody(list(containers))
        response = bulk_create_containers.sync_detailed(client=self.client, json_body=body)
        return model_from_detailed(response)

    @api_method
    def bulk_update(self, containers: Iterable[ContainerBulkUpdateItem]) -> AsyncTaskLink:
        body = ContainerBulkUpdateRequestBody(list(containers))
        response = bulk_update_containers.sync_detailed(client=self.client, json_body=body)
        return model_from_detailed(response)

    @api_method
    def create(self, container: ContainerCreateRequest) -> Container:
        response = create_container.sync_detailed(client=self.client, json_body=container)
        return model_from_detailed(response)

    @api_method
    def update(self, container_id: str, container: ContainerUpdateRequest) -> Container:
        response = update_container.sync_detailed(
            client=self.client, container_id=container_id, json_body=container
        )
        return model_from_detailed(response)

    @api_method
    def archive(
        self, container_ids: Iterable[str], reason: Reason, should_remove_barcodes: bool
    ) -> ContainerArchiveResponse:
        archive_request = ContainerArchiveRequest(
            container_ids=list(container_ids), reason=reason, should_remove_barcodes=should_remove_barcodes
        )
        response = archive_containers.sync_detailed(client=self.client, json_body=archive_request)
        return model_from_detailed(response)

    @api_method
    def unarchive(self, container_ids: Iterable[str]) -> ContainerArchiveResponse:
        unarchive_request = ContainerUnarchiveRequest(container_ids=list(container_ids))
        response = unarchive_containers.sync_detailed(client=self.client, json_body=unarchive_request)
        return model_from_detailed(response)

    @api_method
    def print_labels(self, print_request: PrintLabelsRequest) -> None:
        response = print_labels.sync_detailed(client=self.client, json_body=print_request)
        raise_for_status(response)

    @api_method
    def contents_by_id(self, container_id: str, containable_id: str) -> ContainerContent:
        response = get_container_content.sync_detailed(
            client=self.client, container_id=container_id, containable_id=containable_id
        )
        return model_from_detailed(response)

    @api_method
    def list_contents(self, container_id: str) -> List[ContainerContent]:
        response = list_container_contents.sync_detailed(client=self.client, container_id=container_id)
        contents_list = model_from_detailed(response)
        return contents_list.contents

    @api_method
    def update_contents(
        self, container_id: str, containable_id: str, concentration: Measurement
    ) -> ContainerContent:
        update = ContainerContentUpdate(concentration=concentration)
        response = update_container_content.sync_detailed(
            client=self.client, container_id=container_id, containable_id=containable_id, json_body=update
        )
        return model_from_detailed(response)

    @api_method
    def delete_contents(self, container_id: str, containable_id: str) -> None:
        response = delete_container_content.sync_detailed(
            client=self.client, container_id=container_id, containable_id=containable_id
        )
        raise_for_status(response)

    @api_method
    def reserve(self, reservation: ContainerCheckoutRequest) -> None:
        response = reserve_containers.sync_detailed(client=self.client, json_body=reservation)
        raise_for_status(response)

    @api_method
    def checkout(self, checkout: ContainerCheckoutRequest) -> None:
        response = checkout_containers.sync_detailed(client=self.client, json_body=checkout)
        raise_for_status(response)

    @api_method
    def checkin(self, checkin: ContainerCheckinRequest) -> None:
        response = checkin_containers.sync_detailed(client=self.client, json_body=checkin)
        raise_for_status(response)

    @api_method
    def transfer_into_container(
        self, destination_container_id: str, transfer_request: ContainerTransferRequest
    ) -> None:
        response = transfer_into_container.sync_detailed(
            client=self.client, destination_container_id=destination_container_id, json_body=transfer_request
        )
        raise_for_status(response)

    @api_method
    def transfer_into_containers(
        self, transfer_requests: Iterable[MultipleContainersTransferRequest]
    ) -> AsyncTaskLink:
        multiple_requests = ContainersTransfersRequest(transfers=list(transfer_requests))
        response = transfer_into_containers.sync_detailed(client=self.client, json_body=multiple_requests)
        return model_from_detailed(response)
