<?php

namespace App\Console\Commands;

use App\Services\TripService;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class RedisSubscriber extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'redis:subscribe';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Subscribe to a Redis channel and handle messages';

    /**
     * TripService instance.
     *
     * @var TripService
     */
    private $tripService;

    /**
     * Constructor.
     *
     * @param TripService $tripService
     */
    public function __construct(TripService $tripService)
    {
        parent::__construct();
        $this->tripService = $tripService;
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $this->info('Listening to Redis channel...');

        Redis::subscribe(['mqtt:scooter'], function ($message) {
            try {
                $message = json_decode($message, true);
                if (!isset($message['identifier'])) {
                    throw new \Exception('Invalid message format');
                }

                [$scooter, $scooter_inside_area] = $this->tripService->scooterInsideArea($message['identifier']);

                if ($scooter) {
                    $trip = $scooter->trip;
                    $user = $trip->user;

                    if (!$scooter_inside_area) {
                        $this->handleOutsideArea($scooter, $trip, $user);
                    } else {
                        $trip?->update(['outside_area_at' => null]);
                    }
                }

                Log::info('Processed message from Redis', ['identifier' => $message['identifier']]);
            } catch (Exception $e) {
                Log::error('Error processing Redis message', ['error' => $e->getMessage()]);
            }
        });

        return 0;
    }

    /**
     * Handle logic when the scooter is outside the area.
     *
     * @param object $scooter
     * @param object $trip
     * @param object|null $user
     * @return void
     */
    private function handleOutsideArea($scooter, $trip, $user)
    {
        if ($trip->outside_area_at) {
            $outside_area_time = now()->diffInMinutes($trip->outside_area_at);

            if ($outside_area_time >= 3) {
                $this->forceStopTrip($trip, $user, $scooter);
            } else {
                $this->sendWarning($trip, $user, $scooter);
            }
        } else {
            $trip->update(['outside_area_at' => now()]);
            $this->sendWarning($trip, $user, $scooter);
        }
    }

    /**
     * Force stop the trip.
     *
     * @param object $trip
     * @param object|null $user
     * @param object $scooter
     * @return void
     */
    private function forceStopTrip($trip, $user, $scooter)
    {
        $user?->notify(new \App\Notifications\Api\App\AutoStopOutsideAreaNotification($trip->id, ['database', 'fcm']));
        \App\Services\TripService::end($trip, 'auto_stop');
        \App\Services\MqttService::lock($scooter->serial_number);
    }

    /**
     * Send a warning notification and trigger an alarm.
     *
     * @param object $trip
     * @param object|null $user
     * @param object $scooter
     * @return void
     */
    private function sendWarning($trip, $user, $scooter)
    {
        $user?->notify(new \App\Notifications\Api\App\WarningTripNotification($trip->id, ['database', 'fcm']));
        \App\Services\MqttService::alarmOn($scooter->serial_number);
    }
}
