package Database::Temp::DB;
## no critic (ControlStructures::ProhibitPostfixControls)

use strict;
use warnings;

# ABSTRACT: This class represents the created database to user

our $VERSION = '0.001'; # VERSION: generated by DZP::OurPkgVersion

use DBI;
use Carp qw( shortmess );

use Moo;
use Types::Standard qw( Str Int Bool HashRef CodeRef Maybe );
use Log::Any;

my $ENV_VAR_FOR_KEEPING_DB = 'DATABASE_TEMP_KEEP';

has driver => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has name => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has cleanup => (
    is       => 'rw',
    isa      => Bool,
    required => 1,
);

has init => (
    is       => 'ro',
    isa      => CodeRef,
    required => 1,
);

has deinit => (
    is       => 'rw',
    isa      => CodeRef,
    required => 1,
);

# Connection info
has dsn => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);
has username => (
    is       => 'ro',
    isa      => Maybe [Str],
    required => 1,
);
has password => (
    is       => 'ro',
    isa      => Maybe [Str],
    required => 1,
);
has attr => (
    is       => 'ro',
    isa      => HashRef,
    required => 1,
);

has info => (
    is       => 'ro',
    isa      => HashRef,
    required => 1,
);

sub BUILD {
    my ($self) = @_;
    $self->_execute( $self->_start );
    $self->_execute( $self->init );
    return;
}

sub DEMOLISH {
    my ( $self, $in_global_destruction ) = @_;
    my $_log = Log::Any->get_logger( category => 'Database::Temp' );

    $self->_execute( $self->deinit );
    if ( defined $ENV{$ENV_VAR_FOR_KEEPING_DB} ) {
        $_log->infof( 'Env var %s set. Keeping temp db', $ENV_VAR_FOR_KEEPING_DB ) if ( !$in_global_destruction );
    }
    elsif ( $self->cleanup ) {
        $_log->debugf('Cleanup set. Deleting temp db') if ( !$in_global_destruction );
        $self->_execute( $self->_cleanup );
    }
    else {
        $_log->debugf('Cleanup not set. Keeping temp db') if ( !$in_global_destruction );
    }
    return;
}

sub _execute {
    my ( $self, $sub ) = @_;
    my $dbh = DBI->connect( $self->connection_info );
    $sub->( $dbh, $self->name, $self->info, $self->driver );
    $dbh->disconnect();
    return $self;
}

has _start => (
    is       => 'ro',
    isa      => CodeRef,
    required => 1,
);
has _cleanup => (
    is       => 'ro',
    isa      => CodeRef,
    required => 1,
);

sub connection_info {
    my ($self) = @_;
    return ( $self->dsn, $self->username, $self->password, $self->attr );
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Database::Temp::DB - This class represents the created database to user

=head1 VERSION

version 0.001

=head1 SYNOPSIS

    my $db = Database::Temp->new(
        driver => 'SQLite',
    );
    my $dbh = $db->dbh;
    my $rows = $dbh->selectall_arrayref(
        "SELECT 1, 1+2",
    );

=head1 DESCRIPTION

The end user should never create this class directly.
Database::Temp uses this class to create an object
which represents the database just created
by Database::Temp.

This class, when instantiated into an object,
contains everything user needs
to access the created temporary database,
regardless of it database engine.

=for Pod::Coverage ^(BUILD|DEMOLISH)$

=head1 METHODS

=head2 driver

The short name of the L<Database::Temp> driver,
for example, in the case of L<Database::Temp::Driver::SQLite>
the value would be B<SQLite>. Read-only.

=head2 name

Name of the database. Read-only.

=head2 cleanup

A boolean variable. Do we do cleanup?
Depends on the DB driver what cleanup would be,
e.g. in the case of SQLite, cleanup means erasing the database file.
In the case of Postgres, cleanup would be dropping the database.

User can change this value.
Value 1 means cleanup will be done, value 0 means
no cleanup.

Default value is true: database will be erased when this
object drops out of the scope.

=head2 init

Reference to the subroutine which is executed
immediately after database creation. Therefore,
this action has already been executed
by the time user gets hold of this object.
Read-only.

=head2 deinit

Reference to the subroutine which is executed
immediately before database is demolished.
User can change this value.
Default value is pointer to an empty subroutine.

=head2 dsn

=head2 username

=head2 password

=head2 attr

These four fields are required to make a connection
to a database. Read-only.

B<dsn>, B<username> and B<password> are strings.
B<attr> is a hashref.

Most likely user would not use these directly,
but instead use the method C<connection_info>.
See below.

=head2 info

This field can contain other related information which
is not required for establishing connection and creating
a database handle, but which can be useful to the user or
required by the driver when doing cleanup. Read-only.

=head2 connection_info

This method returns a list of connection information details
which can be fed directly into C<DBI->connect()> method.

    my $dbh = DBI->connect( $temp_db->connection_info );

=head1 AUTHOR

Mikko Koivunalho <mikkoi@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by Mikko Johannes Koivunalho.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
