use strict;
use warnings;

# Returns true if the ACE's principal already has a DIRECT ACE for $new_right
# on the same object, ignoring inherited (group-membership) rights. Used to
# collapse multiple old rights that converge on one new right for the SAME
# principal (e.g. the three dashboard rights) without deleting a principal's
# own ACE just because a group it belongs to was migrated first. This applies
# to any principal that inherits through membership -- a subgroup of the
# migrated group, or a user who is a member of it. Using HasRight here would
# honor that inheritance and wrongly delete the principal's own direct grant.
sub _has_direct_ace {
    my ( $ace, $new_right ) = @_;
    my $existing = RT::ACL->new( RT->SystemUser );
    $existing->Limit( FIELD => 'RightName',     VALUE => $new_right );
    $existing->Limit( FIELD => 'PrincipalId',   VALUE => $ace->__Value('PrincipalId') );
    $existing->Limit( FIELD => 'PrincipalType', VALUE => $ace->__Value('PrincipalType') );
    $existing->Limit( FIELD => 'ObjectType',    VALUE => $ace->__Value('ObjectType') );
    $existing->Limit( FIELD => 'ObjectId',      VALUE => $ace->__Value('ObjectId') );
    return $existing->Count ? 1 : 0;
}

our @Final = (
    sub {
        RT->Logger->debug("Migrating old saved search rights");
        my $acl = RT::ACL->new( RT->SystemUser );
        $acl->Limit(
            FIELD    => 'RightName',
            VALUE    => [ 'ShowSavedSearches', 'CreateSavedSearch', 'EditSavedSearches' ],
            OPERATOR => 'IN',
        );
        while ( my $ace = $acl->Next ) {
            my $principal = $ace->PrincipalObj;
            my $right     = $ace->_Value('RightName');    # Avoid invalid right name warnings
            my $object    = $ace->Object;

            my $new_right;
            if ( $right eq 'ShowSavedSearches' ) {
                if ( $object->isa('RT::System') ) {
                    $new_right = 'SeeSavedSearch';
                }
                elsif ( $object->isa('RT::Group') ) {
                    $new_right = 'SeeGroupSavedSearch';
                }

            }
            elsif ( $right eq 'CreateSavedSearch' ) {
                $new_right = 'AdminSavedSearch';
            }
            elsif ( $right eq 'EditSavedSearches' ) {
                if ( $object->isa('RT::System') ) {
                    $new_right = 'AdminSavedSearch';
                }
                elsif ( $object->isa('RT::Group') ) {
                    $new_right = 'AdminGroupSavedSearch';
                }
            }

            if ($new_right) {
                if ( _has_direct_ace( $ace, $new_right ) ) {
                    # This principal already has a direct ACE for the new right on
                    # this object (a true duplicate), so just remove the old record.
                    my ( $ret, $msg ) = $ace->DBIx::SearchBuilder::Record::Delete;
                    if ( !$ret ) {
                        RT->Logger->error( "Couldn't delete $right for principal #" . $principal->Id . ": $msg" );
                    }
                }
                else {
                    # No direct new right exists, so update the old record with the new right name
                    my ( $ret, $msg ) = $ace->__Set( Field => 'RightName', Value => $new_right );
                    if ( !$ret ) {
                        RT->Logger->error(
                            "Couldn't rename $right to $new_right for principal #" . $principal->Id . ": $msg" );
                    }
                }
            }
        }
    },
    sub {
        RT->Logger->debug("Migrating old dashboard rights");
        my $acl = RT::ACL->new( RT->SystemUser );
        $acl->Limit(
            FIELD => 'RightName',
            VALUE => [
                'CreateDashboard',      'ModifyDashboard', 'DeleteDashboard', 'CreateGroupDashboard',
                'ModifyGroupDashboard', 'DeleteGroupDashboard',
            ],
            OPERATOR => 'IN',
        );
        while ( my $ace = $acl->Next ) {
            my $principal = $ace->PrincipalObj;
            my $right     = $ace->_Value('RightName');    # Avoid invalid right name warnings
            my $object    = $ace->Object;

            my $new_right = $right =~ /Group/ ? 'AdminGroupDashboard' : 'AdminDashboard';

            if ( _has_direct_ace( $ace, $new_right ) ) {
                # This principal already has a direct ACE for the new right on
                # this object (a true duplicate), so just remove the old record.
                my ( $ret, $msg ) = $ace->DBIx::SearchBuilder::Record::Delete;
                if ( !$ret ) {
                    RT->Logger->error( "Couldn't delete $right for principal #" . $principal->Id . ": $msg" );
                }
            }
            else {
                # No direct new right exists, so update the old record with the new right name
                my ( $ret, $msg ) = $ace->__Set( Field => 'RightName', Value => $new_right );
                if ( !$ret ) {
                    RT->Logger->error(
                        "Couldn't rename $right to $new_right for principal #" . $principal->Id . ": $msg" );
                }
            }
        }
    },
);

1;
