<?php
declare(strict_types=1);

namespace PluginNetconfig\Api;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class GlpiApiClient
{
    private string $baseUrl;
    private string $appToken;
    private ?string $sessionToken = null;
    private Client $httpClient;

    public function __construct(string $baseUrl, string $appToken, string $userToken = null)
    {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->appToken = $appToken;
        $this->httpClient = new Client([
            'base_uri' => $this->baseUrl . '/apirest.php/',
            'timeout'  => 30.0,
            'headers'  => [
                'App-Token' => $this->appToken,
                'Content-Type' => 'application/json',
            ],
        ]);

        if ($userToken) {
            $this->loginWithUserToken($userToken);
        }
    }

    /**
     * Login con credenziali utente (username + password)
     */
    public function login(string $username, string $password): bool
    {
        try {
            $response = $this->httpClient->post('initSession', [
                'auth' => [$username, $password],
            ]);
            $data = json_decode($response->getBody(), true);
            $this->sessionToken = $data['session_token'] ?? null;
            return $this->sessionToken !== null;
        } catch (RequestException $e) {
            error_log("GLPI API login failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Login con user token (più sicuro, generato in Profilo > API)
     */
    public function loginWithUserToken(string $userToken): bool
    {
        try {
            $response = $this->httpClient->post('initSession', [
                'headers' => ['User-Token' => $userToken],
            ]);
            $data = json_decode($response->getBody(), true);
            $this->sessionToken = $data['session_token'] ?? null;
            return $this->sessionToken !== null;
        } catch (RequestException $e) {
            error_log("GLPI API user-token login failed: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Logout e distruzione sessione
     */
    public function logout(): void
    {
        if ($this->sessionToken) {
            try {
                $this->httpClient->delete('killSession', [
                    'headers' => ['Session-Token' => $this->sessionToken],
                ]);
            } catch (\Throwable $e) {
                // Ignora errori in logout
            }
            $this->sessionToken = null;
        }
    }

    /**
     * Esegue una chiamata API con retry automatico su token scaduto
     */
    private function request(string $method, string $endpoint, array $data = []): ?array
    {
        $headers = $this->sessionToken ? ['Session-Token' => $this->sessionToken] : [];

        try {
            $response = $this->httpClient->request($method, $endpoint, [
                'headers' => $headers,
                'json'    => $data,
            ]);
            return json_decode($response->getBody(), true);
        } catch (RequestException $e) {
            // Se token scaduto (401), tenta refresh e riprova UNA volta
            if ($e->getResponse()?->getStatusCode() === 401 && $this->sessionToken) {
                $this->logout();
                // Qui potresti ricaricare le credenziali da config/vault e fare re-login
                // Per semplicità, lanciamo eccezione
                throw new \RuntimeException("GLPI API session expired. Re-authentication required.");
            }
            throw $e;
        }
    }

    /**
     * Ottieni dispositivo di rete per ID
     */
    public function getNetworkEquipment(int $id): ?array
    {
        $result = $this->request('GET', "NetworkEquipment/$id");
        return $result && !isset($result[0]['error']) ? $result : null;
    }

    /**
     * Cerca dispositivi per nome/IP (per mapping agent -> GLPI ID)
     */
    public function searchNetworkEquipment(string $nameOrIp): array
    {
        $query = [
            'criteria' => [
                ['field' => 1, 'searchtype' => 'contains', 'value' => $nameOrIp], // name
                ['OR' => [
                    ['field' => 2, 'searchtype' => 'contains', 'value' => $nameOrIp], // ip
                ]],
            ],
            'forcedisplay' => [1, 2, 3], // id, name, ip
        ];

        $result = $this->request('GET', 'search/NetworkEquipment', ['criteria' => $query]);
        return $result['data'] ?? [];
    }

    /**
     * Invia configurazione al plugin (endpoint personalizzato)
     */
    public function sendConfig(int $deviceId, string $config, string $agentToken): bool
    {
        $payload = [
            'device_id' => $deviceId,
            'config'    => $config,
            'token'     => $agentToken,
            'timestamp' => date('c'),
        ];

        $result = $this->request('POST', 'plugin/netconfig/agent', $payload);
        return ($result['status'] ?? '') === 'ok';
    }

    public function __destruct()
    {
        $this->logout();
    }
}
