<?php

namespace App\Http\Controllers;

use App\Folder;
use App\Mailbox;
use App\Subscription;
use App\Thread;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\Rule;
use Validator;

class UsersController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Users list.
     */
    public function users()
    {
        $this->authorize('create', 'App\User');

        $users_query = User::nonDeleted();

        // Exclude admins from list for non-admins.
        if (!auth()->user()->isAdmin()) {
            $users_query->where('role', User::ROLE_USER);
        }

        $users = $users_query->get();
        $users = User::sortUsers($users);

        return view('users/users', ['users' => $users]);
    }

    /**
     * New user.
     */
    public function create()
    {
        $this->authorize('create', 'App\User');
        $mailboxes = Mailbox::all();

        return view('users/create', ['mailboxes' => $mailboxes]);
    }

    /**
     * Create new user.
     *
     * @param \Illuminate\Http\Request $request
     */
    public function createSave(Request $request)
    {
        $invalid = false;
        $this->authorize('create', 'App\User');
        $auth_user = auth()->user();

        $rules = [
            'first_name' => 'required|string|max:20',
            'last_name'  => 'max:30',
            'email'      => 'required|string|email|max:100|unique:users',
            //'role'       => ['required', Rule::in(array_keys(User::$roles))],
        ];
        if ($auth_user->isAdmin()) {
            $rules['role'] = ['required', Rule::in(array_keys(User::$roles))];
        }
        if (empty($request->send_invite)) {
            $rules['password'] = 'required|string|max:255';
        }
        $validator = Validator::make($request->all(), $rules);

        if (User::mailboxEmailExists($request->email)) {
            $invalid = true;
            $validator->errors()->add('email', __('There is a mailbox with such email. Users and mailboxes can not have the same email addresses.'));
        }

        if ($invalid || $validator->fails()) {
            return redirect()->route('users.create')
                        ->withErrors($validator)
                        ->withInput();
        }

        $password = '';
        if (empty($request->send_invite)) {
            // Set password from request.
            $password = $request->password;
        } else {
            // Set some random password before sending invite.
            $password = User::generateRandomPassword();
        }

        $data = [
            'first_name' => $request->first_name,
            'last_name' => $request->last_name ?? '',
            'email' => $request->email,
            'password' => $password,
        ];

        $user = User::create($data);

        if (!$auth_user->can('changeRole', $user)) {
            $user->role = User::ROLE_USER;
        } elseif (array_key_exists($request->role, User::$roles)) {
            $user->role = $request->role;
        }

        // Set system timezone.
        $user->timezone = config('app.timezone') ?: User::DEFAULT_TIMEZONE;
        $user = \Eventy::filter('user.create_save', $user, $request);
        $user->save();

        $user->mailboxes()->sync($request->mailboxes ?: []);
        $user->syncPersonalFolders($request->mailboxes);

        // Send invite
        if (!empty($request->send_invite)) {
            try {
                $user->sendInvite(true);
            } catch (\Exception $e) {
                // Admin is allowed to see exceptions
                \Session::flash('flash_error_floating', $e->getMessage().' — '.__('Check mail settings in "Manage » Settings » Mail Settings"'));
            }
        }

        \Session::flash('flash_success_floating', __('User created successfully'));

        return redirect()->route('users.profile', ['id' => $user->id]);
    }

    /**
     * User profile.
     */
    public function profile($id)
    {
        $user = User::findOrFail($id);
        if ($user->isDeleted()) {
            abort(404);
        }

        $this->authorize('update', $user);

        $users = $this->getUsersForSidebar($id);

        return view('users/profile', ['user' => $user, 'users' => $users]);
    }

    public function getUsersForSidebar($except_id)
    {
        if (auth()->user()->isAdmin()) {
            return User::sortUsers(User::nonDeleted()->get()); // ->except($except_id);
        } else {
            return [];
        }
    }

    /**
     * Handle a registration request for the application.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\Response
     */
    public function profileSave($id, Request $request)
    {
        $invalid = false;

        $user = User::findOrFail($id);
        $this->authorize('update', $user);

        $auth_user = auth()->user();

        // This is also present in PublicController::userSetup
        $validator = Validator::make($request->all(), [
            'first_name'  => 'required|string|max:20',
            'last_name'   => 'max:30',
            'email'       => 'required|string|email|max:100|unique:users,email,'.$id,
            //'emails'      => 'max:100',
            'job_title'   => 'max:100',
            'phone'       => 'max:60',
            'timezone'    => 'required|string|max:255',
            'time_format' => 'required',
            'role'        => ['nullable', Rule::in(array_keys(User::$roles))],
            'photo_url'   => 'nullable|image|mimes:jpeg,png,jpg,gif',
        ]);
        $validator->setAttributeNames([
            'photo_url'   => __('Photo'),
        ]);

        // Photo
        $validator->after(function ($validator) use ($user, $request) {
            if ($request->hasFile('photo_url')) {
                $path_url = $user->savePhoto($request->file('photo_url'));

                if ($path_url) {
                    $user->photo_url = $path_url;
                } else {
                    $invalid = true;
                    $validator->errors()->add('photo_url', __('Error occurred processing the image. Make sure that PHP GD extension is enabled.'));
                }
            }

            // Do not allow to remove last administrator
            if ($user->isAdmin() && isset($request->role) && $request->role != User::ROLE_ADMIN) {
                $admins_count = User::where('role', User::ROLE_ADMIN)->count();
                if ($admins_count < 2) {
                    $invalid = true;
                    $validator->errors()->add('role', __('Role of the only one administrator can not be changed.'));
                }
            }
        });

        if (User::mailboxEmailExists($request->email)) {
            $invalid = true;
            $validator->errors()->add('email', __('There is a mailbox with such email. Users and mailboxes can not have the same email addresses.'));
        }

        if ($invalid || $validator->fails()) {
            return redirect()->route('users.profile', ['id' => $id])
                        ->withErrors($validator)
                        ->withInput();
        }

        // Save language into session.
        if ($auth_user->id == $id && $request->locale) {
            session()->put('user_locale', $request->locale);
        }

        $request_data = $request->all();

        if (isset($request_data['photo_url'])) {
            unset($request_data['photo_url']);
        }
        if (!$auth_user->can('changeRole', $user)) {
            unset($request_data['role']);
        }
        if ($user->status != User::STATUS_DELETED) {
            if (!empty($request_data['disabled'])) {
                $request_data['status'] = User::STATUS_DISABLED;
            } else {
                $request_data['status'] = User::STATUS_ACTIVE;
            }
        }

        // Sanitize $request_data array.
        // $allowed_fields = [
        //     'first_name',
        //     'last_name',
        //     'email', 
        //     'emails',
        //     'job_title',
        //     'phone',
        //     'locale',
        //     'timezone'
        //     'time_format',
        //     'photo_url',
        // ];
        $admin_fields = [
            'role',
            'status',
            'email',
            'only_assigned_tickets',
        ];
        $nonfillable_fields = [
            'type',
            'password',
        ];
        if (!$auth_user->isAdmin()) {
            foreach ($admin_fields as $field) {
                if (isset($request_data[$field])) {
                    unset($request_data[$field]);
                }
            }
        }
        foreach ($nonfillable_fields as $field) {
            if (isset($request_data[$field])) {
                unset($request_data[$field]);
            }
        }

        $user->setData($request_data);

        if ($auth_user->isAdmin() && !$user->isAdmin()) {
            if (!empty($request_data['only_assigned_tickets'])) {
                $user->addPermission(User::PERM_ONLY_ASSIGNED_TICKETS, false);
            } else {
                $user->removePermission(User::PERM_ONLY_ASSIGNED_TICKETS, false);
            }
        }

        $user = \Eventy::filter('user.save_profile', $user, $request);

        $user->save();

        \Session::flash('flash_success_floating', __('Profile saved successfully'));

        return redirect()->route('users.profile', ['id' => $id]);
    }

    /**
     * User permissions.
     */
    public function permissions($id)
    {
        $user = auth()->user();
        if (!$user->isAdmin()) {
            abort(403);
        }

        $user = User::findOrFail($id);

        if ($user->isDeleted()) {
            abort(404);
        }

        $mailboxes = Mailbox::all()->sortBy('name');

        $users = $this->getUsersForSidebar($id);

        return view('users/permissions', [
            'user'           => $user,
            'mailboxes'      => $mailboxes,
            'user_mailboxes' => $user->mailboxes,
            'users'          => $users,
        ]);
    }

    /**
     * Save user permissions.
     *
     * @param int                      $id
     * @param \Illuminate\Http\Request $request
     */
    public function permissionsSave($id, Request $request)
    {
        $user = auth()->user();
        if (!$user->isAdmin()) {
            abort(403);
        }

        $user = User::findOrFail($id);

        $user->mailboxes()->sync($request->mailboxes ?: []);
        $user->syncPersonalFolders($request->mailboxes);

        // Save permissions.
        $user_permissions = $request->user_permissions ?? [];
        $permissions = [];

        foreach (User::getUserPermissionsList() as $permission_id) {
            $new_has_permission = in_array($permission_id, $user_permissions);

            if ($user->hasPermission($permission_id, false) != $new_has_permission) {
                $permissions[$permission_id] = (int)(bool)$new_has_permission;
                $save_user = true;
            }
        }
        $user->permissions = $permissions;
        $user->save();

        \Session::flash('flash_success_floating', __('Permissions saved successfully'));

        return redirect()->route('users.permissions', ['id' => $id]);
    }

    /**
     * User notifications settings.
     */
    public function notifications($id)
    {
        $user = User::findOrFail($id);
        $this->authorize('updateNotifications', $user);

        if ($user->isDeleted()) {
            abort(404);
        }
        
        $subscriptions = $user->subscriptions()->select('medium', 'event')->get();

        $person = '';
        if ($id != auth()->user()->id) {
            $person = $user->getFirstName(true);
        }

        $users = $this->getUsersForSidebar($id);

        return view('users/notifications', [
            'user'          => $user,
            'subscriptions' => $subscriptions,
            'person'        => $person,
            'users'         => $users,
            'mobile_available' => \Eventy::filter('notifications.mobile_available', false),
        ]);
    }

    /**
     * Save user notifications settings.
     *
     * @param int                      $id
     * @param \Illuminate\Http\Request $request
     */
    public function notificationsSave($id, Request $request)
    {
        $user = User::findOrFail($id);
        $this->authorize('updateNotifications', $user);

        Subscription::saveFromArray($request->subscriptions, $user->id);

        \Session::flash('flash_success_floating', __('Notifications saved successfully'));

        return redirect()->route('users.notifications', ['id' => $id]);
    }

    /**
     * Users ajax controller.
     */
    public function ajax(Request $request)
    {
        $response = [
            'status' => 'error',
            'msg'    => '', // this is error message
        ];

        $auth_user = auth()->user();

        switch ($request->action) {

            // Both send and resend
            case 'send_invite':
                if (!$auth_user->isAdmin()) {
                    $response['msg'] = __('Not enough permissions');
                }
                if (empty($request->user_id)) {
                    $response['msg'] = __('Incorrect user');
                }
                if (!$response['msg']) {
                    $user = User::find($request->user_id);
                    if (!$user) {
                        $response['msg'] = __('User not found');
                    } elseif ($user->invite_state == User::INVITE_STATE_ACTIVATED) {
                        $response['msg'] = __('User already accepted invitation');
                    }
                }

                if (!$response['msg']) {
                    try {
                        $user->sendInvite(true);

                        $response['status'] = 'success';
                    } catch (\Exception $e) {
                        // Admin is allowed to see exceptions.
                        $response['msg'] = $e->getMessage().' — '.__('Check mail settings in "Manage » Settings » Mail Settings"');
                    }
                }
                break;

            // Reset password
            case 'reset_password':
                if (!auth()->user()->isAdmin()) {
                    $response['msg'] = __('Not enough permissions');
                }
                if (empty($request->user_id)) {
                    $response['msg'] = __('Incorrect user');
                }
                if (!$response['msg']) {
                    $user = User::find($request->user_id);
                    if (!$user) {
                        $response['msg'] = __('User not found');
                    }
                }

                if (!$response['msg']) {
                    $reset_result = Password::broker()->sendResetLink(
                        //['id' => $request->user_id]
                        ['id' => $request->user_id]
                    );

                    if ($reset_result == Password::RESET_LINK_SENT) {
                        $response['status'] = 'success';
                        $response['msg_success'] = __('Password reset email has been sent');
                    }
                }
                break;

            // Load website notifications
            case 'web_notifications':
                if (!$auth_user) {
                    $response['msg'] = __('You are not logged in');
                }
                if (!$response['msg']) {
                    $web_notifications_info = $auth_user->getWebsiteNotificationsInfo(false);
                    $response['html'] = view('users/partials/web_notifications', [
                        'web_notifications_info_data' => $web_notifications_info['data'],
                    ])->render();

                    $response['has_more_pages'] = (int) $web_notifications_info['notifications']->hasMorePages();

                    $response['status'] = 'success';
                }
                break;

            // Mark all user website notifications as read
            case 'mark_notifications_as_read':
                if (!$auth_user) {
                    $response['msg'] = __('You are not logged in');
                }
                if (!$response['msg']) {
                    $auth_user->unreadNotifications()->update(['read_at' => now()]);
                    $auth_user->clearWebsiteNotificationsCache();

                    $response['status'] = 'success';
                }
                break;

            // Delete user photo
            case 'delete_photo':
                $user = User::find($request->user_id);

                if (!$user) {
                    $response['msg'] = __('User not found');
                } elseif (!$auth_user->can('update', $user)) {
                    $response['msg'] = __('Not enough permissions');
                }
                if (!$response['msg']) {
                    $user->removePhoto();
                    $user->save();

                    $response['status'] = 'success';
                }
                break;

            // Delete user
            case 'delete_user':
                $user = User::find($request->user_id);

                if (!$user) {
                    $response['msg'] = __('User not found');
                } elseif (!$auth_user->can('delete', $user)) {
                    $response['msg'] = __('Not enough permissions');
                } elseif ($auth_user->id == $user->id) {
                    // Do not allow admin delete himself.
                    $response['msg'] = __('Not enough permissions');
                }

                // Check if the user is the only one admin
                // - not needed as only admin can delete  users and
                // current admin can not delete himself.
                // if (!$response['msg'] && $user->isAdmin()) {
                //     $admins_count = User::where('role', User::ROLE_ADMIN)->count();
                //     if ($admins_count < 2) {
                //         $response['msg'] = __('Administrator can not be deleted');
                //     }
                // }

                if (!$response['msg']) {

                    $user->deleteUser($auth_user, $request->assign_user);

                    \Session::flash('flash_success_floating', __('User deleted').': '.htmlspecialchars($user->getFullName()));

                    $response['status'] = 'success';
                }
                break;

            default:
                $response['msg'] = 'Unknown action';
		        $response = \Eventy::filter('users.ajax.response_default', $response, $request);
                break;
        }

        if ($response['status'] == 'error' && empty($response['msg'])) {
            $response['msg'] = 'Unknown error occurred';
        }

        return \Response::json($response);
    }

    /**
     * Change user password.
     */
    public function password($id)
    {
        $user = User::findOrFail($id);
        $this->authorize('update', $user);

        $users = User::all()->except($id);

        return view('users/password', ['user' => $user, 'users' => $users]);
    }

    /**
     * Save changed user password.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return \Illuminate\Http\Response
     */
    public function passwordSave($id, Request $request)
    {
        // It is allowed to edit only your own password
        $user = auth()->user();
        if ($user->id != $id) {
            abort(403);
        }

        // This is also present in PublicController::userSetup
        $validator = Validator::make($request->all(), [
            'password_current' => 'required|string',
            'password'         => 'required|string|min:8|confirmed',
        ]);

        $validator->after(function ($validator) use ($user, $request) {
            // Check current password
            if (!Hash::check($request->password_current, $user->password)) {
                $validator->errors()->add('password_current', __('This password is incorrect.'));
            } elseif (Hash::check($request->password, $user->password)) {
                // Check new password
                $validator->errors()->add('password', __('The new password is the same as the old password.'));
            }
        });

        if ($validator->fails()) {
            return redirect()->route('users.password', ['id' => $id])
                        ->withErrors($validator)
                        ->withInput();
        }

        $user->password = bcrypt($request->password);
        $user->save();

        $user->sendPasswordChanged();

        \Session::flash('flash_success_floating', __('Password saved successfully!'));

        return redirect()->route('users.profile', ['id' => $id]);
    }
}