#!perl

use 5.010;
use strict;
use warnings;
#use Log::Any '$log';

use Data::Clean::JSON;
use Perinci::Sub::GetArgs::Argv qw(
                                      gen_getopt_long_spec_from_meta
                              );
use Test::More 0.98;

my $meta = {
    v => 1.1,
    args => {
        str_arg1   => {schema=>'str'},
        'ary.arg1' => {schema=>[array => of => 'str']},
        float1     => {schema=>'float', cmdline_aliases=>{f=>{}}},
        int1       => {schema=>'int', cmdline_aliases=>{set_zero=>{schema=>'bool', code=>sub{}}}},
        bool1      => {schema=>'bool'},
        help       => {schema=>['bool', is=>1]},
        hash1      => {schema=>'hash', meta=>{v=>1.1, args=>{a=>{schema=>'str'}}}},
        ary1       => {schema=>'array', element_meta=>{v=>1.1, args=>{a=>{schema=>'str'}}}},
    },
};

# TODO: test per_arg_json=0
# TODO: test per_arg_yaml=0
# TODO: test conflicts

my $res = gen_getopt_long_spec_from_meta(
    meta=>$meta,
    per_arg_json=>1,
    per_arg_yaml=>1,
    common_opts=>{
        help    => {getopt=>'help|h|?' , handler=>sub {}},
        version => {getopt=>'version|v', handler=>sub {}},
        verbose => {getopt=>'verbose!' , handler=>sub {}},
        format  => {getopt=>'format=s' , handler=>sub {}},
        fmtopts => {getopt=>'format-options=s', handler=>sub {}},
    },
);

my $cleanser = Data::Clean::JSON->get_cleanser;
$cleanser->clean_in_place($res);

# strip parsed to keep things short
{
    my $sms = $res->[3]{'func.specmeta'};
    for (keys %$sms) {
        $sms->{$_}{parsed} = 'PARSED' if $sms->{$_}{parsed};
    }
}

# due to random hash ordering, sometimes 'f=f' is processed first (and thus
# 'float1=f' becomes CIRCULAR) and sometimes it's the other way around. so we
# just change 'CIRCULAR' to 'CODE' here.
{
    my $res = $res->[2];
    for (keys %$res) {
        $res->{$_} = 'CODE' if $res->{$_} eq 'CIRCULAR';
    }
}

my $expected_res = [
    200, "OK",
    {
        'help|h|?' => 'CODE',
        'version|v' => 'CODE',
        'verbose!' => 'CODE',
        'format=s' => 'CODE',
        'format-options=s' => 'CODE',
        'str-arg1=s' => 'CODE',
        'ary-arg1=s@' => 'CODE',
        'ary-arg1-json=s' => 'CODE',
        'ary-arg1-yaml=s' => 'CODE',
        'f=f' => 'CODE',
        'float1=f' => 'CODE',
        'int1=i' => 'CODE',
        'bool1!' => 'CODE',
        'set-zero' => 'CODE', # XXX should be 'set-zero'
        'help-arg' => 'CODE',
        'hash1=s' => 'CODE',
        'hash1-json=s' => 'CODE',
        'hash1-yaml=s' => 'CODE',
        'hash1-a=s' => 'CODE',
        'ary1=s' => 'CODE',
        'ary1-json=s' => 'CODE',
        'ary1-yaml=s' => 'CODE',
        'ary1-a=s' => 'CODE',
    },
    {
        'func.specmeta' => {
            'help|h|?' => {arg=>undef, parsed=>'PARSED',},
            'version|v' => {arg=>undef, parsed=>'PARSED',},
            'verbose!' => {arg=>undef, parsed=>'PARSED',},
            'format=s' => {arg=>undef, parsed=>'PARSED',},
            'format-options=s' => {arg=>undef, parsed=>'PARSED',},
            'str-arg1=s' => {arg=>'str_arg1', fqarg=>'str_arg1', parsed=>'PARSED',},
            'ary-arg1=s@' => {arg=>'ary.arg1', fqarg=>'ary.arg1', parsed=>'PARSED',},
            'ary-arg1-json=s' => {arg=>'ary.arg1', fqarg=>'ary.arg1', is_json=>1, parsed=>'PARSED',},
            'ary-arg1-yaml=s' => {arg=>'ary.arg1', fqarg=>'ary.arg1', is_yaml=>1, parsed=>'PARSED',},
            'float1=f' => {arg=>'float1', fqarg=>'float1', parsed=>'PARSED', noncode_aliases=>['f=f'],},
            'f=f' => {is_alias=>1, alias=>'f', alias_for=>'float1=f', is_code=>0, arg=>'float1', fqarg=>'float1', parsed=>'PARSED',},
            'int1=i' => {arg=>'int1', fqarg=>'int1', parsed=>'PARSED', code_aliases=>['set-zero'],},
            'set-zero' => {is_alias=>1, alias=>'set_zero', alias_for=>'int1=i', is_code=>1, arg=>'int1', fqarg=>'int1', parsed=>'PARSED',},
            'bool1!' => {arg=>'bool1', fqarg=>'bool1', parsed=>'PARSED',},
            'help-arg' => {arg=>'help', fqarg=>'help', parsed=>'PARSED',},
            'hash1=s' => {arg=>'hash1', fqarg=>'hash1', parsed=>'PARSED',},
            'hash1-json=s' => {arg=>'hash1', fqarg=>'hash1', is_json=>1, parsed=>'PARSED',},
            'hash1-yaml=s' => {arg=>'hash1', fqarg=>'hash1', is_yaml=>1, parsed=>'PARSED',},
            'hash1-a=s' => {arg=>'a', fqarg=>'hash1::a', parsed=>'PARSED',},
            'ary1=s' => {arg=>'ary1', fqarg=>'ary1', parsed=>'PARSED',},
            'ary1-json=s' => {arg=>'ary1', fqarg=>'ary1', is_json=>1, parsed=>'PARSED',},
            'ary1-yaml=s' => {arg=>'ary1', fqarg=>'ary1', is_yaml=>1, parsed=>'PARSED',},
            'ary1-a=s' => {arg=>'a', fqarg=>'ary1::a', parsed=>'PARSED',},
        },
        'func.opts' => [
            '--ary-arg1',
            '--ary-arg1-json',
            '--ary-arg1-yaml',
            '--ary1',
            '--ary1-a',
            '--ary1-json',
            '--ary1-yaml',
            '--bool1',
            '--float1',
            '--format',
            '--format-options',
            '--hash1',
            '--hash1-a',
            '--hash1-json',
            '--hash1-yaml',
            '--help',
            '--help-arg',
            '--int1',
            '--no-bool1',
            '--no-verbose',
            '--nobool1',
            '--noverbose',
            '--set-zero',
            '--str-arg1',
            '--verbose',
            '--version',
            '-?',
            '-f',
            '-h',
            '-v',
        ],
        'func.common_opts' => [
            '--format',
            '--format-options',
            '--help',
            '--no-verbose',
            '--noverbose',
            '--verbose',
            '--version',
            '-?',
            '-h',
            '-v',
        ],
        'func.func_opts' => [
            '--ary-arg1',
            '--ary-arg1-json',
            '--ary-arg1-yaml',
            '--ary1',
            '--ary1-a',
            '--ary1-json',
            '--ary1-yaml',
            '--bool1',
            '--float1',
            '--hash1',
            '--hash1-a',
            '--hash1-json',
            '--hash1-yaml',
            '--help-arg',
            '--int1',
            '--no-bool1',
            '--nobool1',
            '--set-zero',
            '--str-arg1',
            '-f',
        ],
        'func.opts_by_arg' => {
            'ary.arg1' => [
                '--ary-arg1',
                '--ary-arg1-json',
                '--ary-arg1-yaml',
            ],
            'bool1' => [
                '--bool1',
                '--no-bool1',
                '--nobool1',
            ],
            'float1' => [
                '--float1',
                '-f',
            ],
            'help' => [
                '--help-arg',
            ],
            'int1' => [
                '--int1',
                '--set-zero',
            ],
            'str_arg1' => [
                '--str-arg1',
            ],
            'hash1' => [
                '--hash1',
                '--hash1-json',
                '--hash1-yaml',
            ],
            'hash1::a' => [
                '--hash1-a',
            ],
            'ary1' => [
                '--ary1',
                '--ary1-json',
                '--ary1-yaml',
            ],
            'ary1::a' => [
                '--ary1-a',
            ],
        },
        'func.opts_by_common' => {
            'format-options=s' => [
                '--format-options',
            ],
            'format=s' => [
                '--format',
            ],
            'help|h|?' => [
                '--help',
                '-?',
                '-h',
            ],
            'verbose!' => [
                '--no-verbose',
                '--noverbose',
                '--verbose',
            ],
            'version|v' => [
                '--version',
                '-v',
            ]
        },
    },
];

is_deeply($res, $expected_res)
    or diag explain $res;

DONE_TESTING:
done_testing;
