use strict;
use warnings;

our @ScripConditions;

our @Initial = (
    sub {
        my $searches = RT::Attributes->new( RT->SystemUser );
        $searches->Limit( FIELD => 'Name', VALUE => 'SavedSearch' );
        $searches->OrderBy( FIELD => 'id' );

        while ( my $search = $searches->Next ) {
            my $content = $search->Content;
            next unless ref $content eq 'HASH';

            if ( $content->{OrderBy} ) {
                my @order_by = split /\|/, $content->{OrderBy};
                my @new_order_by;
                my $changed;
                for my $order_by (@order_by) {
                    if ( $order_by eq 'Owner' ) {
                        push @new_order_by, 'Owner.Name';
                        $changed = 1;
                    }
                    else {
                        push @new_order_by, $order_by;
                    }
                }
                if ($changed) {
                    $content->{OrderBy} = join '|', @new_order_by;
                    my ( $ok, $msg ) = $search->SetContent($content);
                    RT->Logger->error("Unable to upgrade saved chart #@{[$search->id]}: $msg")
                        unless $ok;
                }
            }
        }
    },
    sub {
        my @conditions = (
            {   Name                 => 'On Create Via Email',
                Description          => 'When a ticket is created via Email',
                ApplicableTransTypes => 'Create',
                Argument             => 'Email',
                ExecModule           => 'ViaInterface',
            },
            {   Name                 => 'On Create Via Web',
                Description          => 'When a ticket is created via Web',
                ApplicableTransTypes => 'Create',
                Argument             => 'Web,Mobile',
                ExecModule           => 'ViaInterface',
            },
        );
        for my $condition ( @conditions ) {
            my $object = RT::ScripCondition->new(RT->SystemUser);
            $object->Load($condition->{Name});
            if ( $object->Id ) {
                RT->Logger->info("Found scrip condition '$condition->{Name}', skipping");
            }
            else {
                push @ScripConditions, $condition;
            }
        }
    },
    # add default reports
    sub {
        my $reports_in_menu = 'ReportsInMenu';
        my $attr            = RT::Attribute->new( RT->SystemUser );
        $attr->LoadByNameAndObject( Object => RT->System, Name => $reports_in_menu );

        # Update menu if it's not touched by anyone else
        if ( $attr->Id && $attr->Created eq $attr->LastUpdated ) {
            RT->Logger->debug("Adding time worked report in menu");
            my $content = $attr->Content or return;
            splice @$content, 3, 0,
                {   id    => 'user_time',
                    title => 'User time worked',
                    path  => '/Reports/TimeWorkedReport.html',
                };

            my ( $ret, $msg ) = $attr->SetContent($content);
            if ( !$ret ) {
                RT->Logger->error("Couldn't update ReportsInMenu: $msg");
            }
        }
    }
);

my $orig_sync_link = \&RT::Attribute::_SyncLinks;
no warnings 'redefine';
*RT::Attribute::_SyncLinks = sub {
    my $self = shift;
    my $name = $self->__Value('Name');

    my $success;

    if ( $name eq 'Dashboard' ) {
        my $content = $self->_DeserializeContent( $self->__Value('Content') );

        my %searches = map { $_->{id} => 1 } grep { $_->{portlet_type} eq 'search' } @{ $content->{Panes}{body} },
            @{ $content->{Panes}{sidebar} };

        my $links = $self->DependsOn;
        while ( my $link = $links->Next ) {
            next if delete $searches{ $link->TargetObj->id };
            my ( $ret, $msg ) = $link->Delete;
            if ( !$ret ) {
                RT->Logger->error( "Couldn't delete link #" . $link->id . ": $msg" );
                $success //= 0;
            }
        }

        for my $id ( keys %searches ) {
            my $link = RT::Link->new( $self->CurrentUser );
            my $attribute = RT::Attribute->new( $self->CurrentUser );
            $attribute->Load($id);
            if ( $attribute->id ) {
                my ( $ret, $msg )
                    = $link->Create( Type => 'DependsOn', Base => 'attribute:' . $self->id, Target => "attribute:$id" );
                if ( !$ret ) {
                    RT->Logger->error( "Couldn't create link for attribute #:" . $self->id . ": $msg" );
                    $success //= 0;
                }
            }
        }
    }
    elsif ( $name =~ /DefaultDashboard$/ ) {
        my $id    = $self->__Value('Content');
        my $links = $self->DependsOn;
        my $found;
        while ( my $link = $links->Next ) {
            if ( $link->TargetObj->id == $id ) {
                $found = 1;
            }
            else {

                my ( $ret, $msg ) = $link->Delete;
                if ( !$ret ) {
                    RT->Logger->error( "Couldn't delete link #" . $link->id . ": $msg" );
                    $success //= 0;
                }
            }
        }

        if ( !$found ) {
            my $link      = RT::Link->new( $self->CurrentUser );
            my $attribute = RT::Attribute->new( $self->CurrentUser );
            $attribute->Load($id);
            if ( $attribute->id ) {
                my ( $ret, $msg )
                    = $link->Create( Type => 'DependsOn', Base => 'attribute:' . $self->id, Target => "attribute:$id" );
                if ( !$ret ) {
                    RT->Logger->error( "Couldn't create link for attribute #:" . $self->id . ": $msg" );
                    $success //= 0;
                }
            }
        }
    }

    return $success // 1;
};

our @Final = (
    sub {
        RT->Logger->debug("Converting homepages to dashboards");
        my $attrs = RT::Attributes->new( RT->SystemUser );
        $attrs->Limit( FIELD => 'Name', VALUE => [ 'Pref-HomepageSettings', 'HomepageSettings' ], OPERATOR => 'IN' );
    OUTER: while ( my $attr = $attrs->Next ) {
            my $attr_id = $attr->Id;
            my $object  = $attr->Object;
            my $content = $attr->Content;

            if ( $object && ( $object->isa('RT::User') || $object->isa('RT::System') ) && $content ) {
                my $panes     = {};

                for my $pane ( sort keys %$content ) {
                    my $list = $content->{$pane} or next;
                    for my $entry (@$list) {
                        my $new_entry = { pane => $pane };
                        if ( $entry->{type} eq 'system' ) {
                            if ( my $name = $entry->{name} ) {
                                my ($search)
                                    = RT::System->new( RT->SystemUser )->Attributes->Named( 'Search - ' . $name );

                                # Check user created system searches
                                if ( !$search ) {
                                    my (@searches)
                                        = RT::System->new( RT->SystemUser )->Attributes->Named('SavedSearch');
                                    for my $custom (@searches) {
                                        if ( $custom->Description eq $entry->{name} ) {
                                            $search = $custom;
                                            last;
                                        }
                                    }
                                }

                                if ( $search ) {
                                    $new_entry->{portlet_type} = 'search';
                                    $new_entry->{id}           = $search->Id;
                                    $new_entry->{description}  = "Saved Search: $name";
                                    $new_entry->{privacy}      = 'RT::System-1';
                                }
                                else {
                                    RT->Logger->warning(
                                        "System search $name in attribute #$attr_id not found, skipping");
                                    next;
                                }
                            }
                            else {
                                RT->Logger->warning("Missing system search name in attribute #$attr_id, skipping");
                                next;
                            }
                        }
                        elsif ( $entry->{type} eq 'saved' ) {
                            if ( my $name = $entry->{name} ) {
                                if ( $name =~ /(.+)-SavedSearch-(\d+)/ ) {
                                    $new_entry->{privacy}      = $1;
                                    $new_entry->{id}           = $2;
                                    $new_entry->{portlet_type} = 'search';
                                    my $search = RT::Attribute->new( RT->SystemUser );
                                    $search->Load( $new_entry->{id} );
                                    if ( $search->Id ) {
                                        $new_entry->{description} = "Saved Search: " . $search->Description;
                                    }
                                    else {
                                        RT->Logger->warning(
                                            "Saved search $name in attribute #$attr_id not found, skipping");
                                        next;
                                    }
                                }
                                else {
                                    RT->Logger->warning(
                                        "System search $name in attribute #$attr_id not found, skipping");
                                    next;
                                }
                            }
                            else {
                                RT->Logger->warning("Missing system search name in attribute #$attr_id, skipping");
                                next;
                            }
                        }
                        elsif ( $entry->{type} eq 'component' ) {
                            $new_entry->{portlet_type} = 'component';
                            $new_entry->{component}    = $entry->{name};
                            $new_entry->{description}  = $entry->{name};
                            $new_entry->{path}         = "/Elements/$entry->{name}";
                        }
                        else {
                            RT->Logger->warning("Unsupported type $entry->{type} in attribute #$attr_id, skipping");
                            next;
                        }
                        push @{$panes->{$pane}}, $new_entry;
                    }
                }

                $RT::Handle->BeginTransaction;
                my %new_values = (
                    'Name'        => 'Dashboard',
                    'Description' => 'Homepage',
                    'Content'     => { Panes => $panes },
                );

                for my $field ( sort keys %new_values ) {
                    my $method = "Set$field";
                    my ( $ret, $msg ) = $attr->$method( $new_values{$field} );
                    if ( !$ret ) {
                        RT->Logger->error("Couldn't update $field of attribute #$attr_id: $msg");
                        $RT::Handle->Rollback;
                        next OUTER;
                    }
                }

                my ( $id, $msg ) = $object->SetAttribute(
                    'Name'        => $object->isa('RT::User') ? 'Pref-DefaultDashboard' : 'DefaultDashboard',
                    'Description' => 'Default Dashboard',
                    'Content'     => $attr_id,
                );
                if ($id) {
                    $RT::Handle->Commit;
                }
                else {
                    RT->Logger->error("Couldn't set DefaultDashboard to $id for attribute #$attr_id: $msg");
                    $RT::Handle->Rollback;
                }
            }
        }
    },
    sub {
        my $acl = RT::ACL->new(RT->SystemUser);

        # Grant dashboard rights so users with ModifySelf can still
        # customize MyRT
        RT->Logger->debug("Granting dashboard rights to users with ModifySelf");
        $acl->Limit( FIELD => 'RightName', VALUE => 'ModifySelf' );
        while ( my $ace = $acl->Next ) {
            my $object = $ace->Object;
            my $principal = $ace->PrincipalObj;

            for my $right ( 'SeeOwnDashboard', 'AdminOwnDashboard', 'SeeOwnSavedSearch', 'AdminOwnSavedSearch' ) {
                if ( !$principal->HasRight( Object => $object, Right => $right ) ) {
                    my ( $ret, $msg ) = $principal->GrantRight( Object => $object, Right => $right );
                    if ( !$ret ) {
                        RT->Logger->error( "Couldn't grant $right to user #" . $object->Id . ": $msg" );
                    }
                }
            }
        }
    },
    sub {
        my $role_groups = RT::Groups->new( RT->SystemUser );
        $role_groups->{'find_disabled_rows'} = 1;
        $role_groups->Limit( FIELD => 'Name',   VALUE => 'RT::CustomRole-', OPERATOR => 'LIKE', CASESENSITIVE => 0 );
        $role_groups->Limit( FIELD => 'Domain', VALUE => '-Role',           OPERATOR => 'LIKE', CASESENSITIVE => 0 );
        $role_groups->LimitToDeleted;

        while ( my $role_group = $role_groups->Next ) {
            my ( $ret, $msg ) = $role_group->SetDisabled( 0 );
            RT->Logger->error( "Couldn't enable role group #" . $role_group->id . ": $msg" ) unless $ret;
        }
    },
    sub {
        my $attrs = RT::Attributes->new( RT->SystemUser );
        $attrs->Limit(
            FIELD    => 'Name',
            VALUE    => [ 'Dashboard', 'DefaultDashboard', 'Pref-DefaultDashboard' ],
            OPERATOR => 'IN',
        );
        while ( my $attr = $attrs->Next ) {
            my ( $ret, $msg ) = $attr->_SyncLinks;
            if ( !$ret ) {
                die "Couldn't sync links for attribute #" . $attr->id . ": $msg";
            }
        }
    },
    sub {
        *RT::Attribute::_SyncLinks = $orig_sync_link;
    },
);
