import typing as tp

from nile.api.v1 import (
    extractors as ne,
    stream as nstream,
)

from .common import convert_bytes_to_str
from .constants import MEMORY_LIMIT


def prepare_nmaps_data(
    nmaps_feedback_db_table: nstream.Stream,
    nmaps_from_fbapi_feedback_table: nstream.Stream
) -> nstream.Stream:
    def extract_fbapi_id(fbapi_data: tp.Dict[bytes, tp.Any]) -> str:
        return fbapi_data[b"id"].decode("utf-8")

    fbapi_nmaps_ids_mapping = nmaps_from_fbapi_feedback_table.project(
        nmaps_id="id",
        id=ne.custom(extract_fbapi_id, "fbapi_data").with_type(str),
    )

    return nmaps_feedback_db_table.project(
        "commit_ids",
        nmaps_id="id",
        reject_reason=ne.custom(convert_bytes_to_str, "reject_reason").with_type(str),
        nmaps_type="type",
    ).join(
        fbapi_nmaps_ids_mapping,
        by="nmaps_id",
        type="inner",
        memory_limit=MEMORY_LIMIT,
    )


def join_sprav_and_nmaps_types(sprav_type: str, nmaps_type: tp.Optional[str]) -> str:
    if nmaps_type is not None:
        return nmaps_type
    return sprav_type


def join_nmaps_data(
    feedback_table: nstream.Stream,
    nmaps_feedback_db_table: nstream.Stream,
    nmaps_from_fbapi_feedback_table: nstream.Stream
) -> None:
    prepared_nmaps_table: nstream.Stream = prepare_nmaps_data(
        nmaps_feedback_db_table,
        nmaps_from_fbapi_feedback_table
    ).label("prepared_nmaps_table")

    return feedback_table.join(
        prepared_nmaps_table,
        by="id",
        type="left",
        memory_limit=MEMORY_LIMIT
    ).project(
        ne.all(exclude=["type", "nmaps_type"]),
        type=ne.custom(join_sprav_and_nmaps_types, "type", "nmaps_type").with_type(str),
    )
