<?php
declare(strict_types=1);

require_once dirname(__DIR__, 2).'/conexao.php';
$mysqli->set_charset('utf8mb4');
date_default_timezone_set('America/Sao_Paulo');

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: no-referrer');

function apiOut(int $status, array $data): never
{
    http_response_code($status);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function apiMethod(string $method): void
{
    if (strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) !== $method) {
        header('Allow: '.$method);
        apiOut(405, ['ok' => false, 'message' => 'Método não permitido.']);
    }
}

function apiRequireHttps(): void
{
    $host = strtolower((string)($_SERVER['HTTP_HOST'] ?? ''));
    $local = $host === '' || str_starts_with($host, 'localhost') || str_starts_with($host, '127.0.0.1');
    $https = !empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off';
    $forwarded = strtolower((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')) === 'https';
    if (!$local && !$https && !$forwarded) {
        apiOut(426, ['ok' => false, 'message' => 'HTTPS é obrigatório.']);
    }
}

function apiJson(int $maxBytes = 262144): array
{
    $length = (int)($_SERVER['CONTENT_LENGTH'] ?? 0);
    if ($length > $maxBytes) apiOut(413, ['ok' => false, 'message' => 'Payload excede o limite permitido.']);
    $contentType = strtolower(trim(explode(';', (string)($_SERVER['CONTENT_TYPE'] ?? ''))[0]));
    if ($contentType !== 'application/json') apiOut(415, ['ok' => false, 'message' => 'Use Content-Type application/json.']);
    $raw = file_get_contents('php://input', false, null, 0, $maxBytes + 1);
    if ($raw === false || $raw === '') apiOut(400, ['ok' => false, 'message' => 'Corpo JSON obrigatório.']);
    if (strlen($raw) > $maxBytes) apiOut(413, ['ok' => false, 'message' => 'Payload excede o limite permitido.']);
    try {$data = json_decode($raw, true, 64, JSON_THROW_ON_ERROR);} catch (JsonException) {
        apiOut(400, ['ok' => false, 'message' => 'JSON inválido.']);
    }
    if (!is_array($data) || array_is_list($data)) apiOut(400, ['ok' => false, 'message' => 'O JSON deve ser um objeto.']);
    return $data;
}

function apiText(mixed $value, int $max = 500): string
{
    if (is_array($value) || is_object($value)) return '';
    $value = trim((string)$value);
    return function_exists('mb_substr') ? mb_substr($value, 0, $max, 'UTF-8') : substr($value, 0, $max);
}

function apiInt(mixed $value, int $min, int $max, string $field): int
{
    if (!is_int($value) && !(is_string($value) && preg_match('/^-?\d+$/', $value))) {
        apiOut(422, ['ok' => false, 'message' => "Campo inválido: {$field}."]);
    }
    $number = (int)$value;
    if ($number < $min || $number > $max) apiOut(422, ['ok' => false, 'message' => "Campo fora do limite: {$field}."]);
    return $number;
}

function apiTableExists(mysqli $db, string $table): bool
{
    $safe = $db->real_escape_string($table);
    $result = $db->query("SHOW TABLES LIKE '{$safe}'");
    return $result instanceof mysqli_result && $result->num_rows > 0;
}

function apiCfg(mysqli $db): array
{
    $result = $db->query('SELECT * FROM ti_mobile_config WHERE id=1 LIMIT 1');
    $cfg = $result ? ($result->fetch_assoc() ?: []) : [];
    if (!$cfg || empty($cfg['api_enabled'])) apiOut(503, ['ok' => false, 'message' => 'API de inventário temporariamente desativada.']);
    return $cfg;
}

function apiIp(): string { return substr((string)($_SERVER['REMOTE_ADDR'] ?? ''), 0, 45); }

function apiAudit(mysqli $db, string $action, ?int $device = null, ?string $actor = null, ?string $details = null): void
{
    if (!apiTableExists($db, 'ti_mobile_auditoria')) return;
    $ip = apiIp();
    $stmt = $db->prepare('INSERT INTO ti_mobile_auditoria(dispositivo_id,acao,ator,ip_remoto,detalhes) VALUES(?,?,?,?,?)');
    $stmt->bind_param('issss', $device, $action, $actor, $ip, $details);
    $stmt->execute();
    $stmt->close();
}

function apiRateLimit(mysqli $db, string $scope, int $limit = 30, int $windowSeconds = 60): void
{
    if (!apiTableExists($db, 'ti_mobile_rate_limits')) return;
    $key = hash('sha256', $scope.'|'.apiIp());
    $stmt = $db->prepare('SELECT janela_inicio,tentativas FROM ti_mobile_rate_limits WHERE chave_hash=? LIMIT 1');
    $stmt->bind_param('s', $key);$stmt->execute();$row = $stmt->get_result()->fetch_assoc();$stmt->close();
    $expired = !$row || time() - (strtotime((string)$row['janela_inicio']) ?: 0) >= $windowSeconds;
    if ($expired) {
        $stmt = $db->prepare('INSERT INTO ti_mobile_rate_limits(chave_hash,janela_inicio,tentativas) VALUES(?,NOW(),1) ON DUPLICATE KEY UPDATE janela_inicio=NOW(),tentativas=1');
        $stmt->bind_param('s', $key);$stmt->execute();$stmt->close();
    } else {
        if ((int)$row['tentativas'] >= $limit) apiOut(429, ['ok' => false, 'message' => 'Muitas requisições. Aguarde e tente novamente.']);
        $stmt = $db->prepare('UPDATE ti_mobile_rate_limits SET tentativas=tentativas+1 WHERE chave_hash=?');
        $stmt->bind_param('s', $key);$stmt->execute();$stmt->close();
    }
    if (random_int(1, 100) === 1) $db->query('DELETE FROM ti_mobile_rate_limits WHERE janela_inicio<DATE_SUB(NOW(),INTERVAL 1 DAY)');
}

function apiBearer(): string
{
    $header = (string)($_SERVER['HTTP_AUTHORIZATION'] ?? '');
    if (preg_match('/^Bearer\s+([A-Fa-f0-9]{64})$/', $header, $match)) return $match[1];
    $fallback = apiText($_SERVER['HTTP_X_AGENT_TOKEN'] ?? '', 64);
    return preg_match('/^[A-Fa-f0-9]{64}$/', $fallback) ? $fallback : '';
}

function apiAuth(mysqli $db): array
{
    $agent = apiText($_SERVER['HTTP_X_AGENT_ID'] ?? '', 40);
    $token = apiBearer();
    $timestamp = (int)($_SERVER['HTTP_X_TIMESTAMP'] ?? 0);
    $nonce = strtolower(apiText($_SERVER['HTTP_X_NONCE'] ?? '', 64));
    apiRateLimit($db, 'auth:'.$agent, 90, 60);
    if (!preg_match('/^(TAB|MOB)-\d{4}-\d{7}$/', $agent) || !preg_match('/^[a-f0-9]{64}$/i', $token) || $timestamp <= 0 || !preg_match('/^[a-f0-9]{32,64}$/', $nonce)) {
        apiOut(401, ['ok' => false, 'message' => 'Credenciais do agente incompletas.']);
    }
    if (abs(time() - $timestamp) > 600) apiOut(401, ['ok' => false, 'message' => 'Relógio do dispositivo fora da janela permitida.']);
    $stmt = $db->prepare('SELECT * FROM ti_mobile_dispositivos WHERE agent_id=? LIMIT 1');
    $stmt->bind_param('s', $agent);$stmt->execute();$device = $stmt->get_result()->fetch_assoc();$stmt->close();
    if (!$device || !hash_equals((string)$device['token_hash'], hash('sha256', $token))) apiOut(401, ['ok' => false, 'message' => 'Agente não autorizado.']);
    if (($device['status'] ?? '') === 'REVOGADO') {
        apiEvent($db, (int)$device['id'], 'TENTATIVA_REVOGADO', 'CRITICO', 'Dispositivo revogado tentou sincronizar', null, null, 'A API bloqueou o token revogado.', 'API');
        apiAudit($db, 'TOKEN_REVOGADO_BLOQUEADO', (int)$device['id'], $agent);
        apiOut(403, ['ok' => false, 'message' => 'Agente revogado. Procure a Gerência de Tecnologia.']);
    }
    if (($device['status'] ?? '') !== 'ATIVO') apiOut(403, ['ok' => false, 'message' => 'Agente inativo.']);
    $deviceId = (int)$device['id'];
    $stmt = $db->prepare('INSERT INTO ti_mobile_nonces(dispositivo_id,nonce) VALUES(?,?)');
    $stmt->bind_param('is', $deviceId, $nonce);
    try {
        $stmt->execute();
    } catch (mysqli_sql_exception $error) {
        $duplicate = $error->getCode() === 1062;$stmt->close();
        apiOut($duplicate ? 409 : 500, ['ok' => false, 'message' => $duplicate ? 'Requisição repetida.' : 'Falha na validação da requisição.']);
    }
    $stmt->close();
    if (random_int(1, 100) === 1) $db->query('DELETE FROM ti_mobile_nonces WHERE criado_em<DATE_SUB(NOW(),INTERVAL 2 DAY)');
    return $device;
}

function apiEvent(mysqli $db, int $device, string $type, string $severity, string $title, ?string $old = null, ?string $new = null, ?string $detail = null, string $origin = 'AGENTE'): void
{
    $stmt = $db->prepare('INSERT INTO ti_mobile_eventos(dispositivo_id,tipo,severidade,titulo,valor_anterior,valor_novo,detalhes,origem) VALUES(?,?,?,?,?,?,?,?)');
    $stmt->bind_param('isssssss', $device, $type, $severity, $title, $old, $new, $detail, $origin);$stmt->execute();$stmt->close();
}

function apiLink(mysqli $db, ?string $patrimony, ?string $imei, ?string $serial): array
{
    $smartphone = null;$asset = null;
    if (apiTableExists($db, 'telecom_smartphones')) {
        $sql = 'SELECT id FROM telecom_smartphones WHERE 1=0';$types = '';$values = [];
        if ($patrimony) {$sql .= ' OR patrimonio=?';$types .= 's';$values[] = $patrimony;}
        if ($imei) {$sql .= ' OR imei1=? OR imei2=?';$types .= 'ss';$values[] = $imei;$values[] = $imei;}
        if ($serial) {$sql .= ' OR numero_serie=?';$types .= 's';$values[] = $serial;}
        $sql .= ' LIMIT 1';
        if ($types) {$stmt = $db->prepare($sql);$stmt->bind_param($types, ...$values);$stmt->execute();$row = $stmt->get_result()->fetch_assoc();$stmt->close();if ($row) $smartphone = (int)$row['id'];}
    }
    if (apiTableExists($db, 'ti_ativos')) {
        $compact = static fn(?string $value): string => preg_replace('/[^a-z0-9]+/i', '', strtolower((string)$value)) ?? '';
        $pat = $compact($patrimony);$ser = $compact($serial);$secondary = $compact($imei);
        if ($pat !== '' || $ser !== '' || $secondary !== '') {
            $stmt = $db->prepare("SELECT id FROM ti_ativos WHERE (?<>'' AND patrimonio_normalizado=?) OR (?<>'' AND serie_normalizada=?) OR (?<>'' AND identificador_secundario=?) LIMIT 1");
            $stmt->bind_param('ssssss', $pat, $pat, $ser, $ser, $secondary, $secondary);$stmt->execute();$row = $stmt->get_result()->fetch_assoc();$stmt->close();if ($row) $asset = (int)$row['id'];
        }
    }
    return ['smartphone_id' => $smartphone, 'asset_id' => $asset];
}

function apiTrackedApps(mysqli $db): array
{
    if (!apiTableExists($db, 'ti_mobile_apps_catalogo')) return [];
    $items = [];$result = $db->query('SELECT package_name,app_label,obrigatorio FROM ti_mobile_apps_catalogo WHERE ativo=1 ORDER BY app_label');
    while ($result && $row = $result->fetch_assoc()) $items[] = ['package_name' => $row['package_name'], 'label' => $row['app_label'], 'required' => (bool)$row['obrigatorio']];
    return $items;
}

apiRequireHttps();
?>
