package Autocache::Strategy::Refresh;

use Any::Moose;

extends 'Autocache::Strategy';

use Autocache;
use Carp;
use Autocache::Logger qw(get_logger);
use Scalar::Util qw( weaken );

#
# Refresh Strategy - freshen content regularly in the background
#

#
# refresh_age : content older than this in seconds will be refreshed in the
# background by a work queue
#
has 'refresh_age' => (
    is => 'rw',
    isa => 'Int',
    default => 60,
);

#
# base_strategy : underlying strategy that handles storage and expiry -
# defaults
#
has 'base_strategy' => (
    is => 'ro',
    isa => 'Autocache::Strategy',
    lazy_build => 1,
);

#
# work_queue : object that provides a work_queue interface to push refresh
# jobs on to
#
has 'work_queue' => (
    is => 'ro',
    isa => 'Autocache::WorkQueue',
    lazy_build => 1,
);

#
# create REQ
#
sub create
{
    my ($self,$req) = @_;
    get_logger()->debug( "create" );
    return $self->base_strategy->create( $req );
}

sub get
{
    my ($self,$req) = @_;
    get_logger()->debug( "get" );
    my $rec = $self->base_strategy->get( $req );


    #
    # TODO - add min refresh time to stop cache stampede for shared caches
    #
    if( $rec and ( $rec->age > $self->refresh_age ) )
    {
        get_logger()->debug( "record age  : " . $rec->age );
        get_logger()->debug( "refresh age : " . $self->refresh_age );

        $self->work_queue->push( $self->_refresh_task( $req, $rec ) );
    }

    return $rec;
}

#
# REQ REC
#
sub set
{
    my ($self,$req,$rec) = @_;
    get_logger()->debug( "set " . $req->name );
    return $self->base_strategy->set( $req, $rec );
}

sub _refresh_task
{
    my ($self,$req,$rec) = @_;

    get_logger()->debug( "_refresh_task " . $rec->name );

    weaken $self;

    return sub
    {
        get_logger()->debug( "refreshing record: " . $rec->to_string );
        my $fresh_rec = $self->create( $req );
        $self->set( $fresh_rec );
    };
}

#
# delete KEY
#
sub delete
{
    my ($self,$key) = @_;
    return $self->base_strategy->delete( $key );
}

sub clear
{
    my ($self) = @_;
    return $self->base_strategy->clear;
}

sub _build_base_strategy
{
    return Autocache->singleton->get_default_strategy();
}

sub _build_work_queue
{
    return Autocache->singleton->get_work_queue();
}

around BUILDARGS => sub
{
    my $orig = shift;
    my $class = shift;

    get_logger()->debug( __PACKAGE__ . " - BUILDARGS" );

    if( ref $_[0] )
    {
        my $config = $_[0];
        my %args;
        my $node;

        if( $node = $config->get_node( 'base_strategy' ) )
        {
            get_logger()->debug( "base strategy node found" );
            $args{base_strategy} = Autocache->singleton->get_strategy( $node->value );
        }

        if( $node = $config->get_node( 'refresh_age' ) )
        {
            get_logger()->debug( "refresh age node found" );
            $args{refresh_age} = $node->value;
        }

        return $class->$orig( %args );
    }
    else
    {
        return $class->$orig(@_);
    }
};

no Any::Moose;
__PACKAGE__->meta->make_immutable;

1;