package FileTracker::Matcher;

use strict;
use warnings;
use Carp;

use lib '/app/tools/rps/lib';
use lib '/app/tools/sale_import/lib';
use RPS::Sale::Match;
use File::Util;

use lib '/app/tools/data_classes/lib';
use lib '/app/tools/rps/lib';
use Client::Service;
use RPS::File::File;
use RPS::File::Sale;
use Product::ProductAlbum;
use Product::ProductTrack;

use lib ('/app/tools/common/lib');
use Common::Consts;
use Common::Util;
use RSApache::Session;

# in leiu of a "new" subroutine.
use RSApache::RSApp;
use base 'RSApache::RSApp';

# set up common vars for this app
my $Session;
my $CurrentUser;
my $Sale;
my $File;

sub execute {
    my $self = shift;
    $self->Stylesheet("matcher.xsl");

    # ---------------------------------------
    # check login
    # ---------------------------------------
    $Session = new RSApache::Session();
    if ( !$Session->IsValid() ) {

        # redirect them
        $self->Redirect( $Common::Consts::URLS{login} . "?redirect=" . $self->GetRequestString() );
        return 1;
    }

    # ---------------------------------------
    # check general access
    # ---------------------------------------
    $CurrentUser = $Session->ActiveUser;
    $self->SetNavigationAccess($CurrentUser);
    my $app_access = $CurrentUser->CanAccess( $self->App );
    if ($app_access) {
        $self->{xml}->{CurrentUser} = $CurrentUser->GetObjectXML();
    } else {
        $self->{xml}->{CurrentUser}  = $CurrentUser->GetObjectXML();
        $self->{xml}->{AccessDenied} = 1;
        return 1;
    }

    # ---------------------------------------
    # if we have a file_id param, then
    # lookup the first sale_id and redirect
    # ---------------------------------------
    my $file_param = $self->GetParam("file");
    if ( defined $file_param ) {
        if ( my $line_num = $self->GetParam('line') ) {
            $File = RPS::File::File->new( file_id => $file_param );
            $self->{xml}->{File} = $File->GetObjectXML();

            $Sale = RPS::File::Sale->new();

            unless ( $Sale->GetByLineNumber( file_id => $file_param, line_num => $line_num ) ) {
                print STDERR "no match\n";
                return 1;
            }

            $self->SetNavLinks();

            # get Sale xml
            $self->{xml}->{Sale} = $Sale->GetObjectXML();

            $self->ShowMatchOptions( search_only => 1 );

            return 1;
        }

        my $sale_id = RPS::File::Sale->GetFirstSaleError( file_id => $file_param );
        if ($sale_id) {
            $self->Redirect("/app/matcher?sale=$sale_id");
            return 1;
        }

        $sale_id = RPS::File::Sale->GetFirstException( file_id => $file_param );
        if ($sale_id) {
            $self->Redirect("/app/matcher?sale=$sale_id");
            return 1;
        }

        ## no exceptions to begin with
        $self->Redirect("/app/tracker");
    }

    # ---------------------------------------
    # load sale record and file record
    # ---------------------------------------
    my $sale_param = $self->GetParam("sale");
    if ( defined $sale_param ) {
        $Sale = new RPS::File::Sale( sale_id => $sale_param );
    } else {

        # redirect them to the tracker summary page
        $self->AddMessageXML( type => "error", code => "invalid_sale" );
        return 1;
    }

    $File = RPS::File::File->new( file_id => $Sale->FileID );

    # sanity check
    if ( !defined $File ) {

        # redirect them to the tracker summary page
        $self->AddMessageXML( type => "error", code => "invalid_sale" );
        return 1;
    }

    # --------------------------------------
    # Are we removing a match?
    # --------------------------------------
    my $cmd_param = $self->GetParam("cmd");
    if ( $cmd_param eq 'unmatch' ) {
        my $map_id = $Sale->MapID;

        if ( $Sale->CanUnmatch( map_id => $map_id ) ) {

            ## find what sales uses this map
            my $sale_records = $Sale->GetSalesToUnmatch();

            ## do it on current sale (has to be here, or else won't be updated
            ## through the rest of this script.
            $Sale->Unmatch();
            $File->UpdateRemainingExceptions('unmatch');

            ## do it on all other sales
            while ( my ( $sale_id, $file_id ) = each %$sale_records ) {
                next if ( $sale_id == $Sale->SaleID );    ## skip over self; already done

                my $temp_sale = RPS::File::Sale->new( sale_id => $sale_id );
                $temp_sale->Unmatch();

                my $temp_file = RPS::File::File->new( file_id => $file_id );
                $temp_file->UpdateRemainingExceptions('unmatch');
            }

            ## delte the map
            my $sm = new RPS::Sale::Match( client_id => $self->ClientID() );
            $sm->RemoveMatch( map_id => $map_id );

        } else {
            $self->AddMessageXML( type => "error", code => "unmatch_failed" );
        }
    }

    # --------------------------------------
    # Are we making a match?
    # --------------------------------------
    my $product_param = $self->GetParam("product");
    if ($product_param) {
        my $success = $Sale->MakeMatch( $product_param, $File->ServiceID );

        if ( !$success ) {
            $self->AddMessageXML( type => "error", code => "match_failed" );
            return 1;
        }

        # update the stats on this file
        $File->UpdateRemainingExceptions();

        # redirect to next error
        # next error
        my $next_sale_id = $Sale->GetNextUnfixedID() || $Sale->GetPreviousUnfixedID();
        if ($next_sale_id) {
            print STDERR "next exc: $next_sale_id\n";
            $self->Redirect("/app/matcher?sale=$next_sale_id");
            return 1;
        } else {

            # recalculate remaining exceptions (just for sanity's sake)
            $File->RecalculateRemainingExceptions();

            # all done with exceptions...
        }
    }

    # --------------------------------------
    # Show matching options
    # --------------------------------------
    $self->ShowMatchOptions();

    # --------------------------------------
    # Show matching options
    # --------------------------------------
    $self->SetNavLinks();

    # get Sale xml
    $self->{xml}->{Sale} = $Sale->GetObjectXML();

    # get File xml
    $self->{xml}->{File} = $File->GetObjectXML();

    return 1;
}

# ------------------------------------------------
# Sub Commands Within This App
# ------------------------------------------------

sub SetNavLinks {
    my $self = shift;

    # previous error
    my $tmp_sale_id = $Sale->GetPreviousErrorID();
    $self->{xml}->{PreviousError} = $tmp_sale_id;

    # previous unfixed error
    $tmp_sale_id = $Sale->GetPreviousUnfixedID();
    $self->{xml}->{PreviousUnfixed} = $tmp_sale_id;

    # next error
    $tmp_sale_id = $Sale->GetNextErrorID();
    $self->{xml}->{NextError} = $tmp_sale_id;

    # next unfixed error
    $tmp_sale_id = $Sale->GetNextUnfixedID();
    $self->{xml}->{NextUnfixed} = $tmp_sale_id;
}

sub ShowMatchOptions {
    my $self = shift;
    my %args = @_;

    # make sure it's still unmatched
    if (
        !$Sale->ProductID
        && (   $Sale->ImportStatus == File::Sale::STATUS_NOMATCH
            || $Sale->ImportStatus == File::Sale::STATUS_MULTIMATCH )
      ) {
        my $matches;
        my $search_param = $self->GetParam("search");
        if ($search_param) {

            # user search for matches
            my $search_type_param = $self->GetParam("search_type");
            $matches = $Sale->GetSearchMatches( search => $search_param, search_type => $search_type_param );
        } else {

            # our suggested matches
            my $file = RPS::File::File->new( file_id => $Sale->FileID );
            $matches = $Sale->GetMatches( search_only => $args{search_only} || 0, service_id => $file->ServiceID );
        }

        # this sale record already has a match
        if ( !defined $matches ) {
            $self->AddMessageXML( type => "error", code => "no_matches" );
        } elsif ( ref($matches) eq 'ARRAY' ) {
            foreach my $prod_id (@$matches) {
                my $prod;
                if ( $Sale->ProductType =~ /^a$/i || $Sale->ProductType =~ /^\d$/i ) {
                    $prod = new Product::ProductAlbum( product_id => $prod_id );
                } elsif ( $Sale->ProductType =~ /^t$/i ) {
                    $prod = new Product::ProductTrack( product_id => $prod_id );
                }

                push @{ $self->{xml}->{Products}->{Product} }, $prod->GetObjectXML();

                last if ( $. > 10 );
            }
        }

        # we found a match internally
        elsif ( $matches == 1 ) {
            if ( $args{search_only} ) {
                push @{ $self->{xml}->{Products}->{Product} }, _get_product_xml();
                return;
            }

            # just kidding, we really did have a match for this one.
            # just go to the next one
            $File->UpdateRemainingExceptions();
            my $tmp_sale_id = $Sale->GetNextUnfixedID() || $Sale->GetPreviousUnfixedID;

            if ($tmp_sale_id) {
                $self->Redirect( "/app/" . $self->App . "?sale=$tmp_sale_id" );
            } else {

                # recalculate remaining exceptions (just for sanity's sake)
                $File->RecalculateRemainingExceptions();

                ## all done with exceptions here...
            }
        }
    }

    # otherwise show what the match is
    else {
        push @{ $self->{xml}->{Products}->{Product} }, _get_product_xml();
    }
}

sub _get_product_xml {
    my $prod;

    if ( $Sale->ProductType =~ /^a$/i || $Sale->ProductType =~ /^\d$/i ) {
        $prod = new Product::ProductAlbum( product_id => $Sale->ProductID );
    } elsif ( $Sale->ProductType =~ /^t$/i ) {
        $prod = new Product::ProductTrack( product_id => $Sale->ProductID );
    }

    return $prod->GetObjectXML();
}

###
1;    # Play nicely.
###
