<?php
// ============================================================
// API DE LICENÇAS - MonitorPing
// ============================================================

require_once __DIR__ . '/config.php';

header('Content-Type: application/json');

function db(): PDO {
    static $pdo = null;
    if ($pdo === null) {
        $pdo = new PDO(
            'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4',
            DB_USER, DB_PASS,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
        );
    }
    return $pdo;
}

function log_action(string $license_key, string $action, ?string $hardware_id, string $message): void {
    try {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        db()->prepare("INSERT INTO license_logs (license_key, action, hardware_id, ip_address, message) VALUES (?,?,?,?,?)")
           ->execute([$license_key, $action, $hardware_id, $ip, $message]);
    } catch (Throwable $e) {}
}

function respond(array $data, int $code = 200): void {
    http_response_code($code);
    echo json_encode($data);
    exit;
}

// ── Rate limiting por IP (máx 30 req/min) ───────────────────
function api_rate_limit(): void {
    $ip   = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $file = sys_get_temp_dir() . '/mp_rl_' . md5($ip) . '.json';
    $data = ['count' => 0, 'start' => time()];
    if (file_exists($file)) {
        $data = json_decode(file_get_contents($file), true) ?? $data;
    }
    if ((time() - $data['start']) > 60) {
        $data = ['count' => 0, 'start' => time()];
    }
    $data['count']++;
    file_put_contents($file, json_encode($data), LOCK_EX);
    if ($data['count'] > 30) {
        http_response_code(429);
        header('Retry-After: 60');
        echo json_encode(['valid' => false, 'error' => 'rate_limited']);
        exit;
    }
}

api_rate_limit();

$body  = file_get_contents('php://input');
$input = json_decode($body, true) ?? [];
$action = $input['action'] ?? '';

// ── PROVISION — troca token de instalação por chave + segredo ──
// Não exige HMAC (o próprio token é a autenticação de uso único)
if ($action === 'provision') {
    $token = trim($input['install_token'] ?? '');
    if (!$token) {
        respond(['valid' => false, 'error' => 'missing_token'], 400);
    }

    $pdo  = db();
    $stmt = $pdo->prepare("
        SELECT l.*, c.name AS customer_name, c.active AS customer_active
        FROM licenses l
        JOIN customers c ON c.id = l.customer_id
        WHERE l.install_token = ?
    ");
    $stmt->execute([$token]);
    $lic = $stmt->fetch();

    if (!$lic) {
        respond(['valid' => false, 'error' => 'invalid_token'], 404);
    }
    if (!$lic['active'] || !$lic['customer_active']) {
        respond(['valid' => false, 'error' => 'license_inactive'], 403);
    }
    if ($lic['install_token_expires_at'] && new DateTime('now') > new DateTime($lic['install_token_expires_at'])) {
        respond(['valid' => false, 'error' => 'token_expired'], 403);
    }

    // Invalida o token imediatamente (uso único)
    $pdo->prepare("UPDATE licenses SET install_token = NULL, install_token_expires_at = NULL WHERE id = ?")
        ->execute([$lic['id']]);

    log_action($lic['license_key'], 'provision', null, 'Token de instalação resgatado');

    respond([
        'valid'              => true,
        'license_key'        => $lic['license_key'],
        'api_secret'         => $lic['api_secret'],
        'customer_name'      => $lic['customer_name'],
        'expires_at'         => $lic['expires_at'],
        'grace_days'         => GRACE_DAYS,
        'evolution_api_url'  => EVOLUTION_API_URL,
        'evolution_api_key'  => EVOLUTION_API_KEY,
    ]);
}

// ── Para validate e activate: HMAC com segredo da licença ────
$license_key = strtoupper(trim($input['license_key'] ?? ''));
$hardware_id = trim($input['hardware_id'] ?? '');

if (!preg_match('/^[A-Z2-9]{5}-[A-Z2-9]{5}-[A-Z2-9]{5}-[A-Z2-9]{5}$/', $license_key)) {
    respond(['valid' => false, 'error' => 'invalid_format'], 400);
}

// Busca o api_secret desta licença para validar o HMAC
$pdo        = db();
$sec_stmt   = $pdo->prepare("SELECT api_secret FROM licenses WHERE license_key = ?");
$sec_stmt->execute([$license_key]);
$sec_row    = $sec_stmt->fetch();
$lic_secret = $sec_row ? $sec_row['api_secret'] : '';

$received_sig = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected_sig = hash_hmac('sha256', $body, $lic_secret);

if (!$lic_secret || !hash_equals($expected_sig, $received_sig)) {
    respond(['valid' => false, 'error' => 'unauthorized'], 401);
}

if (!empty($hardware_id) && !preg_match('/^[a-f0-9]{64}$/', $hardware_id)) {
    respond(['valid' => false, 'error' => 'invalid_hardware_id'], 400);
}

// ── ATIVAR ──────────────────────────────────────────────────
if ($action === 'activate') {
    $stmt = $pdo->prepare("
        SELECT l.*, c.name AS customer_name, c.active AS customer_active
        FROM licenses l
        JOIN customers c ON c.id = l.customer_id
        WHERE l.license_key = ?
    ");
    $stmt->execute([$license_key]);
    $lic = $stmt->fetch();

    if (!$lic) {
        log_action($license_key, 'fail', $hardware_id, 'Chave não encontrada');
        respond(['valid' => false, 'error' => 'invalid_key'], 404);
    }
    if (!$lic['active'] || !$lic['customer_active']) {
        log_action($license_key, 'fail', $hardware_id, 'Licença/cliente inativo');
        respond(['valid' => false, 'error' => 'revoked'], 403);
    }
    if (!empty($lic['hardware_id']) && $lic['hardware_id'] !== $hardware_id) {
        log_action($license_key, 'fail', $hardware_id, 'hardware_id diverge');
        respond(['valid' => false, 'error' => 'hardware_mismatch'], 403);
    }

    $pdo->prepare("UPDATE licenses SET hardware_id = ?, activated_at = NOW(), last_check_at = NOW() WHERE license_key = ?")
        ->execute([$hardware_id, $license_key]);

    log_action($license_key, 'activate', $hardware_id, 'Ativação realizada');
    respond([
        'valid'         => true,
        'customer_name' => $lic['customer_name'],
        'expires_at'    => $lic['expires_at'],
        'grace_days'    => GRACE_DAYS,
    ]);
}

// ── VALIDAR ─────────────────────────────────────────────────
if ($action === 'validate') {
    $stmt = $pdo->prepare("
        SELECT l.*, c.name AS customer_name, c.active AS customer_active
        FROM licenses l
        JOIN customers c ON c.id = l.customer_id
        WHERE l.license_key = ?
    ");
    $stmt->execute([$license_key]);
    $lic = $stmt->fetch();

    if (!$lic) {
        log_action($license_key, 'fail', $hardware_id, 'Chave não encontrada');
        respond(['valid' => false, 'error' => 'invalid_key'], 404);
    }
    if (!$lic['active'] || !$lic['customer_active']) {
        log_action($license_key, 'revoke', $hardware_id, 'Licença/cliente inativo');
        respond(['valid' => false, 'error' => 'revoked'], 403);
    }
    if (!empty($lic['hardware_id']) && $lic['hardware_id'] !== $hardware_id) {
        log_action($license_key, 'fail', $hardware_id, 'hardware_id inválido');
        respond(['valid' => false, 'error' => 'hardware_mismatch'], 403);
    }

    $pdo->prepare("UPDATE licenses SET last_check_at = NOW() WHERE license_key = ?")
        ->execute([$license_key]);

    $expires   = new DateTime($lic['expires_at']);
    $today     = new DateTime('today');
    $grace_end = (clone $expires)->modify('+' . GRACE_DAYS . ' days');
    $expired   = $today > $expires;
    $in_grace  = $expired && $today <= $grace_end;
    $blocked   = $expired && $today > $grace_end;

    log_action($license_key, 'validate', $hardware_id, $blocked ? 'bloqueada' : ($in_grace ? 'carência' : 'ok'));

    respond([
        'valid'         => !$blocked,
        'expired'       => $expired,
        'in_grace'      => $in_grace,
        'blocked'       => $blocked,
        'customer_name' => $lic['customer_name'],
        'expires_at'    => $lic['expires_at'],
        'grace_days'    => GRACE_DAYS,
    ]);
}

respond(['valid' => false, 'error' => 'unknown_action'], 400);
