package # U - Utilities
	U;  # Hide from PAUSE

use strict;
use warnings;

use C;
use L;



# Q: Decide, would it be better to parse log lines for some text, or use caller info
# to email warnings?
sub ewarn {
	L::warn @_[0,1];

	my $to =  C::config->{ Debug }{ to };
	email $to, @_;
}



use Email::Stuffer;
sub email {
	# Send emails in production mode or when 'stop_mail' is configured
	!C::debug()  ||  C::config->{ email }{ stop_mail }   or do{
		L::warn "Emails are disabled in the 'development' mode\n";
		L::debug sprintf "To: %s; Subj: %s\nBody: %s\n", @_[0..2];

		return 1;
	};

	my( $address, $subject, $body, $attachments ) =  @_;


	## Prepare transport
	my $cnf =  C::config->{ email };
	my $transport =  "Email::Sender::Transport" .$cnf->{ transport };
	eval "require $transport"   or die $@;
	$transport =  $transport->new(
		host          =>  $cnf->{ server   },
		port          =>  $cnf->{ port     },
		ssl           =>  $cnf->{ ssl      },
		sasl_username =>  $cnf->{ username },
		sasl_password =>  $cnf->{ password },
	);


	## Read parameters
	my $opts;
	if( ref $address eq 'HASH' ) {
		$opts =  $address;
		$opts->{ from } //=  $cnf->{ from };
		$body =  delete $opts->{ body }  // delete $opts->{ text_body };
	}
	else {
		$opts    =  {
			from      =>  $cnf->{ from },
			to        =>  $address,
			subject   =>  $subject,
		};
	}

	# Put scalar into array if it is not yet
	$$_ =  ref $$_ eq 'ARRAY'? $$_ : [ $$_ ]
		for map{ \$_ } grep { !!$_ } @$opts{qw/ to cc bcc reply_to /};

	# Values should exist
	$opts->{ cc       }   or delete $opts->{ cc       };
	$opts->{ bcc      }   or delete $opts->{ bcc      };
	$opts->{ reply_to }   or delete $opts->{ reply_to };


	if( $cnf->{ stop_mail } ) {
		my $addr =  sub {
			my $type =  shift;
			my $a    =  delete $opts->{ $type }   or return '';
			uc( " $type: " ) .join', ', @$a;
		};

		$opts->{ subject } =  ''
			.$addr->( 'to' ) .$addr->( 'cc' ) .$addr->( 'bcc' )
			.'  SUBJ: ' .$opts->{ subject };

		$opts->{ to } =  [ $cnf->{ stop_mail } ];
	}

	$opts->{ subject } =  ($cnf->{ stop_subj } //'') .$opts->{ subject };


	## Sending
	my $parts =  $attachments  // delete $opts->{ attachments }  // [];
	my $email =  Email::Stuffer->new({ transport => $transport, %$opts });
	$email->text_body( ref $body eq 'ARRAY'? @$body : $body );
	$email->attach( ref $_ eq 'ARRAY'? @$_ : $_ )   for @$parts;

	return $email->send_or_die;
}


# -> 0.00
# 5.6777    -> 5.68
# 5.6777, 1 -> 5.7
# 5.6777, 3 -> 5.778
# 3.2419, 2 -> 3.24
use Math::BigFloat;
sub round {
	my( $n, $p ) =  @_;
	$n //=  0;
	$p //=  2;
	my $bi =  'Math::BigFloat'->new("$n");
	$bi->bfround( -$p, 'common' );
	sprintf "%0.${p}f", $bi;
}


1;

=encoding utf8

=head1 NAME

C<U> - Utilities


=head1 SYNOPSIS

  use U;

  U::ewarn 'Subject', 'Body text';

  U::email 'user@example.com', 'Hello', "Body\n";

  my $rounded =  U::round 5.6777, 2;  # "5.68"

=head1 DESCRIPTION

C<U> contains utility functions used across the project. These functions should not
depend on other dynamic things.


=head1 FUNCTIONS

=head2 ewarn

  U::ewarn $subject, $body, $attachments;

Log the first two arguments using L<L/warn> and send an email to the debug
address configured in C<C::config-E<gt>{Debug}{to}>.

Configuration example:

	Debug => {
		to      =>  'recepient@example.com',
		subject =>  'Invoice: Exception',
	},

See L<C::load> for details.


=head2 email

  U::email $to, $subject, $body;
  U::email $to, $subject, $body, \@attachments;
  U::email \%opts;

Send an email using L<Email::Stuffer>.

Configuration is read from C<C::config-E<gt>{email}> and is expected to
contain transport settings (server, port, ssl, username, password, from,
and so on).

Attachments can be provided as an array reference. Each element is passed to
C<Email::Stuffer-E<gt>attach> (scalar or arrayref of arguments).

Configuration example:

	email => {
		stop_subj =>  '[Debug]',
		# stop_mail =>  'recepient@example.com',

		from      =>  'source@example.com',
		transport =>  '::SMTP::Persistent',

		server    =>  'email-smtp.eu-west-1.amazonaws.com',
		port      =>  '587',
		ssl       =>  'starttls',
		username  =>  'AKIA...',
		password  =>  'XYZ',
  },

See L<C::load> for details.


=head2 round

  my $str =  U::round $number;
  my $str =  U::round $number, $places;

Round a number to C<$places> decimals (default: 2) using
L<Math::BigFloat/bfround>. Returns a formatted string.


=head1 SEE ALSO

L<A>, L<C>, L<I>, L<L>, L<M>, L<S>, L<T>, L<U>, L<Email::Stuffer>.

=cut
