use strict;
use warnings;

### after: use lib qw(@RT_LIB_PATH@);
use lib qw(/opt/rt3/local/lib /opt/rt3/lib);

package RTx::AssetTracker::Test;

our @ISA;
BEGIN {
    local $@;
    eval { require RT::Test; 1 } or do {
        require Test::More;
        Test::More::BAIL_OUT(
            "requires 3.8 to run tests. Error:\n$@\n"
            ."You may need to set PERL5LIB=/path/to/rt/lib"
        );
    };
    push @ISA, 'RT::Test';
}

sub import {
    my $class = shift;
    my %args  = @_;

    $args{'requires'} ||= [];
    if ( $args{'testing'} ) {
        unshift @{ $args{'requires'} }, 'RTx::AssetTracker';
    } else {
        $args{'testing'} = 'RTx::AssetTracker';
    }

    $class->SUPER::import( %args );

    RT->Config->LoadConfig( File => 'AT_Config.pm' );
    require RTx::AssetTracker;
}

=head2 load_or_create_type

=cut

sub load_or_create_type {
    my $self = shift;
    my %args = ( Disabled => 0, @_ );
    my $obj = RTx::AssetTracker::Type->new( RT->SystemUser );
    if ( $args{'Name'} ) {
        $obj->LoadByCols( Name => $args{'Name'} );
    } else {
        die "Name is required";
    }
    unless ( $obj->id ) {
        my ($val, $msg) = $obj->Create( %args );
        die "$msg" unless $val;
    }

    return $obj;
}

=head2 load_or_create_asset_custom_field

=cut

sub load_or_create_asset_custom_field {
    my $self = shift;
    my %args = ( Disabled => 0, LookupType => 'RTx::AssetTracker::Type-RTx::AssetTracker::Asset', @_ );
    my $obj = RT::CustomField->new( $RT::SystemUser );
    if ( $args{'Name'} ) {
        $obj->LoadByName( Name => $args{'Name'}, Type => $args{'AssetType'} );
    } else {
        die "Name is required";
    }
    unless ( $obj->id ) {
        my ($val, $msg) = $obj->Create( %args );
        die "$msg" unless $val;
        my $typeobj = RTx::AssetTracker::Type->new( $RT::SystemUser );
        $typeobj->Load( $args{'AssetType'} );
        ($val, $msg) = $obj->AddToObject( $typeobj );
        die "$msg" unless $val;
    }

    return $obj;
}

sub delete_type_watchers {
    my $self = shift;
    my @types = @_;

    foreach my $q ( @types ) {
        foreach my $t ( RTx::AssetTracker::Type->RoleGroupTypes ) {
            $q->DeleteWatcher( Type => $t, PrincipalId => $_->MemberId )
                foreach @{ $q->$t()->MembersObj->ItemsArrayRef };
        }
    }
}

sub create_assets {
    local $Test::Builder::Level = $Test::Builder::Level + 1;

    my $self = shift;
    my $defaults = shift;
    my @data = @_;
    @data = sort { rand(100) <=> rand(100) } @data
        if delete $defaults->{'RandomOrder'};

    $defaults->{'Type'} ||= 'Servers';

    my @res = ();
    my $i = 0;
    while ( @data ) {
        my %args = %{ shift @data };
        $args{$_} = $res[ $args{$_} ]->id foreach
            grep $args{ $_ }, keys %RTx::AssetTracker::Asset::LINKTYPEMAP;
        $args{'Name'} ||= $defaults->{'Type'} . " asset #" . ++$i;
        push @res, $self->create_asset( %$defaults, %args );
    }
    return @res;
}

sub create_asset {
    local $Test::Builder::Level = $Test::Builder::Level + 1;

    my $self = shift;
    my %args = @_;

    unless ( $args{'Name'} ) {
        die "Name is required";
    }
    if ($args{Type} && $args{Type} =~ /\D/) {
        my $type = RTx::AssetTracker::Type->new(RT->SystemUser);
        if (my $id = $type->Load($args{Type}) ) {
            $args{Type} = $id;
        } else {
            die ("Error: Invalid type $args{Type}");
        }
    }

    my $asset = RTx::AssetTracker::Asset->new( RT->SystemUser );
    my ( $id, undef, $msg ) = $asset->Create( %args );
    Test::More::ok( $id, "asset created" )
        or Test::More::diag("error: $msg");

    # hackish, but simpler
    if ( $args{'LastUpdatedBy'} ) {
        $asset->__Set( Field => 'LastUpdatedBy', Value => $args{'LastUpdatedBy'} );
    }


    for my $field ( keys %args ) {
        #TODO check links and watchers

        if ( $field =~ /CustomField-(\d+)/ ) {
            my $cf = $1;
            my $got = join ',', sort map $_->Content,
                @{ $asset->CustomFieldValues($cf)->ItemsArrayRef };
            my $expected = ref $args{$field}
                ? join( ',', sort @{ $args{$field} } )
                : $args{$field};
            Test::More::is( $got, $expected, 'correct CF values' );
        }
        else {
            next if ref $args{$field};
            next unless $asset->can($field) or $asset->_Accessible($field,"read");
            next if ref $asset->$field();
            Test::More::is( $asset->$field(), $args{$field}, "$field is correct" );
        }
    }

    return $asset;
}

sub delete_assets {
    my $self = shift;
    my $query = shift;
    my $assets = RTx::AssetTracker::Assets->new( RT->SystemUser );
    if ( $query ) {
        $assets->FromSQL( $query );
    }
    else {
        $assets->UnLimit;
    }
    while ( my $asset = $assets->Next ) {
        $asset->Delete;
    }
}

sub add_rights {
    my $self = shift;
    my @list = ref $_[0]? @_: @_? { @_ }: ();

    require RT::ACL;
    foreach my $e (@list) {
        my $principal = delete $e->{'Principal'};
        unless ( ref $principal ) {
            if ( $principal =~ /^(everyone|(?:un)?privileged)$/i ) {
                $principal = RT::Group->new( RT->SystemUser );
                $principal->LoadSystemInternalGroup($1);
            } elsif ( my @princ = grep { $_ eq $principal } RTx::AssetTracker::Type->RoleGroupTypes ) {
                $principal = RT::Group->new( RT->SystemUser );
                $principal->LoadByCols(
                    Domain => (ref($e->{'Object'})||'RT::System').'-Role',
                    Type => $princ[0],
                    ref($e->{'Object'})? (Instance => $e->{'Object'}->id): (),
                );
            } else {
                die "principal is not an object, but also is not name of a system group";
            }
        }
        unless ( $principal->isa('RT::Principal') ) {
            if ( $principal->can('PrincipalObj') ) {
                $principal = $principal->PrincipalObj;
            }
        }
        my @rights = ref $e->{'Right'}? @{ $e->{'Right'} }: ($e->{'Right'});
        foreach my $right ( @rights ) {
            my ($status, $msg) = $principal->GrantRight( %$e, Right => $right );
            $RT::Logger->debug($msg);
        }
    }
    return 1;
}

1;
