<?php

namespace App\Console\Commands;

use App\Models\BastionAccount;
use Illuminate\Console\Command;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;

class CheckBastionLoginCommand extends Command
{
    protected $signature = 'bastion:check-login {--account_id=} {--refresh-invalid}';

    protected $description = '检测堡垒机账号 token 是否有效（0 有效，-1 无效）';

    public function handle(): int
    {
        $query = BastionAccount::query()
            ->where('is_active', true)
            ->whereNotNull('usm')
            ->where('usm', '!=', '')
            ->whereNotNull('usm_authentication')
            ->where('usm_authentication', '!=', '');

        $accountId = $this->option('account_id');
        if ($accountId !== null && $accountId !== '') {
            $query->where('id', (int) $accountId);
        }

        $accounts = $query->orderBy('id')->get();
        if ($accounts->isEmpty()) {
            $this->line('[]');

            return self::SUCCESS;
        }

        $rows = [];
        foreach ($accounts as $account) {
            $status = $this->checkAccountTokenStatus((string) $account->usm_authentication, (string) $account->usm);
            $refreshed = false;
            if ($status === -1 && $this->option('refresh-invalid')) {
                $refreshed = $this->refreshAccountToken($account);
                if ($refreshed) {
                    $account->refresh();
                    $status = $this->checkAccountTokenStatus((string) $account->usm_authentication, (string) $account->usm);
                }
            }

            $rows[] = [
                'id' => $account->id,
                'username' => $account->username,
                'status' => $status,
                'refreshed' => $refreshed,
            ];
        }

        $this->line(json_encode($rows, JSON_UNESCAPED_UNICODE));

        return self::SUCCESS;
    }

    private function checkAccountTokenStatus(string $usmAuthentication, string $usm): int
    {
        $url = 'https://172.16.254.2/index.php/extend/check_login';
        $cookie = sprintf(
            'LANG=zh; USM-AUTHENTICATION=%s; USM=%s; LOGON=1; AUTH_METHOD=oauth2',
            $usmAuthentication,
            $usm
        );

        try {
            $response = Http::timeout(10)
                ->withOptions(['verify' => false])
                ->withHeaders([
                    'Cookie' => $cookie,
                    'Accept' => '*/*',
                ])
                ->get($url);
        } catch (ConnectionException) {
            return -1;
        }

        if (! $response->successful()) {
            return -1;
        }

        return trim((string) $response->body()) === '0' ? 0 : -1;
    }

    private function refreshAccountToken(BastionAccount $account): bool
    {
        $baseUrl = (string) config('services.bastion_token.base_url');
        if ($baseUrl === '') {
            return false;
        }

        $submitEndpoint = (string) config('services.bastion_token.submit_endpoint', '/bastion_token');
        $statusEndpoint = (string) config('services.bastion_token.status_endpoint', '/bastion_token/{task_id}');
        $timeout = (int) config('services.bastion_token.timeout', 30);
        $service = (string) config('services.bastion_token.service', 'https://myapp.cdu.edu.cn/index.html');
        $verifySsl = (bool) config('services.bastion_token.verify_ssl', false);

        try {
            $submitResponse = Http::baseUrl($baseUrl)
                ->acceptJson()
                ->timeout($timeout)
                ->retry(2, 300, throw: false)
                ->post($submitEndpoint, [
                    'username' => $account->username,
                    'password' => $account->password,
                    'service' => $service,
                    'verify_ssl' => $verifySsl,
                ]);
        } catch (ConnectionException|RequestException) {
            return false;
        }

        if (! $submitResponse->successful()) {
            return false;
        }

        $taskId = (string) data_get($submitResponse->json(), 'task_id', '');
        if ($taskId === '') {
            return false;
        }

        $maxPoll = 20;
        for ($i = 0; $i < $maxPoll; $i++) {
            usleep(500000);
            $statusUrl = str_replace('{task_id}', $taskId, $statusEndpoint);
            try {
                $statusResponse = Http::baseUrl($baseUrl)
                    ->acceptJson()
                    ->timeout($timeout)
                    ->retry(2, 300, throw: false)
                    ->get($statusUrl);
            } catch (ConnectionException|RequestException) {
                continue;
            }

            if (! $statusResponse->successful()) {
                continue;
            }

            $taskResult = $statusResponse->json();
            $status = strtolower((string) data_get($taskResult, 'status', 'pending'));
            if ($status === 'pending') {
                continue;
            }

            if ($status === 'error') {
                return false;
            }

            $usmAuthentication = (string) data_get($taskResult, 'data.bastion.token_cookies.USM-AUTHENTICATION', '');
            $usm = (string) data_get($taskResult, 'data.bastion.token_cookies.USM', '');
            if ($usmAuthentication === '' || $usm === '') {
                $usmAuthentication = (string) data_get($taskResult, 'data.USM-AUTHENTICATION', $usmAuthentication);
                $usm = (string) data_get($taskResult, 'data.USM', $usm);
            }
            if ($usmAuthentication === '' || $usm === '') {
                return false;
            }

            $account->update([
                'usm_authentication' => $usmAuthentication,
                'usm' => $usm,
                'last_token_refreshed_at' => now(),
            ]);

            return true;
        }

        return false;
    }
}
