<?php
/**
 * Created by PhpStorm.
 * User: leemason
 * Date: 30/10/15
 * Time: 19:34.
 */

namespace App\Providers;

use App\Conversation;
use App\Notifications\BroadcastNotification;
use Carbon\Carbon;
use Illuminate\Broadcasting\BroadcastManager;
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;

class PolycastServiceProvider extends ServiceProvider
{
    //public function boot(BroadcastingFactory $factory){
    public function boot()
    {

        // register the polycast driver
        // $factory->extend('polycast', function(/*Application $app*/){
        //     return new PolycastBroadcaster();
        // });
        //$factory->extend('polycast', function (Application $app, $config) {
        //app('Illuminate\Broadcasting\BroadcastManager')->extend('polycast', function (array $config) {
        //$broadcastManager->extend('polycast', function (array $config) {

        // This has to be done in BroadcastServiceProvider to avoid "Driver [driver] is not supported" error
        // $this->app[BroadcastManager::class]->extend('polycast', function (array $config) {
        //     echo 'we are in extend';
        //     return new \App\Misc\PolycastBroadcaster();
        // });

        // $this->publishes([
        //     __DIR__.'/../dist/js/polycast.js' => public_path('vendor/polycast/polycast.js'),
        //     __DIR__.'/../dist/js/polycast.min.js' => public_path('vendor/polycast/polycast.min.js'),
        // ], 'public');

        // $this->publishes([
        //     __DIR__.'/../migrations/' => database_path('migrations')
        // ], 'migrations');

        $this->app['router']->group(['middleware' => ['web'], 'prefix' => \Helper::getSubdirectory()], function ($router) {

            // establish connection and send current time
            $this->app['router']->post('polycast/connect', function (Request $request) {
                \Broadcast::auth($request);

                return ['status' => 'success', 'time' => Carbon::now()->toDateTimeString()];
            });

            // send payloads to requested browser
            $this->app['router']->post('polycast/receive', function (Request $request) {
                \Broadcast::auth($request);

                $query = \DB::table('polycast_events')
                    ->select('*');

                $channels = $request->get('channels', []);

                // $request->channels must contain a list of channels.
                // https://github.com/freescout-help-desk/freescout/security/advisories/GHSA-7x4c-p73f-67x5
                if (empty($channels)) {
                    \Helper::denyAccess();
                }

                foreach ($channels as $channel => $events) {

                    // https://github.com/freescout-help-desk/freescout/security/advisories/GHSA-gh3r-jh6q-wrvj
                    $channel = \Helper::sqlEscapeLike($channel);

                    foreach ($events as $event) {
                        // No need to add index to DB for this query.
                        $query->orWhere(function ($query) use ($channel, $event, $request) {
                            $query->where('channels', 'like', '%"'.$channel.'"%')
                                ->where('event', '=', $event)
                                // Recors are fetched starting from opening the page or from the last event.
                                ->where('created_at', '>=', $request->get('time'));
                        });
                    }
                }

                $collection = collect($query->get());

                $payload = $collection->map(function ($item, $key) use ($request) {
                    $created = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $item->created_at);
                    $requested = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $request->get('time'));
                    $item->channels = json_decode($item->channels, false);
                    $item->payload = json_decode($item->payload, false);
                    // Add extra data to the payload - used in RealtimeBroadcastNotificationCreated.
                    // This works only if payload has medius and thread_id
                    $item->data = BroadcastNotification::fetchPayloadData($item->payload);

                    $event_class = '\\'.$item->event;
                    if (method_exists($event_class, "processPayload")) {
                        // If user is not allowed to access this event, payload will be set to empty array.
                        $item->payload = $event_class::processPayload($item->payload);

                        if (!$item->payload) {
                            $item->data = [];
                        }
                    }

                    $item->delay = $requested->diffInSeconds($created);
                    $item->requested_at = $requested->toDateTimeString();

                    return $item;
                });

                // Reflash session data - otherwise on reply flash alert is not displayed
                // https://stackoverflow.com/questions/37019294/laravel-ajax-call-deletes-session-flash-data
                \Session::reflash();

                $this->processConvView($request);

                \Eventy::action('polycast.receive', $request, $collection, $payload);

                return ['status' => 'success', 'time' => Carbon::now()->toDateTimeString(), 'payloads' => $payload];
            });
        });
    }

    /**
     * Process conversation viewers.
     */
    public function processConvView($request)
    {
        // Periodically save info indicating that user is still viewing the conversation.
        $viewing_conversation_id = null;
        if (!empty($request->data) && !empty($request->data['conversation_id'])) {
            $viewing_conversation_id = $request->data['conversation_id'];

            if (!empty($request->data['conversation_view_focus'])) {
                $conversation = Conversation::find($request->data['conversation_id']);
                if ($conversation) {
                    \Eventy::action('conversation.view.focus', $conversation);
                }
            }
        }
        if ($viewing_conversation_id) {

            $user = auth()->user();
            $now = Carbon::now();
            
            $cache_key = 'conv_view_'.$user->id.'_'.$viewing_conversation_id;
            $cache_data = \Cache::get($cache_key);
            $view_date = null;
            $replying_changed = false;

            // t - date.
            // r - replying.
            if ($cache_data) {
                if (isset($cache_data['t']) && isset($cache_data['r'])) {
                    $view_date = Carbon::createFromFormat('Y-m-d H:i:s', $cache_data['t']);

                    // Let other users know that user started to reply.
                    if (!(int)$cache_data['r'] && (int)$request->data['replying']) {
                        // Started to reply.
                        \App\Events\RealtimeConvView::dispatchSelf($viewing_conversation_id, $user, true);
                        $replying_changed = true;
                    } elseif ((int)$cache_data['r'] && !(int)$request->data['replying']) {
                        // Finished to reply.
                        \App\Events\RealtimeConvView::dispatchSelf($viewing_conversation_id, $user, false);
                        $replying_changed = true;
                    }
                } else {
                    $replying_changed = true;
                }
            }
            
            if (!$cache_data || $replying_changed || ($view_date && $now->diffInSeconds($view_date) > 15)) {
                // Remember date of the last view in the cache.
                // Store for 2 minutes.
                $cache_data = [
                    't' => $now->toDateTimeString(),
                    'r' => (int)$request->data['replying']
                ];
                \Cache::put($cache_key, $cache_data, 1);

                // Job could not detect when user finishes to view converrsation.
                // We are using cron.
                // \App\Jobs\CheckConvView::dispatch($viewing_conversation_id, $user->id)
                //     ->delay(now()->addSeconds(25))
                //     ->onQueue(\Helper::QUEUE_DEFAULT);

                $conv_key = 'conv_view';
                $conv_data = \Cache::get($conv_key) ?? [];
                $conv_data[$viewing_conversation_id][$user->id] = $cache_data;
                \Cache::put($conv_key, $conv_data, 20 /*minutes*/);
                
                // \DB::table('polycast_events')->insert([
                //     'channels'   => json_encode([['name' => 'conv.view']]),
                //     'event'      => 'App\Events\RealtimeConvView',
                //     'payload'    => json_encode([
                //         'conversation_id' => $viewing_conversation_id,
                //         'reiterating' => true
                //     ]),
                //     'created_at' => Carbon::now()->toDateTimeString(),
                // ]);
            }
        }
    }

    public function register()
    {
    }
}