package API::ThirdPartyChannel;
use strict;
use warnings;

use API::Controller;
use Data::Dumper;
use Carp;

#local $Carp::MaxArgLen = $Carp::MaxArgLen;
#local $Carp::MaxArgNums = $Carp::MaxArgNums;
#local $Carp::Verbose = $Carp::Verbose;
#local $Carp::RefArgFormatter = $Carp::RefArgFormatter;
#BEGIN {
#	$Carp::MaxArgLen = 0;
#	$Carp::MaxArgNums = 0;
#	$Carp::Verbose = 1;
#	$Carp::RefArgFormatter = sub { 
#		local $Data::Dumper::Terse = 1; 
#		local $Data::Dumper::Indent = 0; 
#		Data::Dumper::Dumper($_[0]); 
#	};
#}
sub CARP_TRACE { 
	sprintf "<%s:0x%xd>", ref $_[0], $_[0];
}


use Moose;
use ECS::Employee;
use Try::Tiny;
use Type::Utils;
use Types::Standard qw/Optional Str Int Num InstanceOf Map Dict ArrayRef Tuple slurpy Any/;
use SQL::Abstract;
use Time::Piece;
use Time::Piece::MySQL;

extends 'API::Controller';

my $MySQLTime = declare( MySQLTime => 
	as InstanceOf[Time::Piece::]
)->plus_coercions(
	Str,	=> sub {
		Time::Piece->from_mysql_timestamp( $_ )
	});

my $EmployeeID = declare EmployeeID => 
	as Int;

my $ECSEmployee = declare ECSEmployee =>
	as InstanceOf[ECS::Employee::];

my $MarginChange = declare MarginChange => 
		as Dict[
			tsModified => $MySQLTime,
			margin		=> Num,
			employee	=> $ECSEmployee,
		];

my $SQLAbstract = declare SQLAbstract =>
	as Tuple[
		Str,
		ArrayRef[Str],
	];
		

my $MarginHistory = declare MarginHistory =>
	as ArrayRef[ $MarginChange ];

my $Input = declare Input =>
	as Dict[
		margin	=> Optional[Num],
		employeeID	=> Optional[$EmployeeID],
		slurpy Any,
	];

has sisenseDB => (
	is		=> 'ro',
	isa		=> InstanceOf->of("DBI::db"),
	lazy	=> 1,
	builder	=> '_build_sisenseDB',
);

has input => (
	is			=> 'ro',
	isa			=> $Input,
	default		=> sub { {} },
);
	
has employee_id => (
	is			=> 'ro',
	isa			=> $EmployeeID,
	lazy		=> 1,
	builder		=> '_build_employee_id',
);

has margin => (
	is		=> 'rw',
	isa		=> Num,
	lazy	=> 1,
	builder	=> '_build_margin',
	trigger	=> __PACKAGE__->can('_change_margin'),
);

has margin_history => (
	is		=> 'rw',
	isa		=> $MarginHistory,
	lazy	=> 1,
	builder	=> '_build_margin_history',
	clearer	=> '_clear_margin_history',
	coerce	=> 0,
);

#has _employees => (
#	is		=> 'rw',
#	isa		=> Map[ $EmployeeID, $ECSEmployee ],
#	default	=> sub { {} },
#);

has _sqla => (
	is		=> 'rw',
	isa		=> $SQLAbstract,
	lazy	=> 1,
	builder	=> '_build_sqla',
);

has _update_sql => (
	is		=> 'rw',
	isa		=> $SQLAbstract,
	lazy	=> 1,
	builder	=> '_build_sql_update',
);

no Moose;
__PACKAGE__->meta->make_immutable;

sub _build_employee_id {
	(shift)->input->{employeeID};
}

sub _build_margin {
	my $self = shift;
	my $history = $self->margin_history;
	if ($history && @$history) {
		return $history->[0]{margin};
	} else {
		return 0;
	}
}

sub _build_margin_history {
	my $self = shift;
	my ($sql, $binds) = @{ 
		$self->_sqla,
	};
	my $rs = $self->sisenseDB->selectall_arrayref( $sql, { Slice => {} }, @$binds );
	if ($rs && @$rs) {
		my @employee_ids = map $_->{employeeID}, map @$_, $rs;
		my $employees = $self->_load_employees( \@employee_ids );
		my $ToECSEmployee = declare_coercion ToECSEmployee =>
			from Int,
			via {
				$employees->{$_} || ECS::Employee->load({
					DBH			=> $self->dbhr,
					employeeID	=> $_,
				});
			};
		my $ToMarginChange = declare_coercion ToMarginChange => 
			from (Dict[ 
				margin => Num, 
				employeeID	=> Int,
				tsModified	=> Str
			]),
			via {
				+{
					margin	=> $_->{margin},
					employee	=> $ToECSEmployee->( $_->{employeeID} ),
					tsModified => $MySQLTime->coerce( $_->{tsModified} ),
				}
			};
		@$rs = map $ToMarginChange->( $_ ), @$rs;
	}
	return $rs || [];
}

sub _build_sisenseDB {
	my $self = shift;
	return ECS::SQL::custom_connect( $ECS::Config::c{sisense} );
}

sub _build_sqla {
	my $sqla = new SQL::Abstract;
	my ($sql, @binds) = $sqla->select(
		qw/dev_estore.tpcMarginHistory/,
		[qw/
			employeeID
			margin
			tsModified
		/],
		{},
		{ -desc => 'tsModified' },
	);
	return [$sql, \@binds];
}

sub _build_sql_update {
	my $sqla = new SQL::Abstract;
	my ($sql, @binds) = $sqla->insert(
		qw/dev_estore.tpcMarginHistory/,
		[0, 0, \"NOW()"],
		{},
		{ -desc => 'tsModified' },
	);
	return [$sql, \@binds];
}

sub _load_employees {
	my $self = shift;
	my @employee_ids = @_;
	
	loadMulti ECS::Employee {
		DBH	=> $self->dbhr,
		employeeID	=> \@employee_ids,
	};
}

sub _change_margin {
	my $self = shift;
	my ($new_margin, $old_margin) = @_;
	my ($sql, $binds) = @{ $self->_update_sql };
	@$binds[0,1] = ($self->employee_id, $new_margin);
	$self->sisenseDB->do( $sql, {}, @$binds );
	$self->_clear_margin_history;
}

sub index {
	my $self = (shift)->new({ input => shift || {}});
	$self->data->{templates} = [qw/main.html history.html/];
	my $output = $self->TO_JSON;
	$self->data->{$_} = $output->{$_} for qw/history margin/;
	return $self->result;
}

sub changeMargin {
	my $self = (shift)->new({ input => shift || {}});
	if ($self->input->{margin}) {
		$self->margin( $self->input->{margin} );
	}
	$self->data->{redirect} = '.';
	return $self->result;
}

sub TO_JSON {
	my $self = shift;
	
	my @history = map +{
		employeeName	=> $_->{employee}->fullName,
		margin			=> "".sprintf('%0.2f' => $_->{margin}),
		tsModified		=> $_->{tsModified}->mysql_datetime,
	}, map @$_, $self->margin_history;
	return +{
		history	=> \@history,
		margin	=> $self->margin,
	};
}

1;

