"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from __future__ import annotations
from ascend_sdk import utils
from ascend_sdk.types import (
    BaseModel,
    Nullable,
    OptionalNullable,
    UNSET,
    UNSET_SENTINEL,
)
from ascend_sdk.utils import validate_open_enum
from datetime import datetime
from enum import Enum
from pydantic import model_serializer
from pydantic.functional_validators import PlainValidator
from typing import Any, Dict, Optional
from typing_extensions import Annotated, NotRequired, TypedDict


class AchWithdrawalAmountTypedDict(TypedDict):
    r"""The amount to withdraw in USD. The value should not be set if the withdrawal is `full_disbursement`."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalAmount(BaseModel):
    r"""The amount to withdraw in USD. The value should not be set if the withdrawal is `full_disbursement`."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalRetirementDistributionAmountTypedDict(TypedDict):
    r"""Fixed USD amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalRetirementDistributionAmount(BaseModel):
    r"""Fixed USD amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalPercentageTypedDict(TypedDict):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalPercentage(BaseModel):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class FederalTaxWithholdingTypedDict(TypedDict):
    r"""The federal tax withholding."""

    amount: NotRequired[Nullable[AchWithdrawalRetirementDistributionAmountTypedDict]]
    r"""Fixed USD amount to withhold for taxes."""
    percentage: NotRequired[Nullable[AchWithdrawalPercentageTypedDict]]
    r"""Percentage of total disbursement amount to withhold for taxes."""


class FederalTaxWithholding(BaseModel):
    r"""The federal tax withholding."""

    amount: OptionalNullable[AchWithdrawalRetirementDistributionAmount] = UNSET
    r"""Fixed USD amount to withhold for taxes."""

    percentage: OptionalNullable[AchWithdrawalPercentage] = UNSET
    r"""Percentage of total disbursement amount to withhold for taxes."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["amount", "percentage"]
        nullable_fields = ["amount", "percentage"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class AchWithdrawalRetirementDistributionStateTaxWithholdingAmountTypedDict(TypedDict):
    r"""Fixed USD amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalRetirementDistributionStateTaxWithholdingAmount(BaseModel):
    r"""Fixed USD amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalRetirementDistributionPercentageTypedDict(TypedDict):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: NotRequired[str]
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class AchWithdrawalRetirementDistributionPercentage(BaseModel):
    r"""Percentage of total disbursement amount to withhold for taxes."""

    value: Optional[str] = None
    r"""The decimal value, as a string; Refer to [Google’s Decimal type protocol buffer](https://github.com/googleapis/googleapis/blob/40203ca1880849480bbff7b8715491060bbccdf1/google/type/decimal.proto#L33) for details"""


class StateTaxWithholdingTypedDict(TypedDict):
    r"""The state tax withholding."""

    amount: NotRequired[
        Nullable[AchWithdrawalRetirementDistributionStateTaxWithholdingAmountTypedDict]
    ]
    r"""Fixed USD amount to withhold for taxes."""
    percentage: NotRequired[
        Nullable[AchWithdrawalRetirementDistributionPercentageTypedDict]
    ]
    r"""Percentage of total disbursement amount to withhold for taxes."""


class StateTaxWithholding(BaseModel):
    r"""The state tax withholding."""

    amount: OptionalNullable[
        AchWithdrawalRetirementDistributionStateTaxWithholdingAmount
    ] = UNSET
    r"""Fixed USD amount to withhold for taxes."""

    percentage: OptionalNullable[AchWithdrawalRetirementDistributionPercentage] = UNSET
    r"""Percentage of total disbursement amount to withhold for taxes."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["amount", "percentage"]
        nullable_fields = ["amount", "percentage"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class AchWithdrawalType(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""The type of retirement distribution."""

    TYPE_UNSPECIFIED = "TYPE_UNSPECIFIED"
    NORMAL = "NORMAL"
    DISABILITY = "DISABILITY"
    SOSEPP = "SOSEPP"
    PREMATURE = "PREMATURE"
    DEATH = "DEATH"
    EXCESS_CONTRIBUTION_REMOVAL_BEFORE_TAX_DEADLINE = (
        "EXCESS_CONTRIBUTION_REMOVAL_BEFORE_TAX_DEADLINE"
    )
    EXCESS_CONTRIBUTION_REMOVAL_AFTER_TAX_DEADLINE = (
        "EXCESS_CONTRIBUTION_REMOVAL_AFTER_TAX_DEADLINE"
    )
    ROLLOVER_TO_QUALIFIED_PLAN = "ROLLOVER_TO_QUALIFIED_PLAN"
    ROLLOVER_TO_IRA = "ROLLOVER_TO_IRA"
    DISTRIBUTION_TRANSFER = "DISTRIBUTION_TRANSFER"
    RECHARACTERIZATION_PRIOR_YEAR = "RECHARACTERIZATION_PRIOR_YEAR"
    RECHARACTERIZATION_CURRENT_YEAR = "RECHARACTERIZATION_CURRENT_YEAR"
    DISTRIBUTION_CONVERSION = "DISTRIBUTION_CONVERSION"
    MANAGEMENT_FEE = "MANAGEMENT_FEE"
    PLAN_LOAN_401_K = "PLAN_LOAN_401K"
    PREMATURE_SIMPLE_IRA_LESS_THAN_2_YEARS = "PREMATURE_SIMPLE_IRA_LESS_THAN_2_YEARS"
    NORMAL_ROTH_IRA_GREATER_THAN_5_YEARS = "NORMAL_ROTH_IRA_GREATER_THAN_5_YEARS"


class RetirementDistributionTypedDict(TypedDict):
    r"""The retirement distribution details."""

    federal_tax_withholding: NotRequired[Nullable[FederalTaxWithholdingTypedDict]]
    r"""The federal tax withholding."""
    receiving_institution: NotRequired[str]
    r"""The institution receiving retirement funds when performing a transfer to an identical retirement account type at a different financial institution. This is required for check and wire withdrawals because we can't always identify the institution using the transfer instructions. For cash journals this value will default to \"Apex Clearing\", regardless of what is passed in here"""
    state_tax_withholding: NotRequired[Nullable[StateTaxWithholdingTypedDict]]
    r"""The state tax withholding."""
    state_withholding_waiver: NotRequired[bool]
    r"""Whether or not this distribution has a state withholding waiver."""
    tax_year: NotRequired[int]
    r"""Tax year for which the distribution is applied."""
    type: NotRequired[AchWithdrawalType]
    r"""The type of retirement distribution."""


class RetirementDistribution(BaseModel):
    r"""The retirement distribution details."""

    federal_tax_withholding: OptionalNullable[FederalTaxWithholding] = UNSET
    r"""The federal tax withholding."""

    receiving_institution: Optional[str] = None
    r"""The institution receiving retirement funds when performing a transfer to an identical retirement account type at a different financial institution. This is required for check and wire withdrawals because we can't always identify the institution using the transfer instructions. For cash journals this value will default to \"Apex Clearing\", regardless of what is passed in here"""

    state_tax_withholding: OptionalNullable[StateTaxWithholding] = UNSET
    r"""The state tax withholding."""

    state_withholding_waiver: Optional[bool] = None
    r"""Whether or not this distribution has a state withholding waiver."""

    tax_year: Optional[int] = None
    r"""Tax year for which the distribution is applied."""

    type: Annotated[
        Optional[AchWithdrawalType], PlainValidator(validate_open_enum(False))
    ] = None
    r"""The type of retirement distribution."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "federal_tax_withholding",
            "receiving_institution",
            "state_tax_withholding",
            "state_withholding_waiver",
            "tax_year",
            "type",
        ]
        nullable_fields = ["federal_tax_withholding", "state_tax_withholding"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class AchWithdrawalStateState(str, Enum, metaclass=utils.OpenEnumMeta):
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """

    STATE_UNSPECIFIED = "STATE_UNSPECIFIED"
    PROCESSING = "PROCESSING"
    PENDING_REVIEW = "PENDING_REVIEW"
    POSTED = "POSTED"
    COMPLETED = "COMPLETED"
    REJECTED = "REJECTED"
    CANCELED = "CANCELED"
    RETURNED = "RETURNED"
    POSTPONED = "POSTPONED"


class AchWithdrawalStateTypedDict(TypedDict):
    r"""The current state of the ACH withdrawal."""

    actor: NotRequired[str]
    r"""The user or service that triggered the state update."""
    message: NotRequired[str]
    r"""Additional description of the transfer state."""
    metadata: NotRequired[Nullable[Dict[str, Any]]]
    r"""Additional metadata relating to the transfer state. Included data depends on the state, e.g.:
    - Rejection reasons are included when the `state` is `REJECTED`
    - Reason and comment are included when `state` is `CANCELED`
    """
    state: NotRequired[AchWithdrawalStateState]
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """
    update_time: NotRequired[Nullable[datetime]]
    r"""The time of the state update."""


class AchWithdrawalState(BaseModel):
    r"""The current state of the ACH withdrawal."""

    actor: Optional[str] = None
    r"""The user or service that triggered the state update."""

    message: Optional[str] = None
    r"""Additional description of the transfer state."""

    metadata: OptionalNullable[Dict[str, Any]] = UNSET
    r"""Additional metadata relating to the transfer state. Included data depends on the state, e.g.:
    - Rejection reasons are included when the `state` is `REJECTED`
    - Reason and comment are included when `state` is `CANCELED`
    """

    state: Annotated[
        Optional[AchWithdrawalStateState], PlainValidator(validate_open_enum(False))
    ] = None
    r"""The high level state of a transfer, one of:
    - `PROCESSING` - The transfer is being processed and will be posted if successful.
    - `PENDING_REVIEW` - The transfer is pending review and will continue processing if approved.
    - `POSTED` - The transfer has been posted to the ledger and will be completed at the end of the processing window if not canceled first.
    - `COMPLETED` - The transfer has been batched and completed.
    - `REJECTED` - The transfer was rejected.
    - `CANCELED` - The transfer was canceled.
    - `RETURNED` - The transfer was returned.
    - `POSTPONED` - The transfer is postponed and will resume processing during the next processing window.
    """

    update_time: OptionalNullable[datetime] = UNSET
    r"""The time of the state update."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["actor", "message", "metadata", "state", "update_time"]
        nullable_fields = ["metadata", "update_time"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class AchWithdrawalTypedDict(TypedDict):
    r"""A withdrawal transfer using the ACH mechanism."""

    amount: NotRequired[Nullable[AchWithdrawalAmountTypedDict]]
    r"""The amount to withdraw in USD. The value should not be set if the withdrawal is `full_disbursement`."""
    bank_relationship: NotRequired[str]
    r"""The bank relationship to be used for the ACH withdrawal."""
    client_transfer_id: NotRequired[str]
    r"""The external identifier supplied by the API caller. Each request must have a unique pairing of `client_transfer_id` and `account`."""
    memo: NotRequired[str]
    r"""The memo that will appear on the customer's bank statement."""
    name: NotRequired[str]
    r"""The resource name of the ACH withdrawal."""
    retirement_distribution: NotRequired[Nullable[RetirementDistributionTypedDict]]
    r"""The retirement distribution details."""
    state: NotRequired[Nullable[AchWithdrawalStateTypedDict]]
    r"""The current state of the ACH withdrawal."""


class AchWithdrawal(BaseModel):
    r"""A withdrawal transfer using the ACH mechanism."""

    amount: OptionalNullable[AchWithdrawalAmount] = UNSET
    r"""The amount to withdraw in USD. The value should not be set if the withdrawal is `full_disbursement`."""

    bank_relationship: Optional[str] = None
    r"""The bank relationship to be used for the ACH withdrawal."""

    client_transfer_id: Optional[str] = None
    r"""The external identifier supplied by the API caller. Each request must have a unique pairing of `client_transfer_id` and `account`."""

    memo: Optional[str] = None
    r"""The memo that will appear on the customer's bank statement."""

    name: Optional[str] = None
    r"""The resource name of the ACH withdrawal."""

    retirement_distribution: OptionalNullable[RetirementDistribution] = UNSET
    r"""The retirement distribution details."""

    state: OptionalNullable[AchWithdrawalState] = UNSET
    r"""The current state of the ACH withdrawal."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "amount",
            "bank_relationship",
            "client_transfer_id",
            "memo",
            "name",
            "retirement_distribution",
            "state",
        ]
        nullable_fields = ["amount", "retirement_distribution", "state"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in self.model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m
