<?php

namespace App\Jobs;

use App\Models\Customer;
use App\Models\ActivityLog;
use App\Models\Mikrotik;
use App\Models\MikrotikSecret;
use App\Services\MikrotikApiService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

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

    public int $tries = 3;

    public array $backoff = [60, 300, 900];

    public int $timeout = 120;

    public function __construct(public int $mikrotikId, public array $customerIds, public bool $retryWhenDisconnected = false)
    {
        if ($retryWhenDisconnected) {
            $this->tries = 20;
        }
        $this->onQueue('mikrotik');
    }

    public function handle(MikrotikApiService $api): void
    {
        $router = Mikrotik::where('status', true)->find($this->mikrotikId);
        if (! $router || ! $router->connected) {
            foreach ($this->customerIds as $customerId) {
                $this->logSync((int) $customerId, 'customer_mikrotik_sync_pending', 'MikroTik is disconnected; software data was saved and router sync was skipped.');
            }
            if ($router && $this->retryWhenDisconnected) {
                $this->release(300);
            }
            return;
        }

        $customers = Customer::with('package')
            ->where('mikrotik_id', $router->id)
            ->whereIn('id', $this->customerIds)
            ->get();

        foreach ($customers as $customer) {
            $profile = trim((string) $customer->package?->mikrotik_profile_name);
            $username = trim((string) ($customer->ip_address ?: $customer->customer_code));
            if ($username === '' || $profile === '' || blank($customer->queue_password)) {
                Log::notice('Customer skipped for MikroTik sync because username, package profile, or PPPoE password is missing.', ['customer_id' => $customer->id]);
                $this->logSync($customer->id, 'customer_mikrotik_sync_failed', 'MikroTik sync skipped: PPPoE username, password, or package profile is missing.');

                continue;
            }

            $payload = [
                'username' => $username,
                'password' => $customer->queue_password,
                'profile' => $profile,
                'remote_address' => filter_var($customer->ip_address, FILTER_VALIDATE_IP) ? $customer->ip_address : null,
                'disabled' => ! in_array((int) $customer->status, [1, 3], true),
                'comment' => trim((string) $customer->remark),
            ];

            try {
                $localSecret = MikrotikSecret::where('mikrotik_id', $router->id)
                    ->where(function ($query) use ($customer, $username): void {
                        $query->where('customer_id', $customer->id)->orWhere('username', $username);
                    })->first();

                if ($localSecret) {
                    if (! $api->updateSecret($router, $username, $payload)) {
                        $api->addSecret($router, $payload);
                    }
                } else {
                    $api->addSecret($router, $payload);
                }

                MikrotikSecret::updateOrCreate(
                    ['mikrotik_id' => $router->id, 'username' => $username],
                    [
                        'customer_id' => $customer->id,
                        'password' => $customer->queue_password,
                        'profile' => $profile,
                        'remote_address' => $payload['remote_address'],
                        'status' => ! $payload['disabled'],
                        'comment' => $payload['comment'] ?: null,
                    ]
                );
                $this->logSync($customer->id, 'customer_mikrotik_sync_success', 'Customer PPPoE secret synchronized with MikroTik.');
            } catch (\Throwable $exception) {
                Log::warning('Customer MikroTik sync failed.', ['router_id' => $router->id, 'customer_id' => $customer->id, 'message' => $exception->getMessage()]);
                $this->logSync($customer->id, 'customer_mikrotik_sync_failed', 'MikroTik sync failed: '.$exception->getMessage());
                throw $exception;
            }
        }

        SyncMikrotikRouterDataJob::dispatch($router->id);
    }

    private function logSync(int $customerId, string $action, string $description): void
    {
        ActivityLog::create([
            'action' => $action,
            'resource_type' => Customer::class,
            'resource_id' => $customerId,
            'description' => $description,
            'metadata' => ['mikrotik_id' => $this->mikrotikId],
        ]);
    }
}
