import abc
from typing import List, Dict, Any, Generator

from rest_framework.fields import Field

from baserow.core.registry import (
    Instance,
    Registry,
)


class RowMetadataRegistry(Registry):
    """
    Contains all the types of metadata that can be attached to individual rows in
    Baserow.
    """

    name = "row_metadata"

    def generate_and_merge_metadata_for_row(self, table, row_id: int) -> Dict[str, Any]:
        """
        Alternative for generate_and_merge_metadata_for_rows which takes a single row
        id.
        """

        return self.generate_and_merge_metadata_for_rows(
            table, (i for i in [row_id])
        ).get(row_id, {})

    def generate_and_merge_metadata_for_rows(
        self, table, row_ids: Generator[int, None, None]
    ) -> Dict[int, Dict[str, Any]]:
        """
        For every type of row metadata will generate that type of metadata for each
        provided row in the row_ids generator. Then returns a dictionary keyed by the
        row id with the value being a dictionary of metadata. The dictionary of metadata
        is keyed by the particular instance's RowMetadataType.type with the value being
        whatever was returned by that instances generate_metadata_for_rows for that
        row id.

        For example if two different RowMetadata types 'A' and 'B" were registered,
        and they returned {'1': 10, '2': 2} and {'1': False, '2': True} from their
        respective generate_metadata_for_rows methods. This function would then return
        a dict which looked like: {'1': {'A':10, 'B':False}, '2': {'A':2, 'B':True}}.

        :param table: The table containing the row_ids.
        :param row_ids: A generator which should return the row ids generate metadata
            for. If no RowMetadataTypes are registered then this generator will not be
            invoked.
        :return: A dictionary of Row Id -> RowMetadataType -> Metadata Value if
            metadata types are registered, otherwise an empty dict.
        """

        metadata_types = self.get_all()
        if len(metadata_types) > 0:
            row_ids = list(row_ids)
            row_metadata = {}
            for metadata_type in metadata_types:
                per_row_metadata = metadata_type.generate_metadata_for_rows(
                    table, row_ids
                )
                for row_id, metadata in per_row_metadata.items():
                    single_row_metadata = row_metadata.setdefault(row_id, {})
                    single_row_metadata[metadata_type.type] = metadata
            return row_metadata
        else:
            return {}


class RowMetadataType(Instance, abc.ABC):
    """
    Registering a RowMetadataType allows you to attach extra data on a per row basis
    to rows returned by the Baserow API. This metadata can then be accessed via views
    in the frontend code. This metadata is not explicitly stored on its own but instead
    should be retrievable in a single quick query run by generate_metadata_for_rows
    given a list of rows to get metadata for.

    Metadata on each row will be found under the key of this Types 'type' attribute.
    See RowMetadataRegistry.generate_and_merge_metadata_for_rows for more details.
    """

    @abc.abstractmethod
    def generate_metadata_for_rows(self, table, row_ids: List[int]) -> Dict[int, Any]:
        """
        Given a table and a list of row ids to generate metadata for this function
        should be overridden and implemented to return a dictionary of row id to the
        metadata you wish to attach to that row.

        :param table: The table containing the row_ids.
        :param row_ids: A list of the row ids to generate metadata for.
        :return: A dictionary of Row Id -> Metadata Value.
        """

        pass

    @abc.abstractmethod
    def get_example_serializer_field(self) -> Field:
        """
        Used to construct the rest api documentation. Should be a serializer field which
        represents the values in the dict returned by generate_metadata_for_rows.

        :return: A drf field with help_text and other example data set for use by the
            api documentation.
        """

        pass


row_metadata_registry = RowMetadataRegistry()

import abc
from typing import List, Dict, Any, Generator

from rest_framework.fields import Field

from baserow.core.registry import (
    Instance,
    Registry,
)


class RowMetadataRegistry(Registry):
    """
    Contains all the types of metadata that can be attached to individual rows in
    Baserow.
    """

    name = "row_metadata"

    def generate_and_merge_metadata_for_row(self, table, row_id: int) -> Dict[str, Any]:
        """
        Alternative for generate_and_merge_metadata_for_rows which takes a single row
        id.
        """

        return self.generate_and_merge_metadata_for_rows(
            table, (i for i in [row_id])
        ).get(row_id, {})

    def generate_and_merge_metadata_for_rows(
        self, table, row_ids: Generator[int, None, None]
    ) -> Dict[int, Dict[str, Any]]:
        """
        For every type of row metadata will generate that type of metadata for each
        provided row in the row_ids generator. Then returns a dictionary keyed by the
        row id with the value being a dictionary of metadata. The dictionary of metadata
        is keyed by the particular instance's RowMetadataType.type with the value being
        whatever was returned by that instances generate_metadata_for_rows for that
        row id.

        For example if two different RowMetadata types 'A' and 'B" were registered,
        and they returned {'1': 10, '2': 2} and {'1': False, '2': True} from their
        respective generate_metadata_for_rows methods. This function would then return
        a dict which looked like: {'1': {'A':10, 'B':False}, '2': {'A':2, 'B':True}}.

        :param table: The table containing the row_ids.
        :param row_ids: A generator which should return the row ids generate metadata
            for. If no RowMetadataTypes are registered then this generator will not be
            invoked.
        :return: A dictionary of Row Id -> RowMetadataType -> Metadata Value if
            metadata types are registered, otherwise an empty dict.
        """

        metadata_types = self.get_all()
        if len(metadata_types) > 0:
            row_ids = list(row_ids)
            row_metadata = {}
            for metadata_type in metadata_types:
                per_row_metadata = metadata_type.generate_metadata_for_rows(
                    table, row_ids
                )
                for row_id, metadata in per_row_metadata.items():
                    single_row_metadata = row_metadata.setdefault(row_id, {})
                    single_row_metadata[metadata_type.type] = metadata
            return row_metadata
        else:
            return {}


class RowMetadataType(Instance, abc.ABC):
    """
    Registering a RowMetadataType allows you to attach extra data on a per row basis
    to rows returned by the Baserow API. This metadata can then be accessed via views
    in the frontend code. This metadata is not explicitly stored on its own but instead
    should be retrievable in a single quick query run by generate_metadata_for_rows
    given a list of rows to get metadata for.

    Metadata on each row will be found under the key of this Types 'type' attribute.
    See RowMetadataRegistry.generate_and_merge_metadata_for_rows for more details.
    """

    @abc.abstractmethod
    def generate_metadata_for_rows(self, table, row_ids: List[int]) -> Dict[int, Any]:
        """
        Given a table and a list of row ids to generate metadata for this function
        should be overridden and implemented to return a dictionary of row id to the
        metadata you wish to attach to that row.

        :param table: The table containing the row_ids.
        :param row_ids: A list of the row ids to generate metadata for.
        :return: A dictionary of Row Id -> Metadata Value.
        """

        pass

    @abc.abstractmethod
    def get_example_serializer_field(self) -> Field:
        """
        Used to construct the rest api documentation. Should be a serializer field which
        represents the values in the dict returned by generate_metadata_for_rows.

        :return: A drf field with help_text and other example data set for use by the
            api documentation.
        """

        pass


row_metadata_registry = RowMetadataRegistry()

Contains all the types of metadata that can be attached to individual rows in     Baserow. Alternative for generate_and_merge_metadata_for_rows which takes a single row         id. For every type of row metadata will generate that type of metadata for each         provided row in the row_ids generator. Then returns a dictionary keyed by the         row id with the value being a dictionary of metadata. The dictionary of metadata         is keyed by the particular instance's RowMetadataType.type with the value being         whatever was returned by that instances generate_metadata_for_rows for that         row id.          For example if two different RowMetadata types 'A' and 'B" were registered,         and they returned {'1': 10, '2': 2} and {'1': False, '2': True} from their         respective generate_metadata_for_rows methods. This function would then return         a dict which looked like: {'1': {'A':10, 'B':False}, '2': {'A':2, 'B':True}}.          :param table: The table containing the row_ids.         :param row_ids: A generator which should return the row ids generate metadata             for. If no RowMetadataTypes are registered then this generator will not be             invoked.         :return: A dictionary of Row Id -> RowMetadataType -> Metadata Value if             metadata types are registered, otherwise an empty dict. Registering a RowMetadataType allows you to attach extra data on a per row basis     to rows returned by the Baserow API. This metadata can then be accessed via views     in the frontend code. This metadata is not explicitly stored on its own but instead     should be retrievable in a single quick query run by generate_metadata_for_rows     given a list of rows to get metadata for.      Metadata on each row will be found under the key of this Types 'type' attribute.     See RowMetadataRegistry.generate_and_merge_metadata_for_rows for more details. Given a table and a list of row ids to generate metadata for this function         should be overridden and implemented to return a dictionary of row id to the         metadata you wish to attach to that row.          :param table: The table containing the row_ids.         :param row_ids: A list of the row ids to generate metadata for.         :return: A dictionary of Row Id -> Metadata Value. Used to construct the rest api documentation. Should be a serializer field which         represents the values in the dict returned by generate_metadata_for_rows.          :return: A drf field with help_text and other example data set for use by the             api documentation.