"""
Filter and subsample a sequence set.

SeqKit is used behind the scenes to handle FASTA files, but this should be
considered an implementation detail that may change in the future.  The CLI
program seqkit must be available.  If it's not on PATH (or you want to use a
version different from what's on PATH), set the SEQKIT environment variable to
path of the desired seqkit executable.

VCFtools is used behind the scenes to handle VCF files, but this should be
considered an implementation detail that may change in the future.  The CLI
program vcftools must be available on PATH.
"""
from augur.argparse_ import ExtendOverwriteDefault, SKIP_AUTO_DEFAULT_IN_HELP
from augur.dates import numeric_date_type
from augur.filter.arguments import descriptions
from augur.filter.io import column_type_pair
from augur.io.metadata import DEFAULT_DELIMITERS, DEFAULT_ID_COLUMNS
from augur.io.print import print_err
from augur.types import EmptyOutputReportingMethod


def register_arguments(parser):
    """
    Add arguments to parser.
    Kept as a separate function than `register_parser` to continue to support
    unit tests that use this function to create argparser.
    """
    input_group = parser.add_argument_group("inputs", "metadata and sequences to be filtered")
    input_group.add_argument('--metadata', required=True, metavar="FILE", help="sequence metadata")
    input_group.add_argument('--sequences', '-s', help="sequences in FASTA or VCF format. For large inputs, consider using --sequence-index in addition to this option.")
    input_group.add_argument('--sequence-index', help="sequence composition report generated by augur index. If not provided, an index will be created on the fly. This should be generated from the same file as --sequences.")
    input_group.add_argument('--metadata-chunk-size', type=int, default=100000, help="maximum number of metadata records to read into memory at a time. Increasing this number can speed up filtering at the cost of more memory used.")
    input_group.add_argument('--metadata-id-columns', default=DEFAULT_ID_COLUMNS, nargs="+", action=ExtendOverwriteDefault, help="names of possible metadata columns containing identifier information, ordered by priority. Only one ID column will be inferred.")
    input_group.add_argument('--metadata-delimiters', default=DEFAULT_DELIMITERS, nargs="+", action=ExtendOverwriteDefault, help="delimiters to accept when reading a metadata file. Only one delimiter will be inferred.")
    input_group.add_argument('--skip-checks', action='store_true', help="use this option to skip checking for duplicates in sequences and whether ids in metadata have a sequence entry. Can improve performance on large files. Note that this should only be used if you are sure there are no duplicate sequences or mismatched ids since they can lead to errors in downstream Augur commands.")

    metadata_filter_group = parser.add_argument_group("metadata filters", "filters to apply to metadata")

    metadata_filter_group.add_argument('--query', help=descriptions['query'])
    metadata_filter_group.add_argument('--query-columns', type=column_type_pair, nargs="+", action=ExtendOverwriteDefault, help=descriptions['query_columns'])
    metadata_filter_group.add_argument('--min-date', type=numeric_date_type, help=descriptions['min_date'])
    metadata_filter_group.add_argument('--max-date', type=numeric_date_type, help=descriptions['max_date'])
    metadata_filter_group.add_argument('--exclude-ambiguous-dates-by', choices=['any', 'day', 'month', 'year'], help=descriptions['exclude_ambiguous_dates_by'])
    metadata_filter_group.add_argument('--exclude', type=str, nargs="+", action=ExtendOverwriteDefault, help=descriptions['exclude'])
    metadata_filter_group.add_argument('--exclude-where', nargs='+', action=ExtendOverwriteDefault, help=descriptions['exclude_where'])
    metadata_filter_group.add_argument('--exclude-all', action="store_true", help=descriptions['exclude_all'])
    metadata_filter_group.add_argument('--include', type=str, nargs="+", action=ExtendOverwriteDefault, help=descriptions['include'])
    metadata_filter_group.add_argument('--include-where', nargs='+', action=ExtendOverwriteDefault, help=descriptions['include_where'])

    sequence_filter_group = parser.add_argument_group("sequence filters", "filters to apply to sequence data")
    sequence_filter_group.add_argument('--min-length', type=int, help=descriptions['min_length'])
    sequence_filter_group.add_argument('--max-length', type=int, help=descriptions['max_length'])
    sequence_filter_group.add_argument('--non-nucleotide', action='store_true', help=descriptions['non_nucleotide'])

    subsample_group = parser.add_argument_group("subsampling", "options to subsample filtered data")
    subsample_group.add_argument('--group-by', nargs='+', action=ExtendOverwriteDefault, default=[], help=descriptions['group_by'])
    subsample_limits_group = subsample_group.add_mutually_exclusive_group()
    subsample_limits_group.add_argument('--sequences-per-group', type=int, help=descriptions['sequences_per_group'])
    subsample_limits_group.add_argument('--subsample-max-sequences', type=int, help=descriptions['subsample_max_sequences'])
    probabilistic_sampling_group = subsample_group.add_mutually_exclusive_group()
    probabilistic_sampling_group.add_argument('--probabilistic-sampling', action='store_true', help=descriptions['probabilistic_sampling'])
    probabilistic_sampling_group.add_argument('--no-probabilistic-sampling', action='store_false', dest='probabilistic_sampling')
    subsample_group.add_argument('--group-by-weights', type=str, metavar="FILE", help=descriptions['group_by_weights'])
    subsample_group.add_argument('--priority', type=str, help="""tab-delimited file with list of priority scores for strains (e.g., "<strain>\\t<priority>") and no header.
    When scores are provided, Augur converts scores to floating point values, sorts strains within each subsampling group from highest to lowest priority, and selects the top N strains per group where N is the calculated or requested number of strains per group.
    Higher numbers indicate higher priority.
    Since priorities represent relative values between strains, these values can be arbitrary.""")
    subsample_group.add_argument('--subsample-seed', type=int, help="random number generator seed to allow reproducible subsampling (with same input data).")

    output_group = parser.add_argument_group("outputs", "options related to outputs, at least one of the possible representations of filtered data (--output-sequences, --output-metadata, --output-strains) is required")
    output_group.add_argument('--output-sequences', help="filtered sequences in FASTA format")
    output_group.add_argument('--output-metadata', help="metadata for strains that passed filters")
    output_group.add_argument('--output-strains', help="list of strains that passed filters (no header)")
    output_group.add_argument('--output-log', help="tab-delimited file with one row for each filtered strain and the reason it was filtered. Keyword arguments used for a given filter are reported in JSON format in a `kwargs` column.")
    output_group.add_argument('--output-group-by-sizes', help="tab-delimited file one row per group with target size.")
    output_group.add_argument(
        '--empty-output-reporting',
        type=EmptyOutputReportingMethod.argtype,
        choices=list(EmptyOutputReportingMethod),
        default=EmptyOutputReportingMethod.ERROR,
        help="How should empty outputs be reported when no strains pass filtering and/or subsampling.")

    other_group = parser.add_argument_group("other", "other options")
    other_group.add_argument('--nthreads', metavar="N", type=int, default=1, help="Number of CPUs/cores/threads/jobs to utilize at once.")

    deprecated_group = parser.add_argument_group("deprecated", "options to be removed in a future major version")
    deprecated_group.add_argument('--output', metavar="FILE", help="alias to --output-sequences" + SKIP_AUTO_DEFAULT_IN_HELP)
    deprecated_group.add_argument('-o', metavar="FILE", help="alias to --output-sequences" + SKIP_AUTO_DEFAULT_IN_HELP)

    parser.set_defaults(probabilistic_sampling=True)


def register_parser(parent_subparsers):
    parser = parent_subparsers.add_parser("filter", help=__doc__)
    register_arguments(parser)
    return parser


def run(args):
    '''
    filter and subsample a set of sequences into an analysis set
    '''
    if args.o:
        print_err("WARNING: -o is deprecated. Use --output-sequences instead.")
        args.output_sequences = args.o
    if args.output:
        print_err("WARNING: --output is deprecated. Use --output-sequences instead.")
        args.output_sequences = args.output

    from .validate_arguments import validate_arguments
    # Validate arguments before attempting any I/O.
    validate_arguments(args)

    from ._run import run as _run
    return _run(args)
