use strict;
use warnings;

my @OUR_QUEUES = ('Incidents', 'Incident Reports', 'Investigations', 'Blocks');

our @Initial = (
    sub {
        return 1 if grep $_ eq 'RT::IR', RT->Config->Get('Plugins');

        die "Please enable RT::IR plugin in the config file.\n";
    },
);

our @ScripConditions = (
    {  Name                 => 'RTIR Require IRs Activation',               # loc
       Description          => "Whenever it's time to activate report",                                        # loc
       ApplicableTransTypes => 'Correspond',
       ExecModule           => 'RTIR_RequireReportActivation',
    },
);

our @ScripActions = (
    {  Name        => 'RTIR Set Block Status',    # loc
       Description => 'Set the status of a Block' ,                                            # loc
       ExecModule => 'RTIR_SetBlockStatus',
    },
    {  Name        => 'RTIR Activate Ticket',    # loc
       Description => 'Set status to first active possible',            # loc
       ExecModule  => 'RTIR_Activate',
    },
);

our @Scrips = (
    {  Description       => "On Correspond Change Status of the Block",
       Queue             => 'Blocks',
       ScripCondition    => 'On Correspond',
       ScripAction       => 'RTIR Set Block Status',
       Template          => 'Blank', },
    {  Description       => "On Linking To Incident Activate Report",
       Queue             => 'Incident Reports',
       ScripCondition    => 'RTIR Linking To Incident',
       ScripAction       => 'RTIR Activate Ticket',
       Template          => 'Blank' },
    {  Description       => "On Correspond (not every) Activate Report",
       Queue             => 'Incident Reports',
       ScripCondition    => 'RTIR Require IRs Activation',
       ScripAction       => 'RTIR Activate Ticket',
       Template          => 'Blank' },
);

sub remove_scrip_action {
    my $module = shift;

    my $actions = RT::ScripActions->new( $RT::SystemUser );
    $actions->Limit( FIELD => 'ExecModule', VALUE => $module );
    while ( my $action = $actions->Next ) {
        my $scrips = RT::Scrips->new( $RT::SystemUser );
        $scrips->Limit( FIELD => 'ScripAction', VALUE => $action->id );
        while ( my $scrip = $scrips->Next ) {
            $scrip->Delete;
        }
        $action->DBIx::SearchBuilder::Record::Delete();
    }
}

sub remove_scrip_condition {
    my $module = shift;

    my $conditions = RT::ScripConditions->new( $RT::SystemUser );
    $conditions->Limit( FIELD => 'ExecModule', VALUE => $module );
    while ( my $condition = $conditions->Next ) {
        my $scrips = RT::Scrips->new( $RT::SystemUser );
        $scrips->Limit( FIELD => 'ScripCondition', VALUE => $condition->id );
        while ( my $scrip = $scrips->Next ) {
            $scrip->Delete;
        }
        $condition->DBIx::SearchBuilder::Record::Delete();
    }
}

sub get_queue {
    my $qname = shift;

    my $queue = RT::Queue->new( $RT::SystemUser );
    $queue->Load( $qname );
    unless ( $queue->id ) {
        $RT::Logger->error("Couldn't load queue '$qname'");
        return;
    }
    return $queue;
}

sub get_queue_and_state_cf {
    my $queue = get_queue( @_ );
    return unless $queue;

    my $cf = RT::CustomField->new( $RT::SystemUser );
    $cf->LoadByName( Name => 'State', Queue => $queue->id );
    unless ( $cf->id ) {
        $RT::Logger->error("Couldn't load State CF applied to queue '". $queue->Name ."'");
        return;
    }
    return ($queue, $cf);
}

sub custom_field_real_values {
    my $cf = shift;
    my %args = @_;
    my $values = RT::ObjectCustomFieldValues->new( $RT::SystemUser );
    $values->Limit( FIELD => 'CustomField', VALUE => $cf->id );
    $values->Limit( FIELD => 'Disabled', VALUE => 0 )
        unless $args{'Inactive'};
    return grep defined && length,
        $values->DistinctFieldValues('Content');
}

our @Final = (

    sub {
        $RT::Logger->debug("Going to change type of IP custom field");
        my $ip_cf = RT::CustomField->new($RT::SystemUser);
        $ip_cf->Load('IP');
        if ( $ip_cf->id ) {
            $ip_cf->SetType('IPAddressRange');
            $ip_cf->SetMaxValue(0);
            $ip_cf->SetPattern('');
        }
    },

    sub {
        $RT::Logger->debug("Going to set lifecycles for RTIR's queues");
        for my $name ( @OUR_QUEUES ) {
            my $queue = get_queue( $name );
            next unless $queue;

            my $new_cycle = lc $name;
            $new_cycle =~ s/ /_/;

            if ( $queue->Lifecycle->Name eq $new_cycle ) {
                $RT::Logger->debug("Queue $name already uses '$new_cycle' lifecycle");
                next;
            }

            my ($status, $msg) = $queue->SetLifecycle( $new_cycle );
            unless ( $status ) {
                $RT::Logger->error("Couldn't change lifecycle: $msg");
                next;
            }

            $RT::Logger->info("Changed lifecycle of queue '$name' to '$new_cycle'");
        }
    },

    # in RTIR queues for variouse reasons we kept Status and State in
    # sync with each other and were hiding these txns in the UI. Now
    # we delete old status changes
    # and convert State changes into Status changes

    sub {
        $RT::Logger->debug("Going to update tickets' Status column from ObjectCustomFieldValues table");
        for my $qname ( @OUR_QUEUES ) {
            my ($queue, $cf) = get_queue_and_state_cf( $qname );
            next unless $queue && $cf;

            my @states = custom_field_real_values( $cf );

            foreach my $state ( @states ) {
                my $values = RT::ObjectCustomFieldValues->new( $RT::SystemUser );
                $values->Limit( FIELD => 'CustomField', VALUE => $cf->id );
                $values->Limit( FIELD => 'Disabled', VALUE => 0 );
                $values->Limit( FIELD => 'Content', VALUE => $state );
                $values->Columns('ObjectId');

                my $res = $RT::Handle->SimpleUpdateFromSelect(
                    'Tickets', { Status => $state }, $values->BuildSelectQuery
                );
                $RT::Logger->error("Couldn't update tickets: $res")
                    unless $res;
            }
        }
    },

    sub {
        $RT::Logger->debug("Going to update txn's OldValue and NewValue from OCFVs table");
        for my $qname ( @OUR_QUEUES ) {
            my ($queue, $cf) = get_queue_and_state_cf( $qname );
            next unless $queue && $cf;

            my @states = custom_field_real_values( $cf, Inactive => 1 );

            foreach my $old_new (qw(Old New)) { foreach my $state ( @states ) {
                my $txns = RT::Transactions->new( $RT::SystemUser );
                $txns->Limit( FIELD => ObjectType => VALUE => 'RT::Ticket' );
                $txns->Limit( FIELD => Type => VALUE => 'CustomField' );
                $txns->Limit( FIELD => Field => VALUE => $cf->id );
                my $ocfv_alias = $txns->Join(
                    ALIAS1 => 'main',
                    FIELD1 => $old_new .'Reference',
                    TABLE2 => 'ObjectCustomFieldValues',
                    FIELD2 => 'id',
                );
                $txns->Limit( ALIAS => $ocfv_alias, FIELD => 'Content', VALUE => $state );
                $txns->Limit( ALIAS => $ocfv_alias, FIELD => 'CustomField', VALUE => $cf->id );
                $txns->Columns('id');

                my $res = $RT::Handle->SimpleUpdateFromSelect(
                    'Transactions', { $old_new .'Value' => $state }, $txns->BuildSelectQuery
                );
                $RT::Logger->error("Couldn't update transactions")
                    unless $res;
            } }
        }
    },

    sub {
        $RT::Logger->debug("Going to delete records from ObjectCustomFieldValues");
        for my $qname ( @OUR_QUEUES ) {
            my ($queue, $cf) = get_queue_and_state_cf( $qname );
            next unless $queue && $cf;

            foreach my $old_new (qw(Old New)) {
                my $txns = RT::Transactions->new( $RT::SystemUser );
                $txns->Limit( FIELD => ObjectType => VALUE => 'RT::Ticket' );
                $txns->Limit( FIELD => Type => VALUE => 'CustomField' );
                $txns->Limit( FIELD => Field => VALUE => $cf->id );
                $txns->Limit(
                    FIELD    => $old_new .'Reference',
                    OPERATOR => 'IS NOT',
                    VALUE    => 'NULL'
                );
                $txns->Columns( $old_new .'Reference' );

                my $res = $RT::Handle->DeleteFromSelect(
                    'ObjectCustomFieldValues', $txns->BuildSelectQuery
                );
                $RT::Logger->error("Couldn't delete ObjectCustomFieldValues")
                    unless $res;
            }
        }
    },

    # delete tickets' Status changes (txns) in RTIR queues,
    sub {
        for my $qname ( @OUR_QUEUES ) {
            my $queue = get_queue( $qname ) or next;

            my $query = "SELECT txn.id FROM Transactions txn JOIN Tickets t"
                ." WHERE txn.ObjectType = 'RT::Ticket' AND txn.ObjectId = t.id"
                ." AND t.Type = 'ticket' AND t.Queue = ?"
                ." AND ((txn.Type = 'Set' AND txn.Field = 'Status') OR txn.Type = 'Status')"
            ;
            my $res = $RT::Handle->DeleteFromSelect( 'Transactions', $query, $queue->id );
            $RT::Logger->error("Couldn't delete transactions: $res")
                unless $res;
        }
    },

    # update txns, set Type = 'Set', Field = 'Status' and flush
    # reference fields
    sub {
        for my $qname ( @OUR_QUEUES ) {
            my ($queue, $cf) = get_queue_and_state_cf( $qname );
            next unless $queue && $cf;

            my $txns = RT::Transactions->new( $RT::SystemUser );
            $txns->Limit( FIELD => 'ObjectType', VALUE => 'RT::Ticket' );
            $txns->Limit( FIELD => 'Type', VALUE => 'CustomField' );
            $txns->Limit( FIELD => 'Field', VALUE => $cf->id );
            $txns->Columns('id');

            my $res = $RT::Handle->SimpleUpdateFromSelect(
                'Transactions',
                {
                    Type          => 'Set',
                    Field         => 'Status',
                    ReferenceType => undef,
                    OldReference  => undef,
                    NewReference  => undef,
                },
                $txns->BuildSelectQuery
            );
            $RT::Logger->error("Couldn't update transactions")
                unless $res;
        }
    },

    # un-apply and disable State custom fields
    sub {
        for my $qname ( @OUR_QUEUES ) {
            my ($queue, $cf) = get_queue_and_state_cf( $qname );
            next unless $queue && $cf;

            {
                my ($status, $msg) = $cf->RemoveFromObject( $queue );
                $RT::Logger->error("Couldn't unapply custom field #". $cf->id .": $msg")
                    unless $status;
            }

            unless ( $cf->Disabled ) {
                my ($status, $msg) = $cf->SetDisabled(1);
                $RT::Logger->error("Couldn't disable custom field #". $cf->id .": $msg")
                    unless $status;
            }
        }
    },


    # remove no longer needed scrip actions and scrips based on them
    sub {
        # started date is handled by lifecycles now
        remove_scrip_action('RTIR_SetStartedToNow');

        # IRs and Blocks has some special status treatment, but
        # it is handled by other scrips
        remove_scrip_action('RTIR_SetIncidentReportState');
        remove_scrip_action('RTIR_SetBlockState');

        # Investigations and Incidents don't need special status
        # treatment, it was only sync with State CF
        remove_scrip_action('RTIR_SetInvestigationState');
        remove_scrip_action('RTIR_SetIncidentState');

        # SLA extension has saner Due management
        remove_scrip_action('RTIR_SetDueBySLA');
        remove_scrip_action('RTIR_SetDueCorrespond');
        remove_scrip_action('RTIR_SetDueReopen');
        remove_scrip_action('RTIR_SetDueToNow');
        remove_scrip_action('RTIR_UnsetDue');
        remove_scrip_action('RTIR_SetStartsByBizHours');
        remove_scrip_action('RTIR_SetStartsToNow');
    },

    # remove no longer needed scrip conditions and scrips based on them
    sub {
        # started date is handled by lifecycles now
        remove_scrip_condition('RTIR_RequireStateChange');
        remove_scrip_condition('RTIR_BlockActivation');
    },

    # replace conditions with RT's StatusChange
    sub {
        my %map = (
            RTIR_ReopenTicket => 'old: inactive; new: initial, active',
            RTIR_CloseTicket => 'old: initial, active; new: inactve',
        );
        while ( my ($module, $argument) = each %map ) {
            my $conditions = RT::ScripConditions->new( $RT::SystemUser );
            $conditions->Limit( FIELD => 'ExecModule', VALUE => $module );
            while ( my $condition = $conditions->Next ) {
                my ($status, $msg) = $condition->SetExecModule('StatusChange');
                RT->Logger->error("Couldn't change scrip condition: $msg")
                    unless $status;
                ($status, $msg) = $condition->SetApplicableTransTypes('Status,Set');
                RT->Logger->error("Couldn't change scrip condition: $msg")
                    unless $status;
                ($status, $msg) = $condition->SetArgument( $argument );
                RT->Logger->error("Couldn't change scrip condition: $msg")
                    unless $status;
            }
        }
    },

    sub {
        my ($d_portlets) = RT::System->new( RT->SystemUser )
            ->Attributes->Named('RTIR_HomepageSettings');
        my $content = $d_portlets->Content;
        if ( process_home_page_settings( $content ) ) {
            my ($status, $msg) = $d_portlets->SetContent( $content );
            $RT::Logger->error("Couldn't update content: $msg")
                unless $status;
        }

        my $attrs = RT::Attributes->new( RT->SystemUser );
        $attrs->Limit( FIELD => 'ObjectType', VALUE => 'RT::User' );
        $attrs->Limit( FIELD => 'Name', VALUE => 'Pref-RTIR_HomepageSettings' );
        while ( my $attr = $attrs->Next ) {
            my $content = $attr->Content;
            if ( process_home_page_settings( $content ) ) {
                my ($status, $msg) = $attr->SetContent( $content );
                $RT::Logger->error("Couldn't update content: $msg")
                    unless $status;
            }
        }
    },

    # fix description of a few scrips, it doesn't match meaning
    sub {
        my $scrips = RT::Scrips->new( RT->SystemUser );
        my $alias = $scrips->Join(
            FIELD1 => 'ScripAction',
            TABLE2 => 'ScripActions',
            FIELD2 => 'id',
        );
        $scrips->Limit(
            ALIAS => $alias,
            FIELD => 'ExecModule',
            VALUE => 'RTIR_SetDueIncident',
        );
        while ( my $scrip = $scrips->Next ) {
            my $value = 'Set Due Date On Incident';
            next if $scrip->Name eq $value;

            my ($status, $msg) = $scrip->SetName( $value );
            $RT::Logger->error("Couldn't set scrip's name: $msg")
                unless $status;
        }
    },

    # replace State CF in format strings of saved searches with status
    sub {
        my $searches = RT::Attributes->new( RT->SystemUser );
        $searches->Limit(FIELD => 'Name', VALUE => 'SavedSearch');
        while ( my $search = $searches->Next ) {
            my $props = $search->Content;
            next unless $props && ref($props) eq 'HASH' && $props->{'Format'};

            next unless $props->{'Format'} =~ s{'__CustomField\.{State}__(?:/TITLE:State)?'}{__Status__}g;

            my ($status, $msg) = $search->SetContent( $props );
            RT->Logger->error("Couldn't update saved search: $msg")
                unless $status;
        }
    },
);


sub process_home_page_settings {
    my $content = shift || {};
    my $found = 0;
    my $has_quicksearch =
        grep $_->{'type'} eq 'component' && $_->{'name'} eq 'Quicksearch',
        map @$_, grep $_, values %$content
    ;
    foreach my $a ( grep $_, values %$content ) {
        foreach my $e ( splice @$a ) {
            if (
                $e->{type} eq 'component'
                && $e->{name} eq '/RTIR/Elements/QueueSummary'
            ) {
                $found = 1;
                $e->{name} = 'Quicksearch';
                next if $has_quicksearch;
            }
            push @$a, $e;
        }
    }
    return $found;
}

