package Convert::Pheno::CLI::Args;

use strict;
use warnings;

use Exporter 'import';
use Getopt::Long qw(GetOptionsFromArray :config no_ignore_case);
use File::Spec::Functions qw(catfile file_name_is_absolute);
use Convert::Pheno::OMOP::Definitions qw(@omop_supported_tables);
use Convert::Pheno::Operations qw(conversion_spec is_public_conversion);

our @EXPORT_OK = qw(build_cli_request);

sub _normalize_cli_type {
    my ($type) = @_;
    return undef unless defined $type;
    my $norm = lc $type;
    $norm =~ s/^\s+|\s+$//g;
    $norm =~ s/[-_\s]+//g;
    my %map = (
        bff          => 'bff',
        beacon       => 'bff',
        pxf          => 'pxf',
        phenopackets => 'pxf',
        omop         => 'omop',
        omopcdm      => 'omop',
        openehr      => 'openehr',
        ehrbase      => 'openehr',
        redcap       => 'redcap',
        cbioportal   => 'cbioportal',
        cdiscodm     => 'cdiscodm',
        datasetjson  => 'datasetjson',
        datasetxml   => 'datasetxml',
        fhir         => 'fhir',
        csv          => 'csv',
        jsonf        => 'jsonf',
        jsonld       => 'jsonld',
    );
    return $map{$norm};
}

sub _count_defined {
    return scalar grep { defined $_ } @_;
}

sub _resolve_output_path {
    my ( $dir, $path ) = @_;
    return undef unless defined $path;
    return $path if file_name_is_absolute($path);
    return catfile( $dir, $path );
}

sub build_cli_request {
    my (%arg) = @_;
    my $argv           = $arg{argv} || [];
    my $usage_error    = $arg{usage_error} || sub { die @_ };
    my $schema_default = $arg{schema_file};
    my $out_dir        = exists $arg{out_dir}   ? $arg{out_dir}   : '.';
    my $color          = exists $arg{color}     ? $arg{color}     : 1;
    my $stream         = exists $arg{stream}    ? $arg{stream}    : 0;
    my $ohdsi_db       = exists $arg{ohdsi_db}  ? $arg{ohdsi_db}  : 0;

    my ( $in_type_arg, $out_type_arg );
    my ( $in_pxf, $in_bff, $in_cbioportal, $in_redcap, $in_cdiscodm, $in_csv );
    my @datasetjson_files;
    my @datasetxml_files;
    my @fhir_files;
    my @openehr_files;
    my @omop_files;
    my ( $out_bff, $out_pxf, $out_csv, $out_jsonf, $out_jsonld );
    my $out_omop_selected = 0;
    my $out_bff_selected = 0;
    my @entities_args;
    my @out_name_specs;
    my ( $help, $man, $mapping_file, $define_xml, $max_lines_sql, $search );
    my ( $text_similarity_method, $min_text_similarity_score, $levenshtein_weight );
    my ( $debug, $verbose, $sep, $exposures_file, $sql2csv, $test, $term_audit );
    my ( @omop_tables, $redcap_dictionary, $path_to_ohdsi_db );
    my ( $self_validate_schema, $overwrite, $username, $log, $version );
    my $default_vital_status;
    my $schema_file = $schema_default;
    my $source_info = 1;

    GetOptionsFromArray(
        $argv,
        'i=s'                         => \$in_type_arg,
        'o=s'                         => \$out_type_arg,
        'ipxf=s'                      => \$in_pxf,
        'ibff=s'                      => \$in_bff,
        'icbioportal=s'               => \$in_cbioportal,
        'iredcap=s'                   => \$in_redcap,
        'icdisc-odm=s'                => \$in_cdiscodm,
        'idataset-json=s{1,}'          => \@datasetjson_files,
        'idataset-xml=s{1,}'           => \@datasetxml_files,
        'ifhir=s{1,}'                  => \@fhir_files,
        'iomop=s{1,}'                 => \@omop_files,
        'iopenehr=s{1,}'              => \@openehr_files,
        'icsv=s'                      => \$in_csv,
        'obff:s'                     => sub {
            my ( $opt_name, $opt_value ) = @_;
            $out_bff_selected = 1;
            $out_bff = $opt_value if defined $opt_value;
        },
        'opxf=s'                      => \$out_pxf,
        'ocsv=s'                      => \$out_csv,
        'ojsonf=s'                    => \$out_jsonf,
        'ojsonld=s'                   => \$out_jsonld,
        'oomop'                       => \$out_omop_selected,
        'out-dir=s'                   => \$out_dir,
        'entities=s{1,}'              => \@entities_args,
        'out-name=s'                  => \@out_name_specs,
        'help|?'                      => \$help,
        'man'                         => \$man,
        'mapping-file=s'              => \$mapping_file,
        'define-xml=s'                 => \$define_xml,
        'max-lines-sql=i'             => \$max_lines_sql,
        'search=s'                    => \$search,
        'text-similarity-method=s'    => \$text_similarity_method,
        'min-text-similarity-score=f' => \$min_text_similarity_score,
        'levenshtein-weight=f'        => \$levenshtein_weight,
        'debug=i'                     => \$debug,
        'verbose|v'                   => \$verbose,
        'color!'                      => \$color,
        'separator|sep=s'             => \$sep,
        'schema-file=s'               => \$schema_file,
        'exposures-file=s'            => \$exposures_file,
        'stream!'                     => \$stream,
        'sql2csv'                     => \$sql2csv,
        'test'                        => \$test,
        'term-audit=s'                => \$term_audit,
        'source-info!'                => \$source_info,
        'ohdsi-db'                    => \$ohdsi_db,
        'omop-tables=s{1,}'           => \@omop_tables,
        'redcap-dictionary|rcd=s'     => \$redcap_dictionary,
        'path-to-ohdsi-db=s'          => \$path_to_ohdsi_db,
        'self-validate-schema|svs'    => \$self_validate_schema,
        'default-vital-status=s'      => \$default_vital_status,
        'O'                           => \$overwrite,
        'username|u=s'                => \$username,
        'log:s'                       => \$log,
        'version|V'                   => \$version,
    ) or $usage_error->('Invalid command-line arguments');

    return {
        action  => 'help',
        color   => $color,
    } if $help;

    return {
        action  => 'man',
        color   => $color,
    } if $man;

    return {
        action  => 'version',
        color   => $color,
    } if $version;

    my $normalized_in_type  = _normalize_cli_type($in_type_arg);
    my $normalized_out_type = _normalize_cli_type($out_type_arg);

    $usage_error->("Unsupported input type <$in_type_arg> for -i")
      if defined $in_type_arg && !defined $normalized_in_type;
    $usage_error->("Unsupported output type <$out_type_arg> for -o")
      if defined $out_type_arg && !defined $normalized_out_type;

    $usage_error->("Please use either the generic <-i/-o> syntax or the compact <-ixxx/-oxxx> flags for each side, not both")
      if ( defined $normalized_in_type
        && _count_defined( $in_pxf, $in_bff, $in_cbioportal, $in_redcap, $in_cdiscodm, $in_csv, @datasetjson_files ? 1 : undef, @datasetxml_files ? 1 : undef, @fhir_files ? 1 : undef, @omop_files ? 1 : undef, @openehr_files ? 1 : undef ) )
      || ( defined $normalized_out_type
        && _count_defined( $out_bff_selected ? 1 : undef, $out_pxf, $out_csv, $out_jsonf, $out_jsonld, $out_omop_selected ? 1 : undef ) );

    if ( defined $normalized_in_type ) {
        if ( $normalized_in_type eq 'omop'
            || $normalized_in_type eq 'openehr'
            || $normalized_in_type eq 'datasetjson'
            || $normalized_in_type eq 'datasetxml'
            || $normalized_in_type eq 'fhir' )
        {
            $usage_error->("Please provide $in_type_arg input file(s) after <-i $in_type_arg>") unless @{$argv};
            if ( defined $normalized_out_type ) {
                if ( $normalized_out_type eq 'omop' ) {
                    if ( $normalized_in_type eq 'omop' ) {
                        @omop_files = @{$argv};
                    }
                    elsif ( $normalized_in_type eq 'fhir' ) {
                        @fhir_files = @{$argv};
                    }
                    elsif ( $normalized_in_type eq 'openehr' ) {
                        @openehr_files = @{$argv};
                    }
                    elsif ( $normalized_in_type eq 'datasetxml' ) {
                        @datasetxml_files = @{$argv};
                    }
                    else {
                        @datasetjson_files = @{$argv};
                    }
                    $out_omop_selected = 1;
                }
                else {
                    $usage_error->("Please provide $in_type_arg input file(s) followed by one output path")
                      unless @{$argv} >= 2;
                    if ( $normalized_in_type eq 'omop' ) {
                        @omop_files = @{$argv}[ 0 .. $#{$argv} - 1 ];
                    }
                    elsif ( $normalized_in_type eq 'fhir' ) {
                        @fhir_files = @{$argv}[ 0 .. $#{$argv} - 1 ];
                    }
                    elsif ( $normalized_in_type eq 'openehr' ) {
                        @openehr_files = @{$argv}[ 0 .. $#{$argv} - 1 ];
                    }
                    elsif ( $normalized_in_type eq 'datasetxml' ) {
                        @datasetxml_files = @{$argv}[ 0 .. $#{$argv} - 1 ];
                    }
                    else {
                        @datasetjson_files = @{$argv}[ 0 .. $#{$argv} - 1 ];
                    }
                    my $generic_outfile = $argv->[-1];
                    if    ( $normalized_out_type eq 'bff' )    { $out_bff_selected = 1; $out_bff = $generic_outfile }
                    elsif ( $normalized_out_type eq 'pxf' )    { $out_pxf    = $generic_outfile }
                    elsif ( $normalized_out_type eq 'csv' )    { $out_csv    = $generic_outfile }
                    elsif ( $normalized_out_type eq 'jsonf' )  { $out_jsonf  = $generic_outfile }
                    elsif ( $normalized_out_type eq 'jsonld' ) { $out_jsonld = $generic_outfile }
                }
            }
            else {
                if ( $normalized_in_type eq 'omop' ) {
                    @omop_files = @{$argv};
                }
                elsif ( $normalized_in_type eq 'fhir' ) {
                    @fhir_files = @{$argv};
                }
                elsif ( $normalized_in_type eq 'openehr' ) {
                    @openehr_files = @{$argv};
                }
                elsif ( $normalized_in_type eq 'datasetxml' ) {
                    @datasetxml_files = @{$argv};
                }
                else {
                    @datasetjson_files = @{$argv};
                }
            }
            @{$argv} = ();
        }
        else {
            $usage_error->("Please provide an input file after <-i $in_type_arg>")
              unless @{$argv};
            my $generic_infile = shift @{$argv};
            if    ( $normalized_in_type eq 'pxf' )    { $in_pxf    = $generic_infile }
            elsif ( $normalized_in_type eq 'bff' )    { $in_bff    = $generic_infile }
            elsif ( $normalized_in_type eq 'cbioportal' ) { $in_cbioportal = $generic_infile }
            elsif ( $normalized_in_type eq 'redcap' ) { $in_redcap = $generic_infile }
            elsif ( $normalized_in_type eq 'cdiscodm' ) { $in_cdiscodm = $generic_infile }
            elsif ( $normalized_in_type eq 'csv' )    { $in_csv    = $generic_infile }

            if ( defined $normalized_out_type ) {
                if ( $normalized_out_type eq 'omop' ) {
                    $out_omop_selected = 1;
                }
                else {
                    $usage_error->("Please provide an output file after <-o $out_type_arg>")
                      unless @{$argv};
                    my $generic_outfile = shift @{$argv};
                    if    ( $normalized_out_type eq 'bff' )    { $out_bff_selected = 1; $out_bff = $generic_outfile }
                    elsif ( $normalized_out_type eq 'pxf' )    { $out_pxf    = $generic_outfile }
                    elsif ( $normalized_out_type eq 'csv' )    { $out_csv    = $generic_outfile }
                    elsif ( $normalized_out_type eq 'jsonf' )  { $out_jsonf  = $generic_outfile }
                    elsif ( $normalized_out_type eq 'jsonld' ) { $out_jsonld = $generic_outfile }
                }
            }
        }
    }

    $usage_error->("The flag <-o> requires <-i> when using the generic syntax")
      if defined $normalized_out_type && !defined $normalized_in_type;

    if ( @entities_args && @{$argv} == 1 && !$out_bff_selected && $argv->[0] eq $out_dir ) {
        $usage_error->("When using <--entities>, please also select BFF output with <-obff> and keep <--out-dir> as the directory target");
    }

    $usage_error->("The flag <-oomop> no longer accepts a prefix. Use <-oomop --out-dir DIR> and optional <--out-name TABLE=file> overrides instead")
      if $out_omop_selected && @{$argv};

    $usage_error->("Unexpected extra positional arguments: @{$argv}") if @{$argv};

    my @validation_checks = (
        {
            condition => sub {
                !(     ( defined $in_pxf && -f $in_pxf )
                    || ( defined $in_bff    && -f $in_bff )
                    || defined $in_cbioportal
                    || ( defined $in_redcap && -f $in_redcap )
                    || ( defined $in_cdiscodm && -f $in_cdiscodm )
                    || ( defined $in_csv    && -f $in_csv )
                    || ( @datasetjson_files && -f $datasetjson_files[0] )
                    || ( @datasetxml_files  && -f $datasetxml_files[0] )
                    || ( @fhir_files        && -f $fhir_files[0] )
                    || ( @omop_files        && -f $omop_files[0] )
                    || ( @openehr_files     && -f $openehr_files[0] ) );
            },
            message => "Please specify a valid input [-i input-type] <infile>\n",
        },
        { condition => sub { !-d $out_dir }, message => "Please specify a valid directory for --out-dir\n", },
        {
            condition => sub {
                defined $in_cbioportal
                  && !-d $in_cbioportal
                  && !( -f $in_cbioportal && $in_cbioportal =~ /\.zip\z/i );
            },
            message => "Please specify a valid cBioPortal study directory or ZIP file\n",
        },
        {
            condition => sub { $in_redcap && !$redcap_dictionary },
            message   => "Please specify a valid REDCap data dictionary --rcd <file>\n",
        },
        {
            condition => sub { ( $in_redcap || $in_cdiscodm || $in_csv ) && !$mapping_file },
            message   => "Please specify a valid mapping file --mapping-file <file>\n",
        },
        {
            condition => sub {
                @datasetjson_files
                  && grep { !-f $_ || $_ !~ m/\.json(?:\.gz)?$/i }
                  @datasetjson_files;
            },
            message   => "Please specify valid CDISC Dataset-JSON file(s) (.json or .json.gz)\n",
        },
        {
            condition => sub {
                @datasetxml_files
                  && grep { !-f $_ || $_ !~ m/\.xml$/i }
                  @datasetxml_files;
            },
            message   => "Please specify valid CDISC Dataset-XML file(s) (.xml)\n",
        },
        {
            condition => sub {
                @datasetxml_files && ( !defined $define_xml || !-f $define_xml );
            },
            message   => "Please specify the accompanying Define-XML file with --define-xml <file>\n",
        },
        {
            condition => sub { defined $define_xml && !@datasetxml_files && !@datasetjson_files },
            message   => "The flag <--define-xml> is only valid with <-idataset-json> or <-idataset-xml>\n",
        },
        {
            condition => sub {
                @fhir_files
                  && grep { !-f $_ || $_ !~ m/\.json(?:\.gz)?$/i }
                  @fhir_files;
            },
            message   => "Please specify valid FHIR R4 Bundle file(s) (.json or .json.gz)\n",
        },
        {
            condition => sub { @omop_files && $omop_files[0] !~ m/\.(csv|sql|tsv)/i },
            message   => "Please specify a valid OMOP-CDM file(s) (e.g., *csv or .sql)\n",
        },
        {
            condition => sub { @openehr_files && grep { $_ !~ m/\.(json|ya?ml)(?:\.gz)?$/i } @openehr_files },
            message   => "Please specify valid openEHR JSON/YAML file(s)\n",
        },
        {
            condition => sub { @omop_tables && !@omop_files },
            message   => "The flag <--omop-tables> is only valid with <-iomop>\n",
        },
        {
            condition => sub { $stream && $out_pxf },
            message   => "The flag <--stream> is only valid with <-obff>\n",
        },
        {
            condition => sub { $stream && $sql2csv },
            message   =>
"The flags <--stream> and <--sql2csv> are mutually exclusive.\nIf you are using <--stream> is because you are likely processing huge files and we don't want to duplicate them in your HDD\n",
        },
        {
            condition => sub {
                @omop_files
                  && ( ( defined $out_bff && length $out_bff && $out_bff !~ m/\.json/i )
                    || ( defined $out_pxf && $out_pxf !~ m/\.json/i ) );
            },
            message => "The flag <--iomops> only supports output files in <json|json.gz> format\n",
        },
        {
            condition => sub { !-f $schema_file },
            message   => "Please specify a valid schema for the mapping file --schema-file <file>\n",
        },
        {
            condition => sub { defined $path_to_ohdsi_db && !-d $path_to_ohdsi_db },
            message   => "Please specify a valid directory for the mapping file --path-to-ohdsi-db <dir>\n",
        },
        {
            condition => sub { defined $exposures_file && !-f $exposures_file },
            message   => "Please specify a valid --exposures-file <file>\n",
        },
        {
            condition => sub {
                defined $term_audit
                  && $term_audit !~ /\.(?:tsv(?:\.gz)?|xlsx)\z/i;
            },
            message => "Please use --term-audit with a .tsv, .tsv.gz, or .xlsx output file\n",
        },
        {
            condition => sub { ( $out_csv || $out_jsonf || $out_jsonld ) && ( !$in_bff && !$in_pxf ); },
            message   => "Sorry, <--ocsv>, <--ojsonf> and <--ojsonf> are only compatible with <--ibff> or <--ipxf>\n",
        },
        {
            condition => sub { $out_omop_selected && !$ohdsi_db },
            message   => "Error: Please use --ohdsi-db when using OMOP CDM as an output",
        },
    );

    for my $check (@validation_checks) {
        $usage_error->( $check->{message} ) if $check->{condition}->();
    }

    $usage_error->("Please provide <--entities> as a space-separated list, e.g. <--entities individuals biosamples>")
      if grep { defined $_ && /,/ } @entities_args;

    my @entity_list =
      @entities_args
      ? map { uc($_) eq 'ALL' ? () : $_ } grep { length } map { s/^\s+|\s+$//gr } @entities_args
      : ('individuals');

    @entity_list = ('individuals') unless @entity_list;

    my %supported_entities = map { $_ => 1 } qw(individuals biosamples datasets cohorts);
    for my $entity (@entity_list) {
        $usage_error->("Unsupported entity <$entity> in --entities")
          unless $supported_entities{$entity};
    }

    $usage_error->("The flag <--entities> is only valid with BFF output")
      if @entities_args && ( $out_pxf || $out_csv || $out_jsonf || $out_jsonld || $out_omop_selected );

    $usage_error->("The flag <--stream> is only valid with <-iomop> and <-obff>")
      if $stream && !@omop_files;

    $usage_error->("The openEHR input path currently supports only BFF or PXF output")
      if ( @openehr_files || ( defined $normalized_in_type && $normalized_in_type eq 'openehr' ) )
      && ( $out_csv || $out_jsonf || $out_jsonld || $out_omop_selected );

    $usage_error->("The entities <datasets> and <cohorts> are not supported with <--stream>; please request only <individuals> and/or <biosamples>")
      if $stream && grep { $_ eq 'datasets' || $_ eq 'cohorts' } @entity_list;

    $usage_error->("When using <--entities>, please select BFF output with <-obff> and write the requested entities with <--out-dir>. Use either <-obff FILE> for individuals-only BFF output or <-obff --entities ... --out-dir DIR> for entity-aware BFF output")
      if @entities_args && !$out_bff_selected;

    $usage_error->("When using <-obff FILE> together with <--entities>, please omit the file and use <-obff --entities ... --out-dir DIR>")
      if @entities_args && defined $out_bff && length $out_bff;

    $usage_error->("The flag <--out-name> requires either entity-aware BFF output or OMOP output")
      if @out_name_specs && !@entities_args && !$out_omop_selected;

    if ( defined $default_vital_status ) {
        $default_vital_status =~ s/^\s+|\s+$//g;
        $usage_error->("Unsupported value <$default_vital_status> for --default-vital-status")
          unless $default_vital_status =~ /\A(?:ALIVE|DECEASED|UNKNOWN_STATUS)\z/;
    }

    $usage_error->("The flag <--default-vital-status> is only valid with PXF output")
      if defined $default_vital_status && !$out_pxf;

    my %output_name_overrides;
    for my $spec (@out_name_specs) {
        $usage_error->("Invalid <--out-name> value <$spec>; use key=filename")
          unless defined $spec && $spec =~ /\A([^=]+)=(.+)\z/;
        my ( $key, $filename ) = ( $1, $2 );
        $key =~ s/^\s+|\s+$//g;
        $filename =~ s/^\s+|\s+$//g;

        $usage_error->("Please provide a filename for <--out-name $key=...>")
          unless length $filename;

        if (@entities_args) {
            $usage_error->("Unsupported entity <$key> in --out-name")
              unless $supported_entities{$key};
            $usage_error->("The entity <$key> must also be requested in <--entities>")
              unless grep { $_ eq $key } @entity_list;
            $output_name_overrides{$key} =
              _resolve_output_path( $out_dir, $filename );
            next;
        }

        my $table = uc $key;
        my %supported_tables = map { $_ => 1 } @omop_supported_tables;
        $usage_error->("Unsupported OMOP table <$key> in --out-name")
          unless $supported_tables{$table};
        $output_name_overrides{$table} =
          _resolve_output_path( $out_dir, $filename );
    }

    my $out_file =
        $out_pxf    ? _resolve_output_path( $out_dir, $out_pxf )
      : defined $out_bff && length $out_bff ? _resolve_output_path( $out_dir, $out_bff )
      : $out_csv    ? _resolve_output_path( $out_dir, $out_csv )
      : $out_jsonf  ? _resolve_output_path( $out_dir, $out_jsonf )
      : $out_jsonld ? _resolve_output_path( $out_dir, $out_jsonld )
      : $out_omop_selected ? undef
      : @entity_list == 1
      ? catfile( $out_dir, $entity_list[0] . '.json' )
      : catfile( $out_dir, 'individuals.json' );

    my $log_file =
      catfile( $out_dir, ( $log ? $log : 'convert-pheno-log.json' ) );
    my $term_audit_file =
      defined $term_audit
      ? _resolve_output_path( $out_dir, $term_audit )
      : undef;

    my $in_type =
        $in_pxf     ? 'pxf'
      : $in_bff     ? 'bff'
      : $in_cbioportal ? 'cbioportal'
      : $in_redcap  ? 'redcap'
      : $in_cdiscodm ? 'cdiscodm'
      : @datasetjson_files ? 'datasetjson'
      : @datasetxml_files ? 'datasetxml'
      : @fhir_files  ? 'fhir'
      : $in_csv     ? 'csv'
      : @openehr_files ? 'openehr'
      : @omop_files ? 'omop'
      :               'bff';
    my $out_type =
        $out_pxf    ? 'pxf'
      : $out_bff_selected ? 'bff'
      : $out_csv    ? 'csv'
      : $out_jsonf  ? 'jsonf'
      : $out_jsonld ? 'jsonld'
      : $out_omop_selected ? 'omop'
      :               'bff';
    my $method = $in_type . '2' . $out_type;
    $usage_error->("Unsupported conversion <$method>")
      unless is_public_conversion($method);
    my $conversion_spec = conversion_spec($method);

    if ($out_bff_selected) {
        my %supported_for_route =
          map { $_ => 1 } @{ $conversion_spec->{entities}{supported} };
        for my $entity (@entity_list) {
            $usage_error->(
                "The entity <$entity> is not supported by conversion <$method>"
            ) unless $supported_for_route{$entity};
        }
    }

    $usage_error->("The conversion <$method> does not support --stream")
      if $stream && !$conversion_spec->{streaming};

    my $id = time . substr( "00000$$", -5 );

    my %data = (
        out_dir                   => $out_dir,
        in_textfile               => 1,
        method                    => $method,
        sql2csv                   => $sql2csv ? 1 : 0,
        exposures_file            => $exposures_file,
        search                    => $search,
        ohdsi_db                  => $ohdsi_db ? 1 : 0,
        omop_tables               => \@omop_tables,
        username                  => $username,
        text_similarity_method    => $text_similarity_method,
        min_text_similarity_score => $min_text_similarity_score,
        levenshtein_weight        => $levenshtein_weight,
        max_lines_sql             => $max_lines_sql,
        stream                    => $stream ? 1 : 0,
        schema_file               => $schema_file,
        out_file                  => $out_file,
        id                        => $id,
        test                      => $test ? 1 : 0,
        source_info               => $source_info ? 1 : 0,
        entities                  => \@entity_list,
    );

    $data{output_name_overrides} = \%output_name_overrides if %output_name_overrides;

    my $resolved_in_file =
        $in_pxf     ? $in_pxf
      : $in_bff     ? $in_bff
      : $in_cbioportal ? $in_cbioportal
      : $in_redcap  ? $in_redcap
      : $in_cdiscodm ? $in_cdiscodm
      : $in_csv     ? $in_csv
      :               undef;

    $data{in_file}              = $resolved_in_file if defined $resolved_in_file;
    $data{in_files}             = \@datasetjson_files if @datasetjson_files;
    $data{in_files}             = \@datasetxml_files  if @datasetxml_files;
    $data{in_files}             = \@fhir_files       if @fhir_files;
    $data{in_files}             = \@omop_files      if @omop_files;
    $data{in_files}             = \@openehr_files   if @openehr_files;
    $data{sep}                  = $sep if defined $sep;
    $data{redcap_dictionary}    = $redcap_dictionary if defined $redcap_dictionary;
    $data{mapping_file}         = $mapping_file if defined $mapping_file;
    $data{define_xml}           = $define_xml if defined $define_xml;
    $data{self_validate_schema} = $self_validate_schema if defined $self_validate_schema;
    $data{path_to_ohdsi_db}     = $path_to_ohdsi_db if defined $path_to_ohdsi_db;
    $data{term_audit_file}      = $term_audit_file if defined $term_audit_file;
    $data{default_vital_status} = $default_vital_status if defined $default_vital_status;
    $data{debug}                = $debug if defined $debug;
    $data{log}                  = $log if defined $log;
    $data{verbose}              = $verbose ? 1 : 0 if defined $verbose;

    return {
        action    => 'run',
        color     => $color,
        overwrite => $overwrite,
        out_file  => $out_file,
        log_file  => $log_file,
        verbose   => $verbose,
        stream    => $stream,
        data      => \%data,
    };
}

1;
