package FixMyStreet::App::Controller::Auth::Profile;
use Moose;
use namespace::autoclean;

BEGIN { extends 'Catalyst::Controller'; }

use mySociety::AuthToken;

=head1 NAME

FixMyStreet::App::Controller::Auth::Profile - Catalyst Controller

=head1 DESCRIPTION

Controller for all the authentication profile related pages - adding/ changing/
verifying email, phone, password.

=head1 METHODS

=cut

sub auto : Private {
    my ( $self, $c ) = @_;

    $c->detach( '/auth/redirect' ) unless $c->user;

    return 1;
}

=head2 change_password

Let the user change their password.

=cut

sub change_password : Path('/auth/change_password') {
    my ( $self, $c ) = @_;

    $c->stash->{template} = 'auth/change_password.html';

    $c->forward('/auth/get_csrf_token');

    # If not a post then no submission
    return unless $c->req->method eq 'POST';

    $c->forward('/auth/check_csrf_token');

    # get the passwords
    my $new = $c->get_param('new_password') // '';
    my $confirm = $c->get_param('confirm') // '';

    my $password_error;

    # Check existing password, if available
    if ($c->user->password) {
        my $current = $c->get_param('current_password') // '';
        $c->stash->{current_password} = $current;
        $password_error = 'incorrect' unless $c->user->check_password($current);
    }

    # check for errors
    $password_error ||=
       !$new && !$confirm ? 'missing'
      : $new ne $confirm ? 'mismatch'
      : !$c->forward('/auth/test_password', [ $new ]) ? 'failed'
      :                    '';

    if ($password_error) {
        $c->stash->{password_error} = $password_error;
        $c->stash->{new_password}   = $new;
        $c->stash->{confirm}        = $confirm;
        return;
    }

    if ($c->user->password) {
        # we should have a usable password - save it to the user
        $c->user->obj->password($new);
        $c->user->obj->update;
        $c->stash->{password_changed} = 1;
    } else {
        # Set up arguments for code sign in
        $c->set_param('username', $c->user->username);
        $c->set_param('password_register', $new);
        $c->set_param('r', 'auth/change_password/success');
        $c->detach('/auth/code_sign_in');
    }
}

=head2 change_email

Let the user change their email.

=cut

sub change_email : Path('/auth/change_email') {
    my ( $self, $c ) = @_;

    $c->stash->{template} = 'auth/change_email.html';

    $c->forward('/auth/get_csrf_token');

    # If not a post then no submission
    return unless $c->req->method eq 'POST';

    $c->forward('/auth/check_csrf_token');
    $c->stash->{current_user} = $c->user;
    $c->stash->{email_template} = 'change_email.txt';
    $c->forward('/auth/email_sign_in', [ $c->get_param('email') ]);
}

sub change_phone : Path('/auth/change_phone') {
    my ( $self, $c ) = @_;

    $c->stash->{template} = 'auth/change_phone.html';

    $c->forward('/auth/get_csrf_token');

    # If not a post then no submission
    return unless $c->req->method eq 'POST';

    $c->forward('/auth/check_csrf_token');
    $c->stash->{current_user} = $c->user;

    my $phone = $c->stash->{username} = $c->get_param('username') || '';
    my $parsed = FixMyStreet::SMS->parse_username($phone);

    # Allow removal of phone number, if we have verified email
    if (!$phone && !$c->stash->{verifying} && $c->user->email_verified) {
        $c->user->update({ phone => undef, phone_verified => 0 });
        $c->flash->{flash_message} = _('You have successfully removed your phone number.');
        $c->res->redirect('/my');
        $c->detach;
    }

    $c->stash->{username_error} = 'missing_phone', return unless $phone;
    $c->stash->{username_error} = 'other_phone', return unless $parsed->{phone};

    # If we've not used a mobile and we're not specifically verifying,
    # and phone isn't our only verified way of logging in,
    # then allow change of number (for e.g. landline).
    if (!$c->cobrand->sms_authentication || (!$parsed->{may_be_mobile} && !$c->stash->{verifying} && $c->user->email_verified)) {
        $c->user->update({ phone => $phone, phone_verified => 0 });
        $c->flash->{flash_message} = _('You have successfully added your phone number.');
        $c->res->redirect('/my');
        $c->detach;
    }

    $c->forward('/auth/phone/sign_in', [ $parsed ]);
}

sub verify_item : Path('/auth/verify') : Args(1) {
    my ( $self, $c, $type ) = @_;
    $c->stash->{verifying} = 1;
    $c->detach("change_$type");
}

sub change_email_success : Path('/auth/change_email/success') {
    my ( $self, $c ) = @_;
    $c->flash->{flash_message} = _('You have successfully confirmed your email address.');
    $c->res->redirect('/my');
}

sub change_phone_success : Path('/auth/change_phone/success') {
    my ( $self, $c ) = @_;
    $c->flash->{flash_message} = _('You have successfully verified your phone number.');
    $c->res->redirect('/my');
}

sub change_password_success : Path('/auth/change_password/success') {
    my ( $self, $c ) = @_;
    $c->flash->{flash_message} = _('Your password has been changed');
    $c->res->redirect('/my');
}

sub generate_token : Path('/auth/generate_token') {
    my ($self, $c) = @_;

    $c->detach( '/page_error_403_access_denied', [] )
        unless $c->user and ( $c->user->is_superuser or $c->user->from_body );

    $c->stash->{template} = 'auth/generate_token.html';
    $c->forward('/auth/get_csrf_token');

    my $has_2fa = $c->user->get_extra_metadata('2fa_secret');

    if ($c->req->method eq 'POST') {
        $c->forward('/auth/check_csrf_token');

        if ($c->get_param('generate_token')) {
            my $token = mySociety::AuthToken::random_token();
            my $u = FixMyStreet::DB->resultset("User")->new({ password => $token });
            $c->user->set_extra_metadata('access_token', $u->password);
            $c->user->update;
            $c->stash->{token_generated} = $c->user->id . '-' . $token;
        }

        my $action = $c->get_param('2fa_action') || '';
        $action = 'deactivate' if $c->get_param('2fa_deactivate');
        $action = 'activate' if $c->get_param('2fa_activate');
        $action = 'activate' if $action eq 'deactivate' && $has_2fa && $c->cobrand->call_hook('must_have_2fa', $c->user);

        my $secret;
        if ($action eq 'deactivate') {
            $c->user->unset_extra_metadata('2fa_secret');
            $c->user->update;
            $c->stash->{toggle_2fa_off} = 1;
        } elsif ($action eq 'confirm') {
            $secret = $c->get_param('secret32');
            if ($c->check_2fa($secret)) {
                $c->user->set_extra_metadata('2fa_secret', $secret);
                $c->user->update;
                $c->stash->{stage} = 'success';
                $has_2fa = 1;
            } else {
                $action = 'activate'; # Incorrect code, reshow
            }
        }

        if ($action eq 'activate') {
            my $auth = FixMyStreet::Auth::GoogleAuth->new;
            $c->stash->{qr_code} = $auth->qr_code($secret, $c->user->email, $c->cobrand->base_url);
            $c->stash->{secret32} = $auth->secret32;
            $c->stash->{stage} = 'activate';
        }
    }

    $c->stash->{has_2fa} = $has_2fa ? 1 : 0;
    $c->stash->{existing_token} = $c->user->get_extra_metadata('access_token') ? 1 : 0;
}

__PACKAGE__->meta->make_immutable;

1;
