The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Zonemaster::Backend::DB::SQLite;

our $VERSION = '1.1.0';

use Moose;
use 5.14.2;

use DBI qw(:utils);
use JSON::PP;
use Digest::MD5 qw(md5_hex);

use Zonemaster::Backend::Config;

with 'Zonemaster::Backend::DB';

my $connection_string = Zonemaster::Backend::Config->DB_connection_string( 'sqlite' );

has 'dbh' => (
    is      => 'ro',
    isa     => 'DBI::db',
    default => sub { DBI->connect( $connection_string, { RaiseError => 1, AutoCommit => 1 } ) },
);

sub DEMOLISH {
    my ( $self ) = @_;
    $self->dbh->disconnect() if $self->dbh;
}

sub create_db {
    my ( $self ) = @_;

    ####################################################################
    # TEST RESULTS
    ####################################################################
    $self->dbh->do( 'DROP TABLE IF EXISTS test_specs' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    $self->dbh->do( 'DROP TABLE IF EXISTS test_results' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    $self->dbh->do(
        'CREATE TABLE test_results (
					id integer PRIMARY KEY AUTOINCREMENT,
					batch_id integer NULL,
					creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
					test_start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
					test_end_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
					priority integer DEFAULT 10,
					queue integer DEFAULT 0,
					progress integer DEFAULT 0,
					params_deterministic_hash character varying(32),
					params text NOT NULL,
					results text DEFAULT NULL
			)
	'
    ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    ####################################################################
    # BATCH JOBS
    ####################################################################
    $self->dbh->do( 'DROP TABLE IF EXISTS batch_jobs' ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    $self->dbh->do(
        'CREATE TABLE batch_jobs (
					id integer PRIMARY KEY,
					username character varying(50) NOT NULL,
					creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
			)
	'
    ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    ####################################################################
    # USERS
    ####################################################################
    $self->dbh->do( 'DROP TABLE IF EXISTS users' );
    $self->dbh->do(
        'CREATE TABLE users (
					id integer primary key,
					user_info json DEFAULT NULL
			)
	'
    ) or die "SQLite Fatal error: " . $self->dbh->errstr() . "\n";

    return 1;
}

sub user_exists_in_db {
    my ( $self, $user ) = @_;
    my $user_id;

    my $href = $self->dbh->selectall_hashref( "SELECT * FROM users", 'id' );
    foreach my $id ( keys %$href ) {
        my $user_info = decode_josn( $href->{$id}->{user_info} );
        $user_id = $id if ( $user_info->{username} eq $user );
    }

    return $user_id;
}

sub add_api_user_to_db {
    my ( $self, $user_name, $api_key ) = @_;

    my $dbh = $self->dbh;
    my $nb_inserted = $dbh->do( "INSERT INTO users (user_info) VALUES (?)", undef, encode_json( { username => $user_name, api_key => $api_key } ) );

    return $nb_inserted;
}

sub user_authorized {
    my ( $self, $user, $api_key ) = @_;

    my $user_id;

    my $href = $self->dbh->selectall_hashref( "SELECT * FROM users", 'id' );
    foreach my $id ( keys %$href ) {
        my $user_info = decode_josn( $href->{$id}->{user_info} );
        if ( $user_info->{username} eq $user && $user_info->{api_key} eq $api_key ) {
            $user_id = $id;
        }
    }

    return $user_id;
}

sub create_new_batch_job {
    my ( $self, $username ) = @_;

    my ( $batch_id, $creaton_time ) = $self->dbh->selectrow_array( "
			SELECT 
				batch_id, 
				batch_jobs.creation_time AS batch_creation_time 
			FROM 
				test_results 
			JOIN batch_jobs 
				ON batch_id=batch_jobs.id 
				AND username=" . $self->dbh->quote( $username ) . " WHERE 
				test_results.progress<>100
			LIMIT 1
			" );

    die "You can't create a new batch job, job:[$batch_id] started on:[$creaton_time] still running \n" if ( $batch_id );

    my ( $new_batch_id ) = $self->dbh->selectrow_array(
        "INSERT INTO batch_jobs (username) VALUES(" . $self->dbh->quote( $username ) . ") RETURNING id" );

    return $new_batch_id;
}

sub create_new_test {
    my ( $self, $domain, $test_params, $minutes_between_tests_with_same_params, $batch_id ) = @_;
    my $result;

    my $priority = 10;
    $priority = $test_params->{priority} if (defined $test_params->{priority});
    
    my $queue = 0;
    $queue = $test_params->{queue} if (defined $test_params->{queue});

    $test_params->{domain} = $domain;
    my $js = JSON::PP->new;
    $js->canonical( 1 );
    my $encoded_params                 = $js->encode( $test_params );
    my $test_params_deterministic_hash = md5_hex( $encoded_params );

    my $query =
        "INSERT INTO test_results (batch_id, priority, queue, params_deterministic_hash, params) SELECT "
      . $self->dbh->quote( $batch_id ) . ", "
      . $self->dbh->quote( $priority ) . ", "
      . $self->dbh->quote( $queue ) . ", "
      . $self->dbh->quote( $test_params_deterministic_hash ) . ", "
      . $self->dbh->quote( $encoded_params )
      . " WHERE NOT EXISTS (SELECT * FROM test_results WHERE params_deterministic_hash='$test_params_deterministic_hash' AND creation_time > datetime('now', '-$minutes_between_tests_with_same_params minute'))";

    my $nb_inserted = $self->dbh->do( $query );

    ( $result ) = $self->dbh->selectrow_array(
        "SELECT MAX(id) AS id FROM test_results WHERE params_deterministic_hash='$test_params_deterministic_hash'" );

    return $result;
}

sub test_progress {
    my ( $self, $test_id, $progress ) = @_;

    $self->dbh->do( "UPDATE test_results SET progress=$progress WHERE id=$test_id" ) if ( $progress );

    my ( $result ) = $self->dbh->selectrow_array( "SELECT progress FROM test_results WHERE id=$test_id" );

    return $result;
}

sub get_test_params {
    my ( $self, $test_id ) = @_;

    my $result;

    my ( $params_json ) = $self->dbh->selectrow_array( "SELECT params FROM test_results WHERE id=$test_id" );
    eval { $result = decode_json( $params_json ); };
    die "$@ \n" if $@;

    return $result;
}

sub test_results {
    my ( $self, $test_id, $results ) = @_;

    $self->dbh->do( "UPDATE test_results SET progress=100, test_end_time=datetime('now'), results = "
          . $self->dbh->quote( $results )
          . " WHERE id=$test_id " )
      if ( $results );

    my $result;
    eval {
        my ( $hrefs ) = $self->dbh->selectall_hashref( "SELECT * FROM test_results WHERE id=$test_id", 'id' );
        $result            = $hrefs->{$test_id};
        $result->{params}  = decode_json( $result->{params} );
        $result->{results} = decode_json( $result->{results} );
    };
    die "$@ \n" if $@;

    return $result;
}

sub get_test_request {
    my ( $self ) = @_;

    my ( $id ) = $self->dbh->selectrow_array(
        q[ SELECT id FROM test_results WHERE progress=0 ORDER BY priority ASC, id ASC LIMIT 1 ] );
    $self->dbh->do( q[UPDATE test_results SET progress=1 WHERE id=?], undef, $id );

    return $id;
}

sub get_test_history {
    my ( $self, $p ) = @_;

    my @results;
    my $quoted_domain = $self->dbh->quote( $p->{frontend_params}->{domain} );
    $quoted_domain =~ s/'/"/g;
    my $query =
"SELECT id, creation_time, params FROM test_results WHERE params like '\%\"domain\":$quoted_domain\%' ORDER BY id DESC LIMIT $p->{limit} OFFSET $p->{offset} ";
    my $sth1 = $self->dbh->prepare( $query );
    $sth1->execute;
    while ( my $h = $sth1->fetchrow_hashref ) {
        push( @results,
            { id => $h->{id}, creation_time => $h->{creation_time}, advanced_options => $h->{advanced_options} } );
    }
    $sth1->finish;

    return \@results;
}

sub add_batch_job {
    my ( $self, $params ) = @_;
}

no Moose;
__PACKAGE__->meta()->make_immutable();

1;