<?php
declare(strict_types=1);
$root=dirname(__DIR__);$database='tmp_windows_api_'.date('Ymd_His').'_'.bin2hex(random_bytes(3));$passwordFile=tempnam(sys_get_temp_dir(),'windows-db-pass-');file_put_contents($passwordFile,'');$db=new mysqli('127.0.0.1','root','');$db->set_charset('utf8mb4');$server=null;$pipes=[];
function windowsBatch(mysqli $db,string $sql):void{if(!$db->multi_query($sql))throw new RuntimeException($db->error);do{if($r=$db->store_result())$r->free();if(!$db->more_results())break;}while($db->next_result());if($db->errno)throw new RuntimeException($db->error);}
function windowsRequest(string $url,array $body,array $headers=[]):array{$headers=array_merge(['Content-Type: application/json','Accept: application/json'],$headers);$context=stream_context_create(['http'=>['method'=>'POST','header'=>implode("\r\n",$headers),'content'=>json_encode($body,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),'ignore_errors'=>true,'timeout'=>10]]);$raw=file_get_contents($url,false,$context);$status=0;foreach($http_response_header??[] as $line)if(preg_match('/^HTTP\/\S+\s+(\d+)/',$line,$m)){$status=(int)$m[1];break;}$json=json_decode((string)$raw,true);return [$status,is_array($json)?$json:[]];}
function windowsHeaders(string $agent,string $token,string $nonce):array{return ['Authorization: Bearer '.$token,'X-Agent-Token: '.$token,'X-Agent-Id: '.$agent,'X-Timestamp: '.time(),'X-Nonce: '.hash('sha256',$nonce)];}
function windowsStatus(int $actual,int $expected,string $case):void{if($actual!==$expected)throw new RuntimeException("{$case}: HTTP {$actual}, esperado {$expected}");echo "OK {$case}: HTTP {$actual}\n";}
try{
 $db->query("CREATE DATABASE `{$database}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");$db->select_db($database);windowsBatch($db,(string)file_get_contents($root.'/SQL_CONTROLE_COMPUTADORES.sql'));windowsBatch($db,(string)file_get_contents($root.'/SQL_INVENTARIO_WINDOWS.sql'));
 $code='C52A-81BC-629D';$hash=hash('sha256',$code);$stmt=$db->prepare('UPDATE ti_pc_config SET enrollment_hash=?,enrollment_expires_at=DATE_ADD(NOW(),INTERVAL 1 HOUR),enrollment_uses_remaining=2 WHERE id=1');$stmt->bind_param('s',$hash);$stmt->execute();$stmt->close();
 $env=array_merge(getenv(),['MANUTENCAO_DB_HOST'=>'127.0.0.1','MANUTENCAO_DB_USER'=>'root','MANUTENCAO_DB_PASS_FILE'=>$passwordFile,'MANUTENCAO_DB_NAME'=>$database]);$log=$root.'/tests/windows-api-server.log';file_put_contents($log,'');$server=proc_open([PHP_BINARY,'-S','127.0.0.1:8099','-t',$root],[0=>['file','NUL','r'],1=>['file',$log,'a'],2=>['file',$log,'a']],$pipes,$root,$env,['bypass_shell'=>true]);if(!is_resource($server))throw new RuntimeException('Servidor de teste nao iniciou.');$ready=false;for($i=0;$i<50;$i++){$socket=@fsockopen('127.0.0.1',8099,$eno,$estr,.2);if(is_resource($socket)){fclose($socket);$ready=true;break;}usleep(100000);}if(!$ready)throw new RuntimeException('Servidor recusou conexao: '.file_get_contents($log));$base='http://127.0.0.1:8099/api/computadores/';
 $register=['activation_code'=>$code,'fingerprint_hash'=>hash('sha256','pc-a'),'machine_guid_hash'=>hash('sha256','guid-a'),'tipo'=>'NOTEBOOK','patrimonio'=>'PC-00981','responsavel_nome'=>'Carlos Eduardo','setor'=>'Gerencia de Tecnologia','hostname'=>'EXT-NB-00981','numero_serie'=>'SERIE-PC-1','fabricante'=>'Dell','modelo'=>'Latitude 5440'];$invalid=$register;$invalid['activation_code']='0000-0000-0000';[$status]=windowsRequest($base.'registrar.php',$invalid);windowsStatus($status,403,'codigo invalido');
 [$status,$first]=windowsRequest($base.'registrar.php',$register);windowsStatus($status,200,'primeira ativacao');$agent=(string)($first['agent_id']??'');$oldToken=(string)($first['token']??'');if($agent===''||strlen($oldToken)!==64)throw new RuntimeException('Credenciais nao retornadas.');
 [$status,$second]=windowsRequest($base.'registrar.php',$register);windowsStatus($status,200,'reprovisionamento controlado');$token=(string)($second['token']??'');if(($second['agent_id']??'')!==$agent||$token===$oldToken)throw new RuntimeException('Agent ID/token nao foram preservado/rotacionado.');[$status]=windowsRequest($base.'registrar.php',$register);windowsStatus($status,403,'codigo esgotado');
 $inventory=['tipo'=>'NOTEBOOK','hostname'=>'EXT-NB-00981','dominio'=>'EXTREMA','ultimo_usuario'=>'EXTREMA\\carlos','fabricante'=>'Dell','modelo'=>'Latitude 5440','numero_serie'=>'SERIE-PC-1','bios_version'=>'1.2.3','sistema_operacional'=>'Microsoft Windows 11 Pro','os_version'=>'10.0.26100','os_build'=>'26100','arquitetura'=>'64 bits','instalado_em'=>'2026-01-05 10:00:00','ultimo_boot'=>'2026-08-10 08:00:00','uptime_seconds'=>20000,'processador'=>'Intel Core i5','cpu_cores'=>10,'cpu_logical'=>12,'ram_total_mb'=>16384,'ram_livre_mb'=>8192,'armazenamento_total_mb'=>488386,'armazenamento_livre_mb'=>20000,'armazenamento_tipo'=>'NVMe','gpu'=>'Intel Iris Xe','ipv4'=>'192.168.10.81','mac_address'=>'AA:BB:CC:DD:EE:FF','rede_tipo'=>'Ethernet','bitlocker_status'=>'PROTEGIDO','antivirus'=>'Microsoft Defender','agent_version'=>'1.0.0','agent_version_code'=>1,'coletado_em'=>date('Y-m-d H:i:s')];
 [$status]=windowsRequest($base.'sincronizar.php',$inventory,windowsHeaders($agent,str_repeat('0',64),'invalid'));windowsStatus($status,401,'token invalido');$headers=windowsHeaders($agent,$token,'sync-1');[$status]=windowsRequest($base.'sincronizar.php',$inventory,$headers);windowsStatus($status,200,'sincronizacao');[$status]=windowsRequest($base.'sincronizar.php',$inventory,$headers);windowsStatus($status,409,'replay bloqueado');
 $inventory['os_build']='26200';$inventory['armazenamento_livre_mb']=1000;[$status]=windowsRequest($base.'sincronizar.php',$inventory,windowsHeaders($agent,$token,'sync-2'));windowsStatus($status,200,'mudanca e disco critico');$events=(int)$db->query("SELECT COUNT(*) c FROM ti_pc_eventos WHERE tipo IN ('MUDANCA_OS_BUILD','ARMAZENAMENTO_CRITICO')")->fetch_assoc()['c'];if($events<2)throw new RuntimeException('Eventos automaticos ausentes.');$computer=$db->query("SELECT nome_maquina,sistema_operacional FROM ti_computadores WHERE patrimonio='PC-00981'")->fetch_assoc();if(!$computer||$computer['nome_maquina']!=='EXT-NB-00981')throw new RuntimeException('Cadastro patrimonial nao foi vinculado/atualizado.');echo "OK vinculo patrimonial e eventos: {$events}\n";
 $stmt=$db->prepare("UPDATE ti_pc_agentes SET status='REVOGADO' WHERE agent_id=?");$stmt->bind_param('s',$agent);$stmt->execute();$stmt->close();[$status]=windowsRequest($base.'heartbeat.php',['agent_version'=>'1.0.0','agent_version_code'=>1],windowsHeaders($agent,$token,'revoked'));windowsStatus($status,403,'agente revogado');$stmt=$db->prepare("UPDATE ti_pc_agentes SET status='ATIVO',ultima_comunicacao=DATE_SUB(NOW(),INTERVAL 10 DAY) WHERE agent_id=?");$stmt->bind_param('s',$agent);$stmt->execute();$stmt->close();[$status]=windowsRequest($base.'heartbeat.php',['agent_version'=>'1.0.0','agent_version_code'=>1],windowsHeaders($agent,$token,'online'));windowsStatus($status,200,'retorno apos offline');echo "WINDOWS API TEST OK\nAgent ID {$agent}; seguranca, inventario e vinculo validados.\n";
}finally{if(is_resource($server)){proc_terminate($server);proc_close($server);}$db->select_db('mysql');if(str_starts_with($database,'tmp_windows_api_'))$db->query("DROP DATABASE IF EXISTS `{$database}`");$db->close();if(is_string($passwordFile)&&str_starts_with(basename($passwordFile),'windows-db-pass-'))@unlink($passwordFile);}
