<?php

namespace App\Services;

use App\Models\User;
use App\Models\UserFcmSubscription;
use App\Services\DataService;
use App\Models\Device;
use App\Notifications\DeleteUserRequest;
use Illuminate\Support\Facades\DB;
use Throwable;
use Illuminate\Support\Facades\Log;

class UserDataService extends DataService
{


    /**
     * Creates a user and the device based on the request information
     */
    public function createUserFromRequest(array $requestData): ?User
    {

        $fcmToken = null;
        if (isset($requestData['fcm_token'])) {
            $fcmToken = $requestData['fcm_token'];
            unset($requestData['fcm_token']);
        }
        $deviceId = $requestData['device_id'];
        unset($requestData['device_id']);
        DB::beginTransaction();
        try {
            $user = User::create($requestData);
            Device::create(
                [
                    'identifier' => $deviceId,
                    'fcm_token'  => $fcmToken,
                    'user_id'    => $user->id,
                ]
            );
            DB::commit();
            return $user;
        } catch (Throwable $t) {
            Log::error($t);
            DB::rollback();
        }
        return null;
    }



    /**
     * Updates a user firebase token
     *
     * @param  User $user
     * @param  string $token
     * @param  string $deviceId
     * @return User
     */
    public function updateFcmToken(User $user, string $token, string $deviceId): User
    {
        $device = Device::firstOrCreate([
            'user_id'    => $user->id,
            'identifier' =>  $deviceId,
        ]);

        $device->fcm_token = $token;
        $device->save();
        return $user;
    }


    /**
     * Add a subscription information for a user and a topic
     * @param User $user
     * @param string $topic
     * @return UserFcmSubscription
     */
    public function createUserFcmSubscription(User $user, string $topic): UserFcmSubscription
    {
        $params = [
            'user_id' => $user->id,
            'topic'   => $topic,
        ];

        $subscription =  UserFcmSubscription::firstOrCreate($params);
        $subscription->save();
        return $subscription;
    }

    /**
     * removes a subscription information for a user and a topic
     * @param User $user
     * @param string $topic
     * @return integer
     */
    public function removeUserFcmSubscription(User $user, string $topic): int
    {

        return DB::table('fcm_topic_subscription')
            ->where('user_id',  $user->id)
            ->where('topic',  $topic)
            ->delete();
    }

    /**
     * removes all user topic subscriptions from the database
     * @param User $user
     * @return integer
     */
    public function removeAllUserFcmSubscription(User $user): int
    {

        return DB::table('fcm_topic_subscription')
            ->where('user_id',  $user->id)
            ->delete();
    }


    /**
     * Gets all the FCM subscriptions stored in the database for a user
     * @param User $user
     * @return array
     */
    public function getUserDatabaseStoredFcmSubscriptions(User $user): array
    {
        return DB::table('fcm_topic_subscription')
            ->select('topic')
            ->where('user_id',  $user->id)
            ->pluck('topic')
            ->toArray();
    }


    /**
     * Gets all the user device tokens for a user Id from the email
     * @param string $email 
     * @return array
     */
    public function getUserDeviceTokensByEmail(String $email): array
    {

        $user = User::where('email', $email)->first();

        if ($user) {
            return DB::table('devices')
                ->select('fcm_token')
                ->where('user_id',  $user->id)
                ->whereNotNull('fcm_token')
                ->pluck('fcm_token')
                ->toArray();
        }

        return [];
    }



    /**
     * Sets to null a set of token devices for a user
     * @param User $user
     * @param array $invalidTokens
     * 
     * @return int
     */
    public function clearInvalidTokensFromDatabase(User $user, array $invalidTokens): int
    {
        return DB::table('devices')
            ->where('user_id', $user->id)
            ->whereIn('fcm_token', $invalidTokens)
            ->update(['fcm_token' => null]);
    }

    /**
     * Gets a user using the email
     * @param string $email 
     * @return ?User
     */
    public function getUserByEmail(String $email): ?User
    {
        return User::where('email', $email)->first();
    }


    /**
     * Removes a user (and the device by association ) based on the request information
     */
    public function deleteUser($user): bool
    {

        try {
            return $user->delete();
        } catch (Throwable $t) {
            Log::error($t);
        }
        return false;
    }

    /**
     * Sends an email with a link to delete user data
     *
     * @param User $user
     * @return void
     */
    public function sendDeleteUserLink($user)
    {
        if (
            !$user->delete_request_date ||
            $user->delete_request_date->addMinutes(User::DELETE_USER_REQUEST_EXPIRATION) < now()
        ) {
            // generates new token
            $user->delete_request_token = random_string('alnum', 32);
        }

        // Only re-sends email after 3 minutes
        if (!$user->delete_request_date || $user->delete_request_date->addMinutes(3) < now()) {
            $user->delete_request_date = now();
            $user->notify(new DeleteUserRequest($user->delete_request_token));
        }
        
        $user->save();
    }
}
