<?php

namespace App\Repositories\Frontend\Auth;

use App\Events\Frontend\Auth\UserConfirmed;
use App\Events\Frontend\Auth\UserProviderRegistered;
use App\Exceptions\GeneralException;
use App\Models\Auth\SocialAccount;
use App\Models\Auth\User;
use App\Notifications\Frontend\Auth\UserNeedsConfirmation;
use App\Repositories\BaseRepository;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;


/**
 * Class UserRepository.
 */
class UserRepository extends BaseRepository
{
    /**
     * UserRepository constructor.
     *
     * @param User $model
     */
    public function __construct(User $model)
    {
        $this->model = $model;
    }

    /**
     * @param $token
     *
     * @return bool|\Illuminate\Database\Eloquent\Model
     */
    public function findByPasswordResetToken($token)
    {
        foreach (DB::table(config('auth.passwords.users.table'))->get() as $row) {
            if (password_verify($token, $row->token)) {
                return $this->getByColumn($row->email, 'email');
            }
        }

        return false;
    }

    /**
     * @param $uuid
     *
     * @return mixed
     * @throws GeneralException
     */
    public function findByUuid($uuid)
    {
        $user = $this->model
            ->uuid($uuid)
            ->first();

        if ($user instanceof $this->model) {
            return $user;
        }

        throw new GeneralException(__('exceptions.backend.access.users.not_found'));
    }

    /**
     * @param $code
     *
     * @return mixed
     * @throws GeneralException
     */
    public function findByConfirmationCode($code)
    {
        $user = $this->model
            ->where('confirmation_code', $code)
            ->first();

        if ($user instanceof $this->model) {
            return $user;
        }

        throw new GeneralException(__('exceptions.backend.access.users.not_found'));
    }

    /**
     * @param array $data
     *
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * !! User is created here on registration
     * @throws \Throwable
     * @throws \Exception
     */
    public function create(array $data)
    {
        return DB::transaction(function () use ($data) {
            $user = $this->model::create([
                'first_name' => isset($data['first_name']) ? $data['first_name'] : '',
                'last_name' => isset($data['last_name']) ? $data['last_name'] : '',
                'email' => $data['email'],
                'confirmation_code' => md5(uniqid(mt_rand(), true)),
                //'active' => true,
                'password' => $data['password'],
                // @adam:set first login to true at creation
                'first_login' => 'true',
                // If users require approval or needs to confirm email
                'confirmed' => !(config('access.users.requires_approval') || config('access.users.confirm_email')),
            ]);
            if ($user) {
                // Add the default site role to the new user
                $user->assignRole(config('access.users.default_role'));
            }

            /*
             * If users have to confirm their email and this is not a social account,
             * and the account does not require admin approval
             * send the confirmation email
             *
             * If this is a social account they are confirmed through the social provider by default
             */
            if (config('access.users.confirm_email')) {
                // Pretty much only if account approval is off, confirm email is on, and this isn't a social account.
                $user->notify(new UserNeedsConfirmation($user->confirmation_code));
            }
            //but maybe we still want to confirm them?
            // Return the user object
            return $user;
        });
    }


    /**
     * Create a user for beta testing.
     * @param array $data
     *
     * @return \Illuminate\Database\Eloquent\Model|mixed
     * !! User is created here on registration
     * @throws \Throwable
     * @throws \Exception
     */
    public function create_beta(array $data)
    {
        return DB::transaction(function () use ($data) {
            $user = $this->model::create([
                'first_name' => isset($data['first_name']) ? $data['first_name'] : '',
                'last_name' => isset($data['last_name']) ? $data['last_name'] : '',
                'email' => $data['email'],
                'confirmation_code' => md5(uniqid(mt_rand(), true)),
                //'active' => true,
                'password' => $data['password'],
                // @adam:set first login to true at creation
                'first_login' => 'true',
                // @adam:set inactive
                'active' => '0',
                // If users require approval or needs to confirm email
                'confirmed' => !(config('access.users.requires_approval') || config('access.users.confirm_email')),
            ]);
            if ($user) {
                // Add the default site role to the new user
                $user->assignRole(config('access.users.default_role'));
            }
            // Return the user object
            return $user;
        });
    }

    /**
     * @param       $id
     * @param array $input
     * @param bool|UploadedFile $image
     *
     * @return array|bool
     * @throws GeneralException
     */
    public function update($id, array $input, $image = false)
    {
        //   phpinfo();
        //   dd($image);

        $user = $this->getById($id);
        $user->first_name = $input['first_name'];
        $user->last_name = $input['last_name'];
        $user->avatar_type = $input['avatar_type'];
        $user->prefix_lookup_id = $input['prefix_lookup_id'] ? $input['prefix_lookup_id'] : $user->prefix_lookup_id;
        $user->position_lookup_id = $input['position_lookup_id'] ? $input['position_lookup_id'] : $user->position_lookup_id;
        $user->institution_lookup_id = $input['institution_lookup_id'] ? $input['institution_lookup_id'] : $user->institution_lookup_id;
        $user->research_focus_lookup_ids = $input['research_focus_lookup_ids'] ? $input['research_focus_lookup_ids'] : $user->research_focus_lookup_ids;
        $user->phone = $input['phone'] ? $input['phone'] : $user->phone;
        $user->public = $input['public'];


        // Upload profile image if necessary
        if ($image) {
            $type = strtolower($image->getClientOriginalExtension());

            // is this an allowed type?
            if (!in_array($type, ['jpg', 'jpeg', 'png'])) {
                throw new GeneralException('You must supply a jpg or png image.');
            }

            $img = Image::make($image);
            // fix any weird mobile orientation issues
            $img->orientate();

            // Make the image a 200x200 square
            $img->resize(($img->getHeight() > $img->getWidth()) ? 200 : null, ($img->getHeight() > $img->getWidth()) ? null : 200, function ($constraint) {
                $constraint->aspectRatio();
            });
            // crop to square
            $img->crop(200, 200);

            // save with the UUID as a filename
            $img->save(Storage::disk('public')->path('avatars/') . $user->uuid . '.jpg');

            $user->avatar_location = 'avatars/' . $user->uuid . '.jpg';


        } else {
            // No image being passed
            if ($input['avatar_type'] === 'storage') {
                // If there is no existing image
                if (auth()->user()->avatar_location === '') {
                    throw new GeneralException('You must supply a profile image.');
                }
            } else {
                // If there is a current image, and they are not using it anymore, get rid of it
                if (auth()->user()->avatar_location !== '') {
                    Storage::disk('public')->delete(auth()->user()->avatar_location);
                }

                $user->avatar_location = null;
            }
        }

        if ($user->canChangeEmail()) {
            //Address is not current address so they need to reconfirm
            if ($user->email !== $input['email']) {
                //Emails have to be unique
                if ($this->getByColumn($input['email'], 'email')) {
                    throw new GeneralException(__('exceptions.frontend.auth.email_taken'));
                }

                // Force the user to re-verify his email address if config is set
                if (config('access.users.confirm_email')) {
                    $user->confirmation_code = md5(uniqid(mt_rand(), true));
                    $user->confirmed = false;
                    $user->notify(new UserNeedsConfirmation($user->confirmation_code));
                }
                $user->email = $input['email'];
                $updated = $user->save();

                // Send the new confirmation e-mail

                return [
                    'success' => $updated,
                    'email_changed' => true,
                ];
            }
        }

        return $user->save();
    }

    /**
     * @param      $input
     * @param bool $expired
     *
     * @return bool
     * @throws GeneralException
     */
    public function updatePassword($input, $expired = false)
    {
        $user = $this->getById(auth()->id());

        //@TODO should you be logged out if you change your email?
        //$user->email = $input['email']?$input['email']:$user->email;

        if (Hash::check($input['old_password'], $user->password)) {
            if ($expired) {
                $user->password_changed_at = now()->toDateTimeString();
            }

            return $user->update(['password' => $input['password'], 'email' => $input['email'] ? $input['email'] : $user->email]);
        }

        throw new GeneralException(__('exceptions.frontend.auth.password.change_mismatch'));
    }

    /**
     * @param $code
     *
     * @return bool
     * @throws GeneralException
     */
    public function confirm($code)
    {
        $user = $this->findByConfirmationCode($code);

        if ($user->confirmed === true) {
            throw new GeneralException(__('exceptions.frontend.auth.confirmation.already_confirmed'));
        }

        if ($user->confirmation_code === $code) {
            $user->confirmed = true;

            event(new UserConfirmed($user));

            return $user->save();
        }

        throw new GeneralException(__('exceptions.frontend.auth.confirmation.mismatch'));
    }

    /**
     * @param $data
     * @param $provider
     *
     * @throws GeneralException
     * @return mixed
     */
    public function findOrCreateProvider($data, $provider)
    {
        // User email may not provided.
        $user_email = $data->email ?: "{$data->id}@{$provider}.com";

        // Check to see if there is a user with this email first.
        $user = $this->getByColumn($user_email, 'email');

        /*
         * If the user does not exist create them
         * The true flag indicate that it is a social account
         * Which triggers the script to use some default values in the create method
         */
        if (! $user) {
            // Registration is not enabled
            if (! config('access.registration')) {
                throw new GeneralException(__('exceptions.frontend.auth.registration_disabled'));
            }

            // Get users first name and last name from their full name
            $nameParts = $this->getNameParts($data->getName());

            $user = $this->model::create([
                'first_name' => $nameParts['first_name'],
                'last_name' => $nameParts['last_name'],
                'email' => $user_email,
                'active' => true,
                'confirmed' => true,
                'password' => null,
                'avatar_type' => $provider,
                // @adam: set first login to true
                'first_login' => 'true',
            ]);

            if ($user) {
                // Add the default site role to the new user
                $user->assignRole(config('access.users.default_role'));
            }

            event(new UserProviderRegistered($user));
        }

        // See if the user has logged in with this social account before
        if (! $user->hasProvider($provider)) {
            // Gather the provider data for saving and associate it with the user
            $user->providers()->save(new SocialAccount([
                'provider' => $provider,
                'provider_id' => $data->id,
                'token' => $data->token,
                'avatar' => $data->avatar,
            ]));
        } else {
            // Update the users information, token and avatar can be updated.
            $user->providers()->update([
                'token' => $data->token,
                'avatar' => $data->avatar,
            ]);

            $user->avatar_type = $provider;
            $user->update();
        }

        // Return the user object
        return $user;
    }

//    /**
//     * @param $data
//     * @param $provider
//     *
//     * @return mixed
//     * @throws GeneralException
//     */
//    public function findOrCreateProvider($data, $provider)
//    {
//        // User email may not provided.
//        $user_email = $data->email ?: "{$data->id}@{$provider}.com";
//
//        // Check to see if there is a user with this email first.
//
//        $user = $this->getByColumn($user_email, 'email')->withTrashed();
//
//        /*
//         * If the user does not exist create them
//         * The true flag indicate that it is a social account
//         * Which triggers the script to use some default values in the create method
//         */
//        if (!$user) {
//            // Registration is not enabled
//            if (!config('access.registration')) {
//                throw new GeneralException(__('exceptions.frontend.auth.registration_disabled'));
//            }
//
//            // Get users first name and last name from their full name
//            $nameParts = $this->getNameParts($data->getName());
//
//            $user = $this->model::create([
//                'first_name' => $nameParts['first_name'],
//                'last_name' => $nameParts['last_name'],
//                'email' => $user_email,
//                'active' => true,
//                'confirmed' => true,
//                'password' => null,
//                'avatar_type' => $provider,
//                // @adam: set first login to true
//                'first_login' => 'true',
//            ]);
//
//            if ($user) {
//                // Add the default site role to the new user
//                $user->assignRole(config('access.users.default_role'));
//            }
//
//            event(new UserProviderRegistered($user));
//        }
//
//        // See if the user has logged in with this social account before
//        if (!$user->hasProvider($provider)) {
//            // Gather the provider data for saving and associate it with the user
//            $user->providers()->save(new SocialAccount([
//                'provider' => $provider,
//                'provider_id' => $data->id,
//                'token' => $data->token,
//                'avatar' => $data->avatar,
//            ]));
//        } else {
//            // Update the users information, token and avatar can be updated.
//            $user->providers()->update([
//                'token' => $data->token,
//                'avatar' => $data->avatar,
//            ]);
//
//            $user->avatar_type = $provider;
//            $user->update();
//        }
//
//        // Return the user object
//        return $user;
//    }

    /**
     * @param $fullName
     *
     * @return array
     */
    protected function getNameParts($fullName)
    {
        $parts = array_values(array_filter(explode(' ', $fullName)));
        $size = count($parts);
        $result = [];

        if (empty($parts)) {
            $result['first_name'] = null;
            $result['last_name'] = null;
        }

        if (!empty($parts) && $size === 1) {
            $result['first_name'] = $parts[0];
            $result['last_name'] = null;
        }

        if (!empty($parts) && $size >= 2) {
            $result['first_name'] = $parts[0];
            $result['last_name'] = $parts[1];
        }

        return $result;
    }
}
