from typing import Optional


def gens_are_consistent(
    complex_phase,
    solvent_phase,
    ngens: Optional[int] = 2,
    nsigma: Optional[float] = 3,
) -> bool:
    """
        Return True if GENs are consistent.

        The last `ngens` generations will be checked for consistency with the overall estimate,
        and those with estimates that deviate by more than `nsigma` standard errors will be dropped.
    sprint-5-minimal-test.json
        Parameters
        ----------
        complex_phase : ProjectPair
            The complex phase ProjectPair object to use to check for consistency
        solvent_phase : ProjectPair
            The solvent phase ProjectPair object to use to check for consistency
        ngens : int, optional, default=2
            The last `ngens` generations will be checked for consistency with the overall estimate
        nsigma : int, optional, default=3
            Number of standard errors of overall estimate to use for consistency check
    """
    # Collect free energy estimates for each GEN
    ngens = min(len(complex_phase.gens), len(solvent_phase.gens))
    gen_estimates = list()
    for gen in range(ngens):
        complex_delta_f = complex_phase.gens[gen].free_energy.delta_f
        solvent_delta_f = solvent_phase.gens[gen].free_energy.delta_f
        if (complex_delta_f is None) or (solvent_delta_f is None):
            continue
        binding_delta_f = complex_delta_f - solvent_delta_f
        gen_estimates.append(binding_delta_f)

    if len(gen_estimates) < ngens:
        # We don't have enough GENs
        return False

    # Flag inconsistent if any GEN estimate is more than nsigma stderrs away from overall estimate
    for gen_delta_f in gen_estimates[-ngens:]:
        overall_delta_f = (
            complex_phase.free_energy.delta_f - solvent_phase.free_energy.delta_f
        )
        delta_f = overall_delta_f - gen_delta_f
        # print(gen_delta_f, overall_delta_f, delta_f)
        # if abs(delta_f.point) > nsigma*delta_f.stderr:
        if abs(delta_f.point) > nsigma * gen_delta_f.stderr:
            return False

    return True