<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Order;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use App\Models\User;
use App\Helpers\BalanceHelper;
use App\Services\TelegramLogService;
use App\Helpers\WebhookHelper;
class SmscodePollingWorker extends Command
{
    protected $signature = 'smscode:polling';
    protected $description = 'Realtime OTP polling SMSCode (production safe)';

    public function handle()
    {
        // 🔒 cegah multi worker
        $lock = cache()->lock('smscode_polling_lock', 300);

        if (!$lock->get()) {
    $this->warn('Worker already running...');
    sleep(15);
    return 0;
}

        try {

            $this->info("SMSCode polling worker started...");
            $start = time();

            while (true) {

                // ⛔ restart tiap 5 menit (biar fresh)
                if (time() - $start > 300) {
                    $this->info("Restarting worker...");
                    return;
                }

                try {

                    $orders = Order::where('provider', 'smscode')
    ->where('status', 'PENDING')
    ->whereNull('otp')
    ->where('created_at', '>=', now()->subMinutes(20))
    ->where(function ($q) {
        $q->whereNull('last_checked_at')
          ->orWhere('last_checked_at', '<=', now()->subSeconds(15));
    })
    ->orderByDesc('created_at')
    ->limit(100)
    ->get();

// Log::info('SMSCODE POLLING ORDERS', [
//     'count' => $orders->count(),
//     'orders' => $orders->pluck('order_id')->toArray(),
// ]);

                    foreach ($orders as $order) {

                        // 🔥 jitter biar ga spike CPU
                        usleep(rand(200000, 500000));

                        $key = "order_polling_{$order->id}";
                        if (cache()->has($key)) continue;

                        cache()->put($key, true, 30);

                        try {

                            // 🔒 LOCK ORDER
                            $locked = DB::transaction(function () use ($order) {

                                $o = Order::lockForUpdate()->find($order->id);

                                if (!$o || $o->status !== "PENDING" || $o->otp) {
                                    return null;
                                }

                                if ($o->last_checked_at && $o->last_checked_at > now()->subSeconds(15)) {
                                    return null;
                                }

                                $o->update([
                                    'last_checked_at' => now()
                                ]);

                                return $o;
                            });

                            if (!$locked) continue;

                            // 🌐 API CALL
                            $res = Http::withToken(env('SMSCODE_API_KEY'))
                                ->timeout(5)
                                ->get("https://api.smscode.gg/v1/orders/{$locked->order_id}");

                            if ($res->status() === 429) {
                                $retry = (int) $res->header('Retry-After', 3);
                                sleep($retry);
                                continue;
                            }

                           if (!$res->ok()) {

    Log::error('SMSCODE API ERROR', [
        'provider_order_id' => $locked->order_id,
        'http_status' => $res->status(),
        'body' => $res->body(),
    ]);

    continue;
}

$data = $res->json();

// Log::info('SMSCODE POLLING RESPONSE', [
//     'local_order_id'    => $locked->id,
//     'provider_order_id' => $locked->order_id,
//     'http_status'       => $res->status(),
//     'response'          => $data,
// ]);

$otp    = $data['data']['otp_code'] ?? null;
$status = $data['data']['status'] ?? null;

                            // 🔒 UPDATE RESULT
$updatedOrder = DB::transaction(function () use ($locked, $otp, $status) {

                                $o = Order::lockForUpdate()->find($locked->id);

                                if (!$o || $o->status !== "PENDING" || $o->refunded_at) return;

                                // ✅ OTP masuk
if ($otp) {

    $o->update([
        'status'       => 'SUCCESS',
        'otp'          => $otp,
        'completed_at' => now()
    ]);

    Log::info("OTP FOUND order #{$o->id}");

    return $o; // <-- penting
}

                                // ❌ expired / cancel → refund
                                if (in_array($status, ['EXPIRED', 'CANCELED'])) {

                                    $o->update([
                                        'status' => $status === 'EXPIRED' ? 'FAILED' : 'CANCELED',
                                        'refunded_at' => now()
                                    ]);

                                    $user = User::find($o->user_id);

                                    BalanceHelper::addLog(
                                        $user,
                                        'CREDIT',
                                        $o->harga,
                                        'Refund polling smscode',
                                        $o->id
                                    );

                                    Log::warning("Refund order #{$o->id}");
                                }

                                // optional counter
                                if (isset($o->polling_attempt)) {
                                    $o->increment('polling_attempt');
                                }
                            });


                            if ($updatedOrder instanceof Order) {

    try {
        $updatedOrder->load('user');

        WebhookHelper::send($updatedOrder->user, [
            'event' => 'otp.received',
            'data' => [
                'order_id' => $updatedOrder->id,
                'number'   => $updatedOrder->number,
                'otp_code' => $updatedOrder->otp,
                'status'   => $updatedOrder->status,
            ]
        ]);
    } catch (\Throwable $e) {
        Log::error('Webhook gagal: '.$e->getMessage());
    }

    try {
        TelegramLogService::orderSuccess($updatedOrder);
    } catch (\Throwable $e) {
        Log::error('Telegram gagal: '.$e->getMessage());
    }
}

                        } catch (\Throwable $e) {
                            Log::error("Polling error order #{$order->id}: " . $e->getMessage());
                        }
                    }

                    // 🔥 adaptive delay
                    sleep($orders->count() > 0 ? 5 : 8);

                } catch (\Throwable $e) {
                    Log::critical("Worker crash: " . $e->getMessage());
                    sleep(5);
                }
            }

        } finally {
            optional($lock)->release();
        }
    }
}