<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\V1\ApiUserRegisterRequest;
use App\Http\Requests\V1\ApiUserLoginRequest;
use Throwable;
use App\Exceptions\ApiHttpException;
use App\Http\Requests\V1\ApiDeleteUserRequest;
use App\Http\Resources\V1\UserResource;
use App\Http\Requests\V1\ApiUpdateUserFcmTokenRequest;
use App\Libs\Services\FcmService;
use App\Services\UserDataService;
use Illuminate\Support\Facades\Log;
use App\Models\User;
use Illuminate\Support\Facades\Password;
use Illuminate\Http\Request;
use App\Http\Requests\V1\PasswordChangeRequest;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;

class ApiAuthController extends Controller
{

    private UserDataService $userDataService;
    private FcmService $fcmService;

    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct(UserDataService $userDataSservice, FcmService $fcmService)
    {

        $this->userDataService = $userDataSservice;
        $this->fcmService = $fcmService;
        $this->middleware('auth:api', [
            'except' => [
                'login',
                'register',
                'forgotPassword',
                'resetPassword'
            ]
        ]);
    }

    /**
     * Register user.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function register(ApiUserRegisterRequest $request, UserDataService $userDataService)
    {

        $data = $request->validated();
        $user = $userDataService->createUserFromRequest($data);

        if (!$user) {
            return response()->json([
                'message' => trans('auth.register_not_ok'),
            ], 500);
        }

        return response()->json([
            'message' => trans('auth.register_ok'),
            'user' => $user
        ], 201);
    }

    /**
     * login user
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(ApiUserLoginRequest $request, FcmService $fcmService)
    {
        $data = $request->validated();

        $deviceId = isset($data['device_id']) ? $data['device_id'] : null;
        $fcmToken = isset($data['fcm_token']) ? $data['fcm_token'] : null;

        unset($data['device_id']);
        unset($data['fcm_token']);
        if (!$token = auth()->attempt($data)) {
            throw new ApiHttpException(401, 'failed_login');
        }

        //check if there were fcm subscriptions for this user on the database, if so restore them
        $user = auth()->user();

        //if supplied, update the device token
        if ($deviceId && $fcmToken) {
            $this->userDataService->updateFcmToken($user, $fcmToken, $deviceId);
        }

        //The user logged in,the subscriptions in FCM will be restored
        $fcmService->restoreFcmTopicSubscriptionsFromDatabase($user);
        return $this->respondWithToken($user, $token);
    }

    /**
     * Logout user
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout(FcmService $fcmService)
    {
        $user = auth()->user();

        auth()->logout();

        try {
            //This was introduced so that the user would not receive notifications while logged out of the application
            $fcmService->unsubscribeToAllTopics($user);
        } catch (Throwable $t) {
            Log::error($t);
        }



        return response()->json(['message' => trans('auth.logged_out')]);
    }

    /**
     * Refresh token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        $user  = auth()->user();
        $token = auth()->refresh();
        return $this->respondWithToken($user, $token);
    }

    /**
     * Get user profile.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function profile()
    {
        $user = auth()->user();
        $user->load(
            [
                'favouriteArtists' => function ($query) {
                    $query->full()->with('image');
                }, 'favouriteExhibitions' => function ($query) {
                    $query->full()->with('image');
                }, 'favouriteCollections' => function ($query) {
                    $query->full()->with('image');
                },
                'devices'
            ]
        );
        $data = new UserResource($user);
        return response()->json($data);
    }

    /**
     * Get the token array structure.
     *
     * @param  User $user
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($user, $token, $message = null)
    {
        $ret =  response()->json([
            'access_token'  => $token,
            'token_type'    => 'bearer',
            'expires_in'    => auth()->factory()->getTTL() * 60,
            'subscriptions' => $this->userDataService->getUserDatabaseStoredFcmSubscriptions($user),
        ]);

        if ($message) {
            $ret['message'] = trans('auth.login_ok');
        }

        return $ret;
    }


    /**
     * update the firebase cloud message token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function updateFcmToken(ApiUpdateUserFcmTokenRequest $request, UserDataService $userDataservice)
    {
        $user = auth()->user();
        $data = $request->validated();

        try {
            $userDataservice->updateFcmToken($user, $data['fcm_token'],  $data['device_id']);
        } catch (Throwable $t) {
            Log::error($t);
            return response()->json([
                'message' => trans('response.fcmtoken_update_not_ok'),
            ], 500);
        }

        return response()->json([
            'message' => trans('response.fcmtoken_update_ok'),
        ], 200);
    }


    /**
     * Process the a user request to generate a temporary code for password redefinition
     * @param Request $request
     * @throws ApiHttpException
     */
    public function forgotPassword(Request $request)
    {

        $request->validate(['email' => 'required|email|exists:users']);
        /*         $returnToken = null;
        $status = Password::sendResetLink(
            $request->only('email'),
            function (User $user, string $token) use (&$returnToken) {
                $returnToken = $token;
            }
        );
        dd($returnToken); */


        $status = Password::sendResetLink($request->only('email'));

        if ($status === Password::RESET_LINK_SENT) {
            return response()->json([
                'message' => trans($status),
            ], 200);
        }

        throw new ApiHttpException('500', 'unknown_error');
    }

    /**
     * Acceps a token, email and new password for resetting the user password
     */
    public function resetPassword(PasswordChangeRequest $request)
    {

        $data = $request->validated();
        $data['token'] = $data['password_token'];

        $status = Password::reset(
            $data,
            function ($user, $password) {
                $user->forceFill([
                    'password' => $password
                ])->setRememberToken(Str::random(60));

                $user->save();

                event(new PasswordReset($user));
            }
        );

        $code = 200;
        if ($status !== Password::PASSWORD_RESET) {
            $code = 401;
        }

        return response()->json([
            'message' => trans($status),
        ], $code);
    }


    /**
     * Remove user.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function remove(UserDataService $userDataService)
    {

        $user = Auth::user();
        if (!$user) {
            return response()->json([
                'message' => trans('auth.remove_not_found'),
            ], 500);
        }

        $this->fcmService->unsubscribeToAllTopics($user);
        $res = $userDataService->deleteUser($user);

        if ($res) {
            return response()->json([
                'message' => trans('auth.remove_ok'),
            ], 200);
        }

        return response()->json([
            'message' => trans('auth.remove_not_found'),
        ], 500);
    }
}
