The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Smolder::Control::Admin::Developers;
use strict;
use warnings;
use base 'Smolder::Control';
use Smolder::DB::Project;
use Smolder::DB::Developer;
use Smolder::Email;
use Smolder::Constraints qw(email unsigned_int length_max length_between bool unique_field_value);
use Email::Valid;

=head1 NAME

Smolder::Control::Admin::Developers

=head1 DESCRIPTION

Controller module for Admin activities concerning Developers

=cut

sub setup {
    my $self = shift;
    $self->start_mode('list');
    $self->run_modes(
        [
            qw(
              add
              process_add
              edit
              process_edit
              list
              delete
              reset_pw
              details
              )
        ]
    );
}

sub require_group { 'admin' }

=head1 RUN MODES

=head2 reset_pw

Allow an admin to reset the password of a developer to a new random string
and then email the new password to the developer. Uses the F<Email/reset_pw.tmpl>
template for the email and the F<Admin/Developers/resetpw_success.tmpl> for
displaying the result.

=cut

sub reset_pw {
    my $self = shift;
    my $dev  = Smolder::DB::Developer->retrieve($self->param('id'));
    return $self->error_message("Developer no longer exists!")
      unless $dev;

    my $email  = $dev->email;
    my $user   = $dev->username;
    my $new_pw = $dev->reset_password();

    # send the email
    my $error = Smolder::Email->send_mime_mail(
        name      => 'reset_pw',
        to        => $email,
        subject   => 'Reset of password by Admin',
        tt_params => {
            developer => $dev,
            new_pw    => $new_pw,
        },
    );
    if ($error) {
        $self->add_message(
            msg  => "Could not send email to '$user'. Please check error logs!",
            type => 'warning',
        );
        $self->log->warning("Could not send 'reset_pw' email to $email - $error");
    } else {
        $self->add_message(msg =>
              "Password for '$user' has been reset and an email sent with their new password.");
    }
    return ' ';
}

=head2 edit 

Show the edit form to allow an admin to edit the data about a developer.
Uses the F<Admin/Developers/edit.tmpl> template.

=cut

sub edit {
    my ($self, $err_msgs) = @_;
    my $developer = Smolder::DB::Developer->retrieve($self->param('id'));
    my $output;

    # if we have any error messages, then just re-fill the form
    # and show them
    if ($err_msgs) {
        $err_msgs->{developer} = $developer;
        $output = HTML::FillInForm->new->fill(
            scalarref => $self->tt_process($err_msgs),
            qobject   => $self->query,
        );

        # else get the developer in question
    } else {
        my %developer_data = $developer->vars();
        $output = HTML::FillInForm->new->fill(
            scalarref => $self->tt_process({developer => $developer}),
            fdat      => \%developer_data,
        );
    }
    return $output;
}

=head2 process_edit

Processes the incoming data from the C<edit> mode and update the
developers info in the database if validation passes. Uses the
F<Admin/Developers/edit_success.tmpl> template.

=cut

sub process_edit {
    my $self = shift;
    my $id   = $self->param('id');
    my $form = {
        required           => [qw(username fname lname email admin)],
        constraint_methods => {
            username => [length_max(255), unique_field_value('developer', 'username', $id),],
            fname    => length_max(255),
            lname    => length_max(255),
            email    => email(),
            admin    => bool(),
        },
    };

    my $results = $self->check_rm('edit', $form)
      || return $self->check_rm_error_page;
    my $valid = $results->valid();

    my $developer = Smolder::DB::Developer->retrieve($id);
    return $self->error_message("Developer no longer exists!")
      unless $developer;
    $developer->set(%$valid);

    # we need to eval{} since we don't want there to be duplicate usernames (id)
    eval { $developer->update };

    # if there was a problem.
    if ($@) {

        # if it was a duplicate developer, then we can handle that
        if (Smolder::Conf->unique_failure_msg($@)) {
            return $self->edit({err_unique_username => 1});

            # else it's something else, so just throw it again
        } else {
            die $@;
        }
    }

    # now show the successful message
    $self->add_message(msg => "User '" . $developer->username . "' has been successfully updated.");
    return $self->add_json_header(list_changed => 1);
}

=head2 list

Show a list of all developers. Uses the F<Admin/Developers/list_table.tmpl>
template.

=cut

sub list {
    my $self       = shift;
    my $cgi        = $self->query();
    my @developers = Smolder::DB::Developer->search(guest => 0);

    my %tt_params;
    $tt_params{developers} = \@developers if (@developers);

    return $self->tt_process(\%tt_params);
}

=head2 add

Show the add form for adding a new developer. Uses the 
F<Admin/Developers/add.tmpl> template.

=cut

sub add {
    my ($self, $tt_params) = @_;
    $tt_params ||= {};
    return $self->tt_process($tt_params);
}

=head2 process_add

Process the incoming data from the C<add> mode and add it to the
database if validation passes. Uses the F<Admin/Developers/add_success.tmpl>.

=cut

sub process_add {
    my $self = shift;
    my $form = {
        required           => [qw(username email password admin)],
        optional           => [qw(fname lname)],
        constraint_methods => {
            username => [length_max(255), unique_field_value('developer', 'username'),],
            fname    => length_max(255),
            lname    => length_max(255),
            email    => email(),
            password => length_between(4, 255),
            admin    => bool(),
        },
    };

    my $results = $self->check_rm('add', $form)
      || return $self->check_rm_error_page;
    my $valid = $results->valid();

    # create a new preference for this developer;
    my $pref = Smolder::DB::Preference->create();
    $valid->{preference} = $pref;
    my $developer;

    # we need to eval{} since we don't want there to be duplicate usernames
    eval { $developer = Smolder::DB::Developer->create($valid) };

    # if there was a problem.
    if ($@) {

        # if it was a duplicate developer, then we can handle that
        if (Smolder::Conf->unique_failure_msg($@)) {
            return $self->add({err_unique_username => 1});

            # else it's something else, so just throw it again
        } else {
            die $@;
        }
    }

    # now show the successful message
    $self->add_message(msg => "New user '" . $developer->username . "' successfully created.");
    return $self->add_json_header(list_changed => 1);
}

=head2 delete 

Delete a Developer and all data associated with him. If
successful returns the C<list> mode.

=cut

sub delete {
    my $self      = shift;
    my $id        = $self->param('id');
    my $developer = Smolder::DB::Developer->retrieve($id);

    if ($developer) {

        # remove all reports from this developer
        my @smokes = $developer->smoke_reports();
        foreach my $smoke (@smokes) {
            $smoke->delete_files();
        }

        my $username = $developer->username;
        $developer->delete();
        $self->add_message(msg => "User '$username' has been successfully deleted.");
    }

    return $self->list();
}

=head2 details

Show the details of a developer. Uses the F<Admin/Developers/details.tmpl>
template.

=cut

sub details {
    my ($self, $developer, $action) = @_;
    my $new;

    # if we weren't given a developer, then get it from the query string
    if (!$developer) {
        my $id = $self->param('id');
        $new       = 0;
        $developer = Smolder::DB::Developer->retrieve($id);
        return $self->error_message("Can't find Developer with id '$id'!") unless $developer;
    } else {
        $new = 1;
    }

    my %tt_params = (developer => $developer);
    $tt_params{$action} = 1 if ($action);

    return $self->tt_process(\%tt_params);
}

1;