<?php

namespace App\Http\Controllers\Web;

use App\CaseUpdate;
use App\City;
use App\CompanyRequest;
use App\ConsultingComplaint;
use App\ConsultingEvaluate;
use App\ConsultingRequest;
use App\ConsultingTopic;
use App\DocumentationRequest;
use App\Helpers\ImageHelper;
use App\Helpers\Sms;
use App\Http\Requests\ConsultingComplaintRequest;
use App\Http\Requests\ConsultingEvaluateRequest;
use App\Http\Requests\Web\RegisterRequest;
use App\Http\Requests\Web\UpdateProfileRequest;
use App\Jobs\CodeVerificationJob;
use App\OtherRequest;
use App\User;
use App\UsersNotification;
use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Request;
use App\Http\Requests\Web\RegAndLogPhoneRequest;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
use Mockery\Exception;
use function response;

class UserController extends Controller
{

    use ThrottlesLogins;

    /**
     * The maximum number of attempts to allow.
     *
     * @return int
     */
    protected $maxAttempts = 3;


    /**
     * The number of minutes to throttle for.
     *
     * @return int
     */
    protected $decayMinutes = 1;


    /**
     * The field name throttle trait will use for.
     *
     * @return string
     */
    public function username() : string
    {
        return 'username';
    }

    protected $view = 'web.users.';

    /**
     * User profile
     * @return Factory|View
     * @throws \Exception
     */
    public function profile()
    {
        $user = Auth::user();
        $title = __('members.my_profile');
        if (!in_array($user->role, ['user'])){
            return  abort(404);
        }
        $city = City::find($user->city_id) ?? null;
        $cityName = $city ? descriptions(City::find($user->city_id))->name : null;

        return view($this->view.'profile.index', get_defined_vars());
    }

    /**
     * All user consultings
     * @return Factory|View
     */
    public function myConsulting()
    {
        $title = __('members.my_consulting');
        $user = Auth::user();
        $consultings = ConsultingRequest::where('consulting_requests.user_id', $user->id)
            ->join('consulting_topics', 'consulting_topics.id', 'consulting_requests.consulting_topic_id')
            ->join('consulting_topic_descriptions as cDesc', 'consulting_topics.id', 'cDesc.consulting_topic_id')
            ->where('cDesc.language_id', currentLanguage()->id)
            ->select([
                'consulting_requests.*',
                'cDesc.name as consulting_subject'
            ])->cursor();
        return view($this->view.'my_consulting.index', get_defined_vars());
    }

    /**
     * Consulting details
     * @param ConsultingRequest $consultingRequest
     * @return Factory|View|void
     */
    public function myConsultingDetails(ConsultingRequest $consultingRequest)
    {
        $user = Auth::user();
        if ($user->id != $consultingRequest->user_id){
            return abort(404);
        }

        $title = __('activeConsultings.office_consulting');
        if ($consultingRequest->consulting_way == 'phone'){
            $title = __('activeConsultings.office_consulting');
        }
        if ($consultingRequest->consulting_way == 'zoom'){
            $title = __('activeConsultings.zoom_consulting');
        }

        $topic = ConsultingTopic::withDescription($consultingRequest->consulting_topic_id);
        return view($this->view.'my_consulting.show', get_defined_vars());

    }

    /**
     * Consulting evaluate
     * @param ConsultingEvaluateRequest $request
     * @param ConsultingRequest $consultingRequest
     * @return JsonResponse
     */
    public function consultingEvaluate(ConsultingEvaluateRequest $request, ConsultingRequest $consultingRequest)
    {
        $user = Auth::user();

        if ($user->id != $consultingRequest->user_id){
            return response()->json(['errors' => __('consultingEvaluates.evaluate_not_allowed')]);
        }

        if($consultingRequest->status != 'finished'){
            return response()->json(['errors' => __('consultingEvaluates.consulting_not_finished_yet')]);
        }

        $consultingEvaluateBefore = ConsultingEvaluate::where('consulting_request_id', $consultingRequest->id)->first();

        if ($consultingEvaluateBefore){
            return response()->json(['errors' => __('consultingEvaluates.consulting_evaluated_before')]);
        }

        $data = $request->all();
        $data['consulting_request_id'] = $consultingRequest->id;
        $data['user_id'] = $user->id;
        ConsultingEvaluate::create($data);
        return response()->json(['message' => __('consultingEvaluates.consulting_evaluated_success')]);

    }

    /**
     * Consulting complaint
     * @param ConsultingComplaintRequest $request
     * @param ConsultingRequest $consultingRequest
     * @return JsonResponse
     */
    public  function consultingComplaint(ConsultingComplaintRequest $request, ConsultingRequest $consultingRequest)
    {
        $user = Auth::user();

        if ($user->id != $consultingRequest->user_id){
            return response()->json(['errors' => __('consultingComplaints.complaint_not_allowed')]);
        }

        if($consultingRequest->status != 'finished'){
            return response()->json(['errors' => __('consultingComplaints.consulting_not_finished_yet')]);
        }

        $complaintEvaluateBefore = ConsultingComplaint::where('consulting_request_id', $consultingRequest->id)->first();

        $data = $request->all();
        $data['consulting_request_id'] = $consultingRequest->id;
        $data['user_id'] = $user->id;

        if ($complaintEvaluateBefore){
            $complaintEvaluateBefore->update($data);
            return response()->json(['message' => __('consultingComplaints.consulting_complaint_updated')]);
        }
        ConsultingComplaint::create($data);
        return response()->json(['message' => __('consultingComplaints.consulting_complaint_success')]);
    }


    /**
     * User documentation requests
     * @return Factory|View
     */
    public function myDocumentations()
    {
        $title = __('members.my_documentations');
        $user = Auth::user();
        $documentations = DocumentationRequest::where('user_id', $user->id)
            ->join('documentation_types', 'documentation_types.id', 'documentation_requests.documentation_type_id')
            ->join('documentation_type_descriptions as dtypeDesc', 'documentation_types.id', 'dtypeDesc.documentation_type_id')
            ->where('dtypeDesc.language_id', currentLanguage()->id)
            ->select(['documentation_requests.*', 'dtypeDesc.name as documentationTypeName'])->cursor();
        return view($this->view.'my_documentations.index', get_defined_vars());
    }


    /**
     * User documentation details
     * @param DocumentationRequest $documentationRequest
     * @return Factory|View|void
     */
    public function myDocumentationDetails(DocumentationRequest $documentationRequest)
    {
        $user = Auth::user();
        if ($user->id != $documentationRequest->user_id){
            return abort(404);
        }
        $languageId = currentLanguage()->id;
        $title = __('documentations.documentation_details');
        $documentation = DocumentationRequest::where('documentation_requests.id', $documentationRequest->id)
            ->where('documentation_requests.user_id', $user->id)
            ->join('documentation_types', 'documentation_types.id', 'documentation_requests.documentation_type_id')
            ->join('documentation_type_descriptions as dtypeDesc', 'documentation_types.id', 'dtypeDesc.documentation_type_id')
            ->where('dtypeDesc.language_id', $languageId);

        if ($documentationRequest->city_id){
            $documentation = $documentation->join('cities', 'cities.id', 'documentation_requests.city_id')
                ->join('city_descriptions as cDesc', 'cities.id', 'cDesc.city_id')
                ->where('cDesc.language_id', $languageId)->select([
                    'documentation_requests.*',
                    'cDesc.name as city_name',
                    'dtypeDesc.name as documentationTypeName'
                ])->first();
        } else {
            $documentation = $documentation->select([
                'documentation_requests.*',
                'dtypeDesc.name as documentationTypeName'
            ])->first();
        }
        return view($this->view.'my_documentations.show', get_defined_vars());
    }

    /**
     * User case requests
     * @return Factory|View
     */
    public function caseRequestsAndUpdates()
    {
        $title = __('caseRequests.case_requests_and_updates');
        $user = Auth::user();
        $cases = $user->cases(); // Cases Request
        return view($this->view.'case_requests_and_updates.index', get_defined_vars());
    }

    /**
     * User case updates
     * @return Factory|View
     */
    public function caseUpdates()
    {
        $title = __('members.case_updates');
        $user = Auth::user();
        $files = $user->files();
        return view($this->view.'case_updates.index', get_defined_vars());
    }


    public function myCompanyRequests()
    {
        $title = __('companyRequests.head');
        $user = Auth::user();
        $companyRequests = CompanyRequest::orderByDesc('company_requests.id')
            ->where('company_requests.user_id', $user->id)
            ->join('company_request_topics', 'company_request_topics.id', 'company_requests.company_request_topic_id')
            ->join('company_request_topic_descriptions as tDesc', 'company_request_topics.id', 'tDesc.company_request_topic_id')
            ->where('tDesc.language_id', currentLanguage()->id)
            ->select(['company_requests.*', 'tDesc.name as topicName'])->cursor();
        return view($this->view.'my_companies_requests.index', get_defined_vars());
    }

    public function myOtherRequests()
    {
        $title = __('otherRequests.head');
        $user = Auth::user();
        $otherRequests = OtherRequest::orderByDesc('other_requests.id')
            ->where('other_requests.user_id', $user->id)
            ->join('other_request_topics', 'other_request_topics.id', 'other_requests.other_request_topic_id')
            ->join('other_request_topic_descriptions as oDesc', 'other_request_topics.id', 'oDesc.other_request_topic_id')
            ->where('oDesc.language_id', currentLanguage()->id)
            ->select(['other_requests.*', 'oDesc.name as topicName'])->cursor();
        return view($this->view.'my_others_requests.index', get_defined_vars());
    }

    /**
     * User notifications
     * @return Factory|View
     */
    public function notifications()
    {
        $title = __('members.notifications');
        $user = Auth::user();
        $email_verified_at = $user->email_verified_at;
        $notifications = UsersNotification::orderByDesc('id')
            ->orWhere(function ($query) use ($user, $email_verified_at){
                $query->whereNull('user_id')
                    ->where('send_to', 'all_user')
                    ->where('created_at', '>=', $email_verified_at)
                    ->whereJsonDoesntContain('is_read', "$user->id");
            })
            ->orWhere(function ($query) use ($user, $email_verified_at){
                $query->where('user_id', $user->id)
                    ->whereNull('send_to')
                    ->where('created_at', '>=', $email_verified_at)
                    ->whereJsonDoesntContain('is_read', "$user->id");
            })->cursor();

        return view($this->view.'notifications.index', get_defined_vars());
    }


    public function makeNotificationAsRead(Request $request, UsersNotification $notification)
    {
        if ($request->ajax()){
            $user = Auth::user();
            $message = __('members.notification_deleted_successfully');
            if ($notification->id){
                if ($notification->user_id && ($notification->user_id != $user->id)){
                    return response()->json(['errors' => __('members.not_allowed_to_delete_notification')]);
                }

                $isRead = $notification->is_read;
                if (in_array($user->id, $isRead)){
                    return response()->json(['errors' => __('members.not_allowed_to_delete_notification')]);
                } else {
                    $isRead[] = "$user->id";
                }
                $notification->update(['is_read' => $isRead]);
            }
            if ($request->make_all_as_read){
                $notifications = UsersNotification::orderByDesc('id')
                    ->orWhere(function ($query) use ($user){
                        $query->whereNull('user_id')
                            ->where('send_to', 'all_user')
                            ->whereJsonDoesntContain('is_read', "$user->id");
                    })
                    ->orWhere(function ($query) use ($user){
                        $query->where('user_id', $user->id)
                            ->whereNull('send_to')
                            ->whereJsonDoesntContain('is_read', "$user->id");
                    })->cursor();

                if (count($notifications)){
                    foreach ($notifications as $notification) {
                        $isRead = $notification->is_read;
                        $isRead[] = "$user->id";
                        $notification->update(['is_read' => $isRead]);
                    }
                }
                $message = __('members.all_notifications_deleted_successfully');
            }

            return response()->json(['message' => $message]);

        }
    }

    /**
     * Show profile update from
     * @return Factory|View
     */
    public function profileForm()
    {
        $title = __('members.my_profile');
        $cities = City::withDescription();
        $user = Auth::user();
        return view($this->view.'profile.profile_form', get_defined_vars());
    }

    /**
     * Show phone change form
     * @return Factory|View
     */
    public function phoneForm()
    {
        $title = __('members.edit_phone');
        $user = Auth::user();
        return view($this->view.'profile.phone_form', get_defined_vars());
    }

    /**
     * Update user profile
     * @param UpdateProfileRequest $request
     * @param User $user
     * @return JsonResponse
     */
    public function updateProfile(UpdateProfileRequest $request, User $user)
    {
        $data = $request->all();
        if ($request->has('photo')){
           $photo = ImageHelper::upload($request->photo, 'users/profile', $user->photo);
           if ($photo['errors'] ?? null){
               return response()->json(['errors' => $photo['errors']]);
           }
            $data['photo'] = $photo;
        }

        $user->update($data);
        return response()->json(['url' => route('user.profile'),'message' => __('members.save_successfully')]);
    }

    /**
     * Update user phone
     * @param UpdateProfileRequest $request
     * @param User $user
     * @return JsonResponse
     * @throws ValidationException
     */
    public function updatePhone(UpdateProfileRequest $request, User $user)
    {
        if ($this->checkAttemptCount($request)){
            return $this->checkAttemptCount($request);
        }

        $generateVerifyCode = $this->generateVerifyCode($user);

        if ($generateVerifyCode['errors']){
            return $this->checkAttemptCount($request) ?? response()->json($generateVerifyCode);
        }

        session()->put([
            'operation_type' => 'change_phone',
            'user_phone' => $user->phone,
            'new_phone' => $request->phone
        ]);

        return response()->json(['url' => route('user.typeVerifyCode')]);
    }

    /**
     * Show type phone form
     * @param Request $request
     * @return Factory|View
     */
    public function login()
    {
        $title = __('members.login');
        session()->flash('login', 'yes');
        session()->put(['pev_url' => url()->previous()]);
        session()->forget(['operation_type', 'user_phone']);
        $buttonTitle = __('members.login');
        return view($this->view.'register_login', get_defined_vars());
    }

    /**
     * Show type phone form
     * @return Factory|RedirectResponse|View
     */
    public function register()
    {
        $title = __('members.register');
        session()->flash('login', 'no');
        session()->forget(['operation_type', 'user_phone']);
        $cities = City::withDescription();
        $buttonTitle = __('members.register');
        return view($this->view.'register_login', get_defined_vars());
    }

    /**
     * Submit login
     * @param RegAndLogPhoneRequest $request
     * @return JsonResponse
     * @throws ValidationException
     */
    public function doRegAndLogin(RegAndLogPhoneRequest $request)
    {
        $operationType = null;
        $user = User::where('phone', $request->phone)->first();
        if (url()->previous() == route('user.login')){
            if (!$user){
                return response()->json(['errors' => __('auth.failed')]);
            }

            /**
             *If user create account and go to login
             * without complete register
             * and without code verification
             */
            if (!$user->email_verified_at){
                if ($this->checkAttemptCount($request)){
                    return $this->checkAttemptCount($request);
                }

                $generateVerifyCode = $this->generateVerifyCode($user);

                if ($generateVerifyCode['errors']){
                    return $this->checkAttemptCount($request) ?? response()->json($generateVerifyCode);
                }
                session()->put(['operation_type' => 'register', 'user_phone' => $request->phone]);
                return response()->json(['url' => route('user.typeVerifyCode')]);
            }
            if (!$user->agreement){
                session()->put(['operation_type' => 'register', 'user_phone' => $request->phone]);
                return response()->json(['url' => route('user.completeRegister')]);
            }

            $operationType = 'login';
        }
        if (url()->previous() == route('user.register')){
            $date['phone'] = $request->phone;
            $date['role'] = 'user';
            $date['agreement'] = 0;
            $user = User::create($date);
            $operationType = 'register';
        }


        if ($this->checkAttemptCount($request)){
            return $this->checkAttemptCount($request);
        }

        $generateVerifyCode = $this->generateVerifyCode($user);

        if ($generateVerifyCode['errors']){
            return $this->checkAttemptCount($request) ?? response()->json($generateVerifyCode);
        }


        session()->put(['operation_type' => $operationType, 'user_phone' => $request->phone]);
        return $this->checkAttemptCount($request) ?? response()->json(['url' => route('user.typeVerifyCode')]);
    }


    /**
     * Show register form
     * @return Factory|View
     */
    public function completeRegister()
    {
        if (session('operation_type') != 'register'){
            return redirect()->route('user.login');
        }

        $user = User::where('phone', session('user_phone'))->first();
        if (!$user->email_verified_at){
            return redirect()->route('user.typeVerifyCode');
        }

        $cities = City::withDescription();
        return view($this->view.'complete_register', get_defined_vars());
    }


    /**
     * Register new user
     * @param RegisterRequest $request
     * @return JsonResponse
     */
    public function doCompleteRegister(RegisterRequest $request)
    {
        if ($request->ajax() && session('operation_type') == 'register'){
            $user = User::where('phone', session('user_phone'))->first();
            if ($user){
                $data = $request->all();
                $data['verify_code'] = null;
                $data['code_verify_attempt'] = null;
                $user->update($data);
            } else {
                $user = User::create($request->all());
            }
            Auth::login($user);
            session()->forget(['operation_type', 'user_phone']);
            return response()->json(['url' =>route('home')]);
        }
    }

    /**
     * check attempt try and generate code
     * @param $user
     * @return array
     */
    private function generateVerifyCode($user)
    {

        /**
         * It's commented for test only and after test
         * delete comment
         */

//        if ($user->attempt_send_code >= 6 && strtotime($user->last_attempt_date) == strtotime(date('Y-m-d')) ) {
//            return ['errors' => __('members.verification_code_exceed')];
//        }

        if ($user->last_attempt_date && strtotime($user->last_attempt_date) < strtotime(date('Y-m-d'))){
            $user->update(['code_verify_attempt' => 0, 'attempt_send_code' => 0,'last_attempt_date' => date('Y-m-d')]);
        }

        $attempt_send_code = $user->attempt_send_code + 1;
        $last_attempt_date = date('Y-m-d');
        $code = rand(1000,9999);

//        session()->put(['verify_code' => $code]);

        $phone = explode('0', $user->phone, 2);
        try{
            $message = __('members.sms_message');
            $message = str_replace('@@', $code, $message);
            Sms::send($phone[1], $message);
        } catch (Exception $exception){
            return ['errors' => __('members.sms_error')];
        }

        /**
         * $data = [
        'user' => $user->name,
        'email' => $user->email,
        'code' => $code,
        ];
        CodeVerificationJob::dispatch($data, 'web.members.emails.verification_email');
         */

        $user->update(
            [
                'verify_code' => $code,
                'attempt_send_code' => $attempt_send_code,
                'last_attempt_date' => $last_attempt_date,
            ]
        );

        return ['errors' => false];
    }

    /**
     * Show type verify code form
     * @return Factory|JsonResponse|View
     */
    public function typeVerifyCode()
    {
        $title = __('members.verification_code');
        if (!session('user_phone')){
            return redirect()->route('user.login')->with('errors',  'There\'s no code generated');
        }

        return view($this->view.'type_verify_code', get_defined_vars());
    }

    /**
     * Check verify code attempt and if it's correct or not
     * @param Request $request
     * @return JsonResponse
     * @throws ValidationException
     */
    public function verifyCode(Request $request)
    {

        $code = implode('',  array_reverse($request->code));

        if (mb_strlen($code) < 4){
            return $this->checkAttemptCount($request) ?? response()->json(['errors' => __('members.enter_full_code')]);
        }

        $user = User::where('phone', session('user_phone'))->first()
            ->where('verify_code', $code)->first();

        if (!$user){
            return $this->checkAttemptCount($request) ?? response()->json(['errors' => __('members.invalid_verification_code')]);
        }


        // Check the number of attempts to check the code
        if ($user->code_verify_attempt >= 6 && strtotime($user->last_attempt_date) == strtotime(date('Y-m-d')) ) {
            return $this->checkAttemptCount($request) ?? response()->json(['errors' => __('members.verification_tries_exceed')]);
        }

        // Check if date less then today
        if ($user->last_attempt_date && strtotime($user->last_attempt_date) < strtotime(date('Y-m-d'))){
            $user->update(['code_verify_attempt' => 0, 'last_attempt_date' => date('Y-m-d')]);
        }

        // The user entered the wrong code and the number of attempts increased
        if (strtotime($user->last_attempt_date) == strtotime(date('Y-m-d')) && $user->verify_code != $code){
            $user->update(['code_verify_attempt' => $user->code_verify_attempt+1]);
            return $this->checkAttemptCount($request) ?? response()->json(['errors' => __('members.invalid_verification_code')]);
        }

        $user->update(['verify_code' => null]);

        if (session('operation_type') == 'login'){
            Auth::login($user);
            session()->forget(['operation_type', 'user_phone']);
            return response()->json([
                'url' => session('pev_url') ?? route('home'),
                'message' => __('members.correct_code')]);
        }
        if (session('operation_type') == 'register'){
            $user->update(['email_verified_at' => date('Y-m-d')]);
            return response()->json([
                'url' => route('user.completeRegister'),
                'message' => __('members.correct_code')
            ]);
        }
        if (session('operation_type') == 'change_phone'){
            $user->update(['phone' => session('new_phone')]);
            session()->forget(['operation_type', 'user_phone', 'new_phone']);
            return response()->json([
                'url' => route('user.profile'),
                'message' => __('members.phone_changed_successfully')
            ]);
        }
    }

    /**
     * Resend code for register and login
     * @param Request $request
     * @return JsonResponse
     * @throws ValidationException
     */
    public function resendCode(Request $request)
    {
        if ($request->ajax()){
            $user = User::where('phone', session('user_phone'))->first();

            if ($this->checkAttemptCount($request)){
                return $this->checkAttemptCount($request);
            }

            $generateVerifyCode = $this->generateVerifyCode($user);

            if ($generateVerifyCode['errors']){
                return $this->checkAttemptCount($request) ?? response()->json($generateVerifyCode);
            }

            return response()->json(['success' => true]);
        }
    }

    /**
     * User logout
     * @return RedirectResponse
     */
    public function logout()
    {
        Auth::logout();
        return redirect()->route('home');
    }

    /**
     * Check user attempts count
     * @param $request
     * @return JsonResponse
     * @throws ValidationException
     */
    public function checkAttemptCount($request)
    {
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }
        $this->incrementLoginAttempts($request);

    }

    /**
     * Override parent sendLockoutResponse to handle it
     * to return json response
     * @param Request $request
     * @return JsonResponse
     */
    protected function sendLockoutResponse(Request $request)
    {
        $seconds = $this->limiter()->availableIn(
            $this->throttleKey($request)
        );
        return response()->json(['errors' => Lang::get('auth.throttle', [
            'seconds' => $seconds,
            'minutes' => ceil($seconds / 60),
        ])]);
    }


}
