import warnings


def prep_manifest(manifest, max_cards, n_cvrs):
    """
    Prepare a Dominion Excel ballot manifest (read as a pandas dataframe) for sampling.
    The manifest may have cards that do not contain the contest, but every listed CVR
    is assumed to have a corresponding card in the manifest.
    
    If the number of cards in the manifest is less than the number of cards that might have been cast,
    max_cards, an additional batch of "phantom" cards is appended to the manifest to make up the difference.
    
    Parameters:
    ----------
    manifest : dataframe
        should contain the columns
           'Tray #', 'Tabulator Number', 'Batch Number', 'Total Ballots', 'VBMCart.Cart number'
    max_cards : int
        upper bound on the number of cards cast
    n_cvrs : int
        number of CVRs
        
    Returns:
    --------
    manifest : dataframe
        original manifest with additional column for cumulative cards and, if needed, an additional batch for any phantom cards   
    manifest_cards : int
        the total number of cards in the manifest
    phantoms : int
        the number of phantom cards required
    """
    cols = ['Tray #', 'Tabulator Number', 'Batch Number', 'Total Ballots', 'VBMCart.Cart number']
    assert set(cols).issubset(manifest.columns), "missing columns"
    manifest_cards = manifest['Total Ballots'].sum()
    assert manifest_cards <= max_cards, f"cards in manifest {manifest_cards} exceeds max possible {max_cards}"
    assert manifest_cards >= n_cvrs, f"number of cvrs {n_cvrs} exceeds number of cards in the manifest {manifest_cards}"
    phantoms = 0
    if manifest_cards < max_cards:
        phantoms = max_cards-manifest_cards
        warnings.warn(f'manifest does not account for every card; appending batch of {phantoms} phantom cards to the manifest')
        r = {'Tray #': None, 'Tabulator Number': 'phantom', 'Batch Number': 1, \
             'Total Ballots': phantoms, 'VBMCart.Cart number': None}
        manifest = manifest.append(r, ignore_index = True)
    manifest['cum_cards'] = manifest['Total Ballots'].cumsum()    
    for c in ['Tray #', 'Tabulator Number', 'Batch Number', 'VBMCart.Cart number']:
        manifest[c] = manifest[c].astype(str)
    return manifest, manifest_cards, phantoms