from entegywrapper.schemas.profile import (
    Profile,
    ProfileCreate,
    ProfileIdentifier,
    ProfileType,
    ProfileUpdate
)

from typing import Generator


def all_profiles(
    self,
    *,
    include_custom_fields: bool = False,
    include_permissions: bool = False,
    status: str | None = None,
    profile_type: ProfileType | None = None,
    updated_after: str | None = None,
    created_after: str | None = None
) -> Generator[list[Profile], None, None]:
    """
    Yields all user profiles in blocks generated by the Entegy API's pagination.

    Parameters
    ----------
        `include_custom_fields` (`bool`, optional): whether to include custom fields for each profile; defaults to `False`
        `include_permissions` (`bool`, optional): whether to include permissions for each profile; defaults to `False`
        `status` (`str | None`, optional): only select profiles with this status; defaults to `None`
        `profile_type` (`ProfileType | None`, only select profiles of this type): _description_; defaults to `None`
        `updated_after` (`str | None`, optional): only select profiles updated after this time; defaults to `None`
        `created_after` (`str | None`, optional): only select profiles created after this time; defaults to `None`

    Yields
    ------
        `Generator[list[Profile], None, None]`: paginated blocks of user profiles
    """
    data = {
        "pagination": {
            "start": 0,
            "limit": 1000
        },
        "includeCustomFields": include_custom_fields,
        "includePermissions": include_permissions
    }

    if status is not None:
        data["status"] = status
    if profile_type is not None:
        data["profileType"] = profile_type
    if updated_after is not None:
        data["updatedAfter"] = updated_after
    if created_after is not None:
        data["createdAfter"] = created_after

    response = self.post(
        self.api_endpoint + "/v2/Profile/All",
        data=data
    )
    yield response["profiles"]

    while response["pagination"]["start"] + response["pagination"]["limit"] \
            < response["pagination"]["count"]:
        data["pagination"]["start"] += data["pagination"]["limit"]

        response = self.post(
            self.api_endpoint + "/v2/Profile/All",
            data=data
        )
        yield response["profiles"]


def get_profile(
    self,
    *,
    profile_id: str | None = None,
    external_reference: str | None = None,
    badge_reference: str | None = None,
    internal_reference: str | None = None,
    include_custom_fields: bool = False,
    include_permissions: bool = False
) -> Profile:
    """
    Returns the user profile specified by the given identifier

    Parameters
    ----------
        `profile_id` (`str`, optional): the profileId of the profile to get; defaults to `None`
        `external_reference` (`str`, optional): the externalReference of the profile to get; defaults to `None`
        `badgeReference` (`str`, optional): the badgeReference of the profile to get; defaults to `None`
        `internalReference` (`str`, optional): the internalReference of the profile to get; defaults to `None`
        `include_custom_fields` (`bool`, optional): whether to include custom fields for each profile; defaults to `False`
        `include_permissions` (`bool`, optional): whether to include permissions for each profile; defaults to `False`

    Returns
    -------
        `Profile`: the user profile specified by the given identifier
    """
    data = {
        "profileId": profile_id,
        "includeCustomFields": include_custom_fields,
        "includePermissions": include_permissions
    }

    if external_reference is not None:
        data["externalReference"] = external_reference
    elif badge_reference is not None:
        data["badgeReference"] = badge_reference
    elif internal_reference is not None:
        data["internalReference"] = internal_reference

    response = self.post(
        self.api_endpoint + "/v2/Profile/",
        data=data
    )

    return response["profile"]


def delete_profile(self, profile_id: str):
    """
    Deletes the user profile specified by the given identifier.

    Parameters
    ----------
        `profile_id` (`str`): the profileId of the profile to delete
    """
    data = {
        "profileId": profile_id
    }

    self.delete(
        self.api_endpoint + "/v2/Profile/Delete",
        data=data
    )


def create_profile(self, profile_object: ProfileCreate) -> str:
    """
    Creates the given profile within the Entegy project, returning the newly
    created profileId.

    Parameters
    ----------
        `profile_object` (`ProfileCreate`): a profile object representing the profile to create

    Returns
    -------
        `str`: profileId of the newly created profile
    """
    data = {
        "profile": profile_object
    }

    response = self.post(
        self.api_endpoint + "/v2/Profile/Create",
        data=data
    )

    return response["profileId"]


def update_profile(self, profile_id: str, profile_object: ProfileUpdate):
    """
    Updates the user profile specified by the given identifier using the fields
    in the given profile object.

    Parameters
    ----------
        `profile_id` (`str`): the profileId of the profile to update
        `profile_object` (`ProfileUpdate`): the profile fields to update
    """
    data = {
        "profileId": profile_id,
        "profile": profile_object
    }

    self.post(
        self.api_endpoint + "/v2/Profile/Update",
        data=data
    )


def sync_profiles(
    self,
    update_reference_type: ProfileIdentifier,
    profiles: list[Profile],
    *,
    group_by_first_profile: bool = False
) -> list[dict[str, str | bool]]:
    """
    Updates or creates profiles in bulk.

    Parameters
    ----------
        `update_reference_type` (`Identifier`): the identifier to use to match profiles for updating
        `profiles` (`list[Profile]`): the list of profiles to create or update
        `group_by_first_profile` (`bool`, optional): whether the parent profile of all profiles in this sync should be set to the first profile in the profiles list (except the first profile itself, which will be set to have no parent); defaults to `False`

    Returns
    -------
        `list[dict]`: profileIds and `newProfile` flags for each profile in the given list
    """
    data = {
        "updateReferenceType": update_reference_type,
        "profiles": profiles,
        "groupByFirstProfile": group_by_first_profile
    }

    response = self.post(
        self.api_endpoint + "/v2/Profile/Sync",
        data=data
    )

    return response["results"]


def send_welcome_email(self, profile_id: str):
    """
    Re-sends the welcome email for a given profile on a given project.

    Parameters
    ----------
        `profile_id` (`str`): the profileId of the profile to update
    """
    data = {
        "profileId": profile_id
    }

    self.post(
        self.api_endpoint + "/v2/Profile/SendWelcomeEmail",
        data=data
    )
