<?php

namespace App\Console\Commands;

use App\Classes\MikrotikService\Mikrotik;
use App\Models\Nas;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;

class SyncMikrotikToRedis extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'mikrotik:sync-to-redis {--interval=30 : Sync interval in seconds}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Continuously fetch and store Mikrotik data in Redis';

    /**
     * Maximum retries for connection/operation failures
     */
    private $maxRetries = 3;

    /**
     * Delay between retries in seconds
     */
    private $retryDelay = 5;

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $interval = (int) $this->option('interval');
        $this->info("Starting Mikrotik to Redis sync with {$interval} second interval...");

        // Continuous sync loop
        while (true) {
            try {
                $this->syncAllMikrotiks();
                sleep($interval);
            } catch (\Throwable $e) {
                $this->error("Error in sync loop: " . $e->getMessage());
                Log::error("Mikrotik sync error", [
                    'error' => $e->getMessage(),
                    'trace' => $e->getTraceAsString()
                ]);
                sleep($interval);
            }
        }
    }

    /**
     * Sync all Mikrotik devices
     */
    /**
     * Reset database connections for child process
     */
    private function resetConnections()
    {
        try {
            // Reset Redis connection
            Redis::connection()->disconnect();
            Redis::purge();

            // Reset database connection
            DB::disconnect();
            DB::purge();
            DB::reconnect();
        } catch (\Throwable $e) {
            // Ignore connection reset errors
        }
    }

    private function syncAllMikrotiks()
    {
        $allNas = Nas::all();

        if ($allNas->isEmpty()) {
            $this->warn("No NAS devices found");
            return;
        }

        $this->info("Syncing " . $allNas->count() . " Mikrotik devices in parallel...");

        $childPids = [];

        // Fork a process for each NAS
        foreach ($allNas as $nas) {
            $pid = pcntl_fork();

            if ($pid == -1) {
                // Fork failed
                $this->error("Could not create process for {$nas->shortname}");
                continue;
            } elseif ($pid === 0) {
                // Child process
                try {
                    $this->resetConnections();
                    $this->syncSingleMikrotik($nas);
                } catch (\Throwable $e) {
                    $this->error("Error syncing {$nas->shortname}: " . $e->getMessage());
                }
                exit(0);
            } else {
                // Parent process - track child PID
                $childPids[] = $pid;
            }
        }

        // Wait for all child processes to complete
        foreach ($childPids as $pid) {
            pcntl_waitpid($pid, $status);
        }

        $this->info("Sync cycle completed");
    }

    /**
     * Sync a single Mikrotik device with retries
     */
    private function syncSingleMikrotik(Nas $nas)
    {
        $retries = 0;

        while ($retries < $this->maxRetries) {
            try {
                // Connect to Mikrotik
                $mk = new Mikrotik(
                    $nas->nasname,
                    $nas->mikrotick_user,
                    $nas->mikrotick_user_password,
                    $nas->mikrotick_port ? (int)$nas->mikrotick_port : 8728
                );

                $start = microtime(true);

                // Fetch data
                $activeUsers = $mk->getActiveConnection();
                $allUsers = $mk->getSecret();

                // Store active connections in Redis
                $activeCount = 0;
                foreach ($activeUsers as $user) {
                    if (!isset($user['name'])) continue;

                    $key = "active:" . $user['name'];
                    Redis::setex($key, 300, json_encode($user));
                    $activeCount++;
                }

                // Store secrets in Redis
                $secretCount = 0;
                foreach ($allUsers as $user) {
                    if (!isset($user['name'])) continue;

                    $key = "secret:" . $user['name'];
                    Redis::setex($key, 120, json_encode($user));
                    $secretCount++;
                }

                $duration = round((microtime(true) - $start) * 1000, 2);
                $this->info("Synced {$nas->shortname}: {$activeCount} active, {$secretCount} secrets in {$duration}ms");

                return; // Success - exit retry loop
            } catch (\Throwable $e) {
                $retries++;

                if ($retries < $this->maxRetries) {
                    $this->warn("Attempt {$retries} failed for {$nas->shortname}, retrying in {$this->retryDelay} seconds...");
                    sleep($this->retryDelay);
                } else {
                    throw new Exception("Failed to sync {$nas->shortname} after {$this->maxRetries} attempts: " . $e->getMessage());
                }
            }
        }
    }
}
