<?php

namespace App\Jobs;

use App\Services\FavouriteDataService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App;
use App\Libs\Services\FcmService;
use App\Services\DeviceDataService;
use App\Services\ExhibitionDataService;
use App\Services\FcmMessageDataService;
use Kreait\Firebase\Messaging\MessageTarget;
use Kreait\Firebase\Messaging\MulticastSendReport;
use Kreait\Firebase\Messaging\RawMessageFromArray;
use Kreait\Firebase\Messaging\SendReport;

class ProcessFavouriteNotifications implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected FavouriteDataService $favouriteDataService;
    protected FcmService $fcmService;
    protected FcmMessageDataService $fcmDataService;
    protected DeviceDataService $deviceDataService;
    public const QUEUE = "favourites";

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->onQueue(static::QUEUE);
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle(
        FcmService $fcmService,
        FavouriteDataService $favouriteDataService,
        FcmMessageDataService $fcmDataService,
        DeviceDataService $deviceDataService
    ) {
        $this->favouriteDataService = $favouriteDataService;
        $this->fcmService           = $fcmService;
        $this->fcmDataService       = $fcmDataService;
        $this->deviceDataService    = $deviceDataService;
    }


    protected function processExhibitionsFavourites($exhibitionsFavouritesInfo)
    {
        if (!FcmService::areFcmNotificationsActive()) {
            return;
        }

        //once the structure is returned we need to process it by grouping it by exhibition 
        //in order to send the same message to several tokens

        //structure index to the exhibitions id
        $messagesTosend = [];
        $langsInfo      = getAvailableLanguages();
        $langs          = array_keys($langsInfo);
        $originalLang   = app()->getLocale();
        foreach ($exhibitionsFavouritesInfo as $info) {
            $exhibitionId = $info->exhibition_id;
            $userFcmToken = $info->fcm_token;
            $userLanguage = $info->language;

            if (!isset($messagesTosend[$exhibitionId])) {
                //no structure for this exhibition yet
                //create a structure separated by languages

                foreach ($langs as $lang) {
                    App::setLocale($lang);
                    //dd($exhibitionId);
                    //get the localized exhibition content
                    $exhibition = ExhibitionDataService::getForNotification($exhibitionId);
                    if (!$exhibition) {
                        continue 2;
                    }

                    $artistNames   = '';

                    foreach ($exhibition->artists as $artist) {
                        $artistNames .= $artist->title . ', ';
                    }
                    $artistNames   = rtrim($artistNames, ', ');
                    $title         = trans('fcm.favourites.' . $this->typeIdentifier . '.title', [], $lang);
                    //add the emoji
                    $title = "\u{2B50}" . ' ' . $title;
                    $transFields   = [
                        'name'       => $exhibition->title,
                        'artistName' => $artistNames,
                        'location'   => $exhibition->location->title,
                        'cityName'   => $exhibition->location->city->title,
                        'endDate'    => $exhibition->date_to->format('d.m.Y'),
                    ];
                    $body          = trans('fcm.favourites.' . $this->typeIdentifier . '.body', $transFields, $lang);

                    $langStructure = [
                        'message' => [
                            'title'  => $title,
                            'body'   => $body,
                        ],
                        'data'    => [
                            'user_id' => $info->user_id,
                            'type'    => 'exposicao',
                            'id'      => $exhibition->id,
                        ],
                        'tokens'  => [],
                    ];
                    $exhibitionMessageStruct[$lang] = $langStructure;
                }


                $messagesTosend[$exhibitionId] = $exhibitionMessageStruct;
            }

            App::setLocale($originalLang);


            //At this point the message structure should be set
            //We just need to add the token in the correct language

            $messagesTosend[$exhibitionId][$userLanguage]['tokens'][] = $userFcmToken;
        }

        $this->processMessagesToSend($messagesTosend);
    }


    /**
     * Gets the messages structure, process it and sends the messages
     * @param array $messagesTosend
     */
    protected function processMessagesToSend(array $messagesTosend): void
    {

        $langsInfo = getAvailableLanguages();
        $langs     = array_keys($langsInfo);

        foreach ($messagesTosend as $exhibitionId => $messageTosend) {

            //each message to send is divided in languages we sill need to process them individualy

            foreach ($langs as $lang) {

                $messageInLang = $messageTosend[$lang];
                $existingData  = $messageInLang['data'];
                if (empty($messageInLang['tokens'])) {
                    //no one to send this message to
                    continue;
                }

                $data = $existingData + [
                    'id'   => $exhibitionId,
                    'type' => 'exhibition'
                ];

                $message = $this->fcmService->getTokenMessageStructure(
                    $messageInLang['message']['title'],
                    $messageInLang['message']['body'],
                    $data
                );

                //send the message in chunks and process the results to avoid duplicate messages
                $resSend = $this->fcmService->sendMessageToTokens($message, $messageInLang['tokens']);

                $this->processSendToTokensResult($resSend);
            }
        }
    }


    /**
     * Process the results of the sent messages
     */
    protected function processSendToTokensResult(array $sendResults): void
    {

        foreach ($sendResults as $sendReport) {

            /**@var MulticastSendReport $sendReport */
            $reportItems = $sendReport->getItems();
            foreach ($reportItems as $reportItem) {

                /**@var RawMessageFromArray message  */
                $message     = $reportItem->message();
                $messageArr  = $message->jsonSerialize();
                $userId      = $messageArr['data']['user_id'];


                /**@var SendReport $reportItem */
                if ($reportItem->isSuccess()) {
                    //all went well we can create the message in the fcm_messages_table
                    $result     = $reportItem->result();
                    /**@var string $fcmId  */
                    $fcmId      = $result['name'];
                    $contentType = Exhibition::class;
                    $contentId   = $messageArr['data']['id'];
                    $messageContents = $messageArr;

                    $this->fcmDataService->addTokenMessage(
                        $userId,
                        $this->typeIdentifier,
                        $fcmId,
                        $contentId,
                        $contentType,
                        $messageContents
                    );

                    continue;
                }

                //something went wrong with the sending of the message
                //the assumption is that the token is invalid and we clear it from the devices table for this user

                /**@var MessageTarget $target */
                $target = $reportItem->target();
                $token  = $target->value();
                $this->deviceDataService->clearDeviceFcmToken($userId, $token);
            }
        }
    }
}
