package Pear::LocalLoop::Controller::Api::Upload;
use Mojo::Base 'Mojolicious::Controller';
use Data::Dumper;

=head2 post_upload

Takes a file upload, with a file key of 'file', and a json string under the
'json' key.

The json string should be an object, with the following keys:

=over

=item * transaction_value

The value of the transaction

=item * transaction_type

Is a value of 1, 2, or 3 - depending on the type of transaction.

=item * purchase_time

Is a DateTime value submitted for when the receipt is from.

=item * organisation_id

An ID of a valid organisation. used when transaction_type is 1 or 2.

=item * organisation_name

The name of an organisation. Used when transaction_type is 3.

=item * street_name

The street of an organisation, optional key. Used when transaction_type is 3.

=item * town

The village/town/city of an organisation. Used when transaction_type is 3.

=item * postcode

The postcode of an organisation, optional key. Used when transaction_Type is 3.

=back

=cut

has error_messages => sub {
  return {
    transaction_type => {
      required => { message => 'transaction type is missing.', status => 400 },
      in => { message => 'transaction type is not a valid value.', status => 400 },
    },
    transaction_value => {
      required => { message => 'transaction amount is missing', status => 400 },
      number => { message => 'transaction amount does not look like a number', status => 400 },
      gt_num => { message => 'transaction amount cannot be equal to or less than zero', status => 400 },
    },
    purchase_time => {
      required => { message => 'purchase time is missing', status => 400 },
      is_full_iso_datetime => { message => 'purchase time is in incorrect format', status => 400 },
    },
    file => {
      required => { message => 'No file uploaded', status => 400 },
      upload => { message => 'file key does not contain a file', status => 400 },
      filetype => { message => 'File must be of type image/jpeg', status => 400 },
    },
    organisation_id => {
      required => { message => 'existing organisation ID is missing', status => 400 },
      number => { message => 'organisation ID is not a number', status => 400 },
      in_resultset => { message => 'organisation ID does not exist in the database', status => 400 },
    },
    organisation_name => {
      required => { message => 'organisation name is missing', status => 400 },
    },
    category => {
      in_resultset => { message => 'Category is invalid', status => 400 },
    },
    town => {
      required => { message => 'town/city is missing', status => 400 },
    },
    search_name => {
      required => { message => 'search name is missing', status => 400 },
    },
    postcode => {
      postcode => { message => 'postcode must be valid', status => 400 },
    },
  };
};

sub post_upload {
  my $c = shift;

  my $user = $c->stash->{api_user};

  my $validation = $c->validation;

  # Test for file before loading the JSON in to the validator
  $validation->optional('file')->upload->filetype('image/jpeg');

  $validation->input( $c->stash->{api_json} );

  $validation->required('transaction_value')->number->gt_num(0);
  $validation->required('transaction_type')->in( 1, 2, 3 );

  #Check a proper purchase time was submitted
  $validation->optional('purchase_time')->is_full_iso_datetime;
  $validation->optional('category')->in_resultset( 'id', $c->schema->resultset('Category'));
  $validation->optional('essential');
  $validation->optional('recurring');

  # First pass of required items
  return $c->api_validation_error if $validation->has_error;

  my $type = $validation->param('transaction_type');

  my $organisation;

  if ( $type == 1 ) {
    # Validated Organisation
    my $valid_org_rs = $c->schema->resultset('Organisation')->search({
      pending => 0,
      entity_id => { "!=" => $user->entity_id },
    });
    $validation->required('organisation_id')->number->in_resultset( 'id', $valid_org_rs );

    return $c->api_validation_error if $validation->has_error;

    $organisation = $valid_org_rs->find( $validation->param('organisation_id') );

  } elsif ( $type == 2 ) {
    # Unvalidated Organisation
    my $valid_org_rs = $c->schema->resultset('Organisation')->search({
      submitted_by_id => $user->id,
      pending => 1,
      entity_id => { "!=" => $user->entity_id },
    });
    $validation->required('organisation_id')->number->in_resultset( 'id', $valid_org_rs );

    return $c->api_validation_error if $validation->has_error;

    $organisation = $valid_org_rs->find( $validation->param('organisation_id') );

  } elsif ( $type == 3 ) {
    # Unknown Organisation
    $validation->required('organisation_name');
    $validation->optional('street_name');
    $validation->optional('town');
    $validation->optional('postcode')->postcode;

    return $c->api_validation_error if $validation->has_error;

    my $location = $c->get_location_from_postcode(
      $validation->param('postcode'),
      'organisation',
    );

    my $entity = $c->schema->resultset('Entity')->create_org({
      submitted_by_id => $user->id,
      name            => $validation->param('organisation_name'),
      street_name     => $validation->param('street_name'),
      town            => $validation->param('town'),
      postcode        => $validation->param('postcode'),
      ( defined $location ? ( %$location ) : ( latitude => undef, longitude => undef ) ),
      pending         => 1,
    });
    $organisation = $entity->organisation;
  }

  unless ( defined $organisation ) {
    return $c->render(
      json => {
        success => Mojo::JSON->false,
        message => 'Error Finding Organisation',
        error   => 'organisation_error',
      },
      status => 400,
    );
  }

  my $transaction_value = $validation->param('transaction_value');
  my $upload = $validation->param('file');
  my $purchase_time = $c->parse_iso_datetime($validation->param('purchase_time') || '');
  $purchase_time ||= DateTime->now();
  my $file = defined $upload ? $c->store_file_from_upload( $upload ) : undef;
  my $category = $validation->param('category');
  my $essential = $validation->param('essential');
  my $recurring_period = $validation->param('recurring');
  my $distance = $c->get_distance_from_coords( $user->entity->type_object, $organisation );

  my $new_transaction = $organisation->entity->create_related(
    'sales',
    {
      buyer => $user->entity,
      value => $transaction_value * 100000,
      ( defined $file ? ( proof_image => $file ) : () ),
      purchase_time => $c->format_db_datetime($purchase_time),
      essential => ( defined $essential ? $essential : 0 ),
      distance => $distance,
    }
  );

  unless ( defined $new_transaction ) {
    return $c->render(
      json => {
        success => Mojo::JSON->false,
        message => 'Error Adding Transaction',
        error   => 'transaction_error',
      },
      status => 400,
    );
  }

  if ( defined $category ) {
    $c->schema->resultset('TransactionCategory')->create({
      category_id => $category,
      transaction_id => $new_transaction->id,
    });
  }

  if ( defined $recurring_period ) {
    $c->schema->resultset('TransactionRecurring')->create({
      buyer => $user->entity,
      seller => $organisation->entity,
      value => $transaction_value * 100000,
      start_time => $c->format_db_datetime($purchase_time),
      essential => ( defined $essential ? $essential : 0 ),
      distance => $distance,
      category_id => ( defined $category ? $category : undef ),
      recurring_period => $recurring_period,
    });
  }

  return $c->render( json => {
    success => Mojo::JSON->true,
    message => 'Upload Successful',
  });
}

sub post_category {
  my $c = shift;
  my $self = $c;

  my $category_list = $c->schema->resultset('Category')->as_hash;
  delete $category_list->{0};

  return $self->render( json => {
    success => Mojo::JSON->true,
    categories => $category_list,
  });
}

# TODO Limit search results, possibly paginate them?
# TODO Search by location as well
sub post_search {
  my $c = shift;
  my $self = $c;

  my $user = $c->stash->{api_user};

  my $validation = $c->validation;

  $validation->input( $c->stash->{api_json} );

  $validation->required('search_name');
  $validation->optional('page')->number;

  return $c->api_validation_error if $validation->has_error;

  my $search_name = $validation->param('search_name');

  my $search_stmt = [ 'LOWER("name") LIKE ?', '%' . lc $search_name . '%' ];

  my $org_rs = $c->schema->resultset('Organisation');
  my $valid_orgs_rs = $org_rs->search({
    pending => 0,
    entity_id => { "!=" => $user->entity_id },
  },
  {
    page => $validation->param('page') || 1,
    rows => 10,
    order_by => { -desc => 'name' },
  })->search(
    \$search_stmt,
  );

  my $pending_orgs_rs = $org_rs->search({
      pending => 1,
      submitted_by_id => $c->stash->{api_user}->id,
      entity_id => { "!=" => $user->entity_id },
    })->search(
    \$search_stmt,
  );

  my @valid_orgs = (
    map {{
        id => $_->id,
        name => $_->name,
        street_name => $_->street_name,
        town => $_->town,
        postcode => $_->postcode,
    }} $valid_orgs_rs->all
  );

  my @pending_orgs = (
    map {{
        id => $_->id,
        name => $_->name,
        street_name => $_->street_name,
        town => $_->town,
        postcode => $_->postcode,
    }} $pending_orgs_rs->all
  );

  return $self->render( json => {
    success => Mojo::JSON->true,
    validated => \@valid_orgs,
    unvalidated => \@pending_orgs,
  });
}

1;
