package Net::Compare;

use 5.012;
use warnings;
use File::Slurp;

use Exporter;
use parent 'Exporter';

our @EXPORT = qw /compare/;


sub usage () {
    say "\n网络配置基线巡检模块使用说明：\n";
    say "\n加载模块后，需传入compare函数（基线配置路径，设备运行配置路径）\n";
    say '\n使用过程如有任何疑问，请联系careline@126.com\n';
    exit(-1);
}

#------------------------------------------------------------------
# 网络配置基线巡检功能函数
#------------------------------------------------------------------
sub compare {
    my $line   = shift;
    my $config = shift;

    unless ( $line && $config ) {
        &usage();
    }

    #处理属组元素换行符\n
    my @line   = grep { chomp } read_file($line);
    my @config = grep { chomp } read_file($config);

    #------------------------------------------------------------------
    # 重新组合包含代码块的基线检查
    #------------------------------------------------------------------
    #重新组合运行配置项
    my @ret       = ();
    my $check_ret = 0;

    #检查基线命令行是否为代码块
    my $cli_code = ( grep {/^\s+[^(\+no|\~|\!|\#)]/} @line ) ? 1 : 0;
    if ($cli_code) {
        foreach my $cli (@config) {

            #命中基线首行并设置代码块状态码
            if ( $cli eq $line[0] ) {

                #将代码块内代码添加到属组中
                push @ret, $cli;
                $check_ret = 1;
            }

            #匹配到全局代码行及时结束
            elsif ( $check_ret && $cli =~ /^(\S)/ ) {

                #改写代码块状态
                $check_ret = 0;
                last;
            }

            #将代码块内命令行重组,去除收尾空白字符
            elsif ($check_ret) {
                $cli =~ s/^\s+//;
                $cli =~ s/^\s*$//;
                $cli =~ s/\s+$//;
				next unless $cli;

                #将代码块内代码添加到属组中
                push @ret, $cli if $cli;
            }
        }
    }

    #------------------------------------------------------------------
    # 基线命中检查逻辑
    #------------------------------------------------------------------
    my ( $regex, $result, $report );
    $report->{"cli_code"} = $cli_code;

    foreach my $cmd (@line) {

        #将代码块内命令行重组,去除收尾空白字符
        $cmd =~ s/^\s+//;
        $cmd =~ s/^\s*$//;
        $cmd =~ s/\s+$//;
		next unless $cmd;

        #策略比对结果数据结构
        $report->{"same"} ||= [];
        $report->{"diff"} ||= [];

        #跳过描述性命令行
        if ( $cmd =~ /^\s*(\!|\#).*/ ) {
            next;
        }

        #捕捉模糊匹配命令行
        elsif ( $cmd =~ /^\s*\~\s*\/(?<matched>.*)\// ) {
            $regex = $+{matched};

            if ( scalar @ret ) {
                my $is_matched = 0;
                foreach my $line (@ret) {
                    if ( $line =~ /$regex/ ) {
                        push @{ $report->{"same"} }, $line;
                        $is_matched = 1;
                        last;
                    }
                }

                #遍历元素仍未匹配则不存在该基线
                push @{ $report->{"diff"} }, $cmd unless $is_matched;
            }
            else {
                my $is_matched = 0;
                foreach my $line (@config) {
                    $line =~ s/^\s+//;
                    $line =~ s/^\s*$//;
                    $line =~ s/\s+$//;

                    if ( $line =~ /$regex/ ) {
                        push @{ $report->{"same"} }, $line;
                        $is_matched = 1;
                        last;
                    }
                }

                #遍历元素仍未匹配则不存在该基线
                push @{ $report->{"diff"} }, $cmd unless $is_matched;
            }
        }

        #捕捉需要排除比对的命令
        elsif ( $cmd =~ /^\s*\+no\s+\/(?<exclude>.*)\/$/ ) {
            $regex = $+{exclude};
            next;
        }

        #比对配置是否完全匹配
        else {
            if ( scalar @ret ) {
                my $is_matched = 0;
                foreach my $line (@ret) {
                    if ( $cmd eq $line ) {
                        push @{ $report->{"same"} }, $line;
                        $is_matched = 1;
                        last;
                    }
                }
                push @{ $report->{"diff"} }, $cmd unless $is_matched;
            }
            else {
                my $is_matched = 0;
                foreach my $line (@config) {
                    $line =~ s/^\s+//;
                    $line =~ s/^\s*$//;
                    $line =~ s/\s+$//;
                    if ( $cmd eq $line ) {
                        push @{ $report->{"same"} }, $line;
                        $is_matched = 1;
                        last;
                    }
                }
                push @{ $report->{"diff"} }, $cmd unless $is_matched;
            }
        }
    }

    #输出报告结果
    $report->{"status"} = ( scalar @{$report->{"diff"}} > 0 ) ? "FAIL" : "PASS";
    return $report;
}

1;

__END__
=head1 NAME

Net::Compare - 网络配置基线巡检模块

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';


=head1 SYNOPSIS

Quick summary of what the module does.

Perhaps a little code snippet.

    #应用模块自动导出compare函数
    use Net::Compare;

    say Dumper compare($line, $config);
    ...

=head1 EXPORT

  compare($line, $config);
  $line => '基线配置';
  $config => "运行配置";

=head1 SUBROUTINES/METHODS

=head2 function1

=cut

sub function1 {
}

=head2 function2

=cut

sub function2 {
}

=head1 AUTHOR

WENWU YAN, C<< <careline at 126.com> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-net-compare at rt.cpan.org>, or through
the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-Compare>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Net::Compare


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker (report bugs here)

L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-Compare>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Net-Compare>

=item * CPAN Ratings

L<https://cpanratings.perl.org/d/Net-Compare>

=item * Search CPAN

L<https://metacpan.org/release/Net-Compare>

=back


=head1 ACKNOWLEDGEMENTS


=head1 LICENSE AND COPYRIGHT

This software is Copyright (c) 2019 by WENWU YAN.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)


=cut
