<?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 pcApiOut(int $status, array $data): never
{
    http_response_code($status);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function pcApiMethod(string $method): void
{
    if (strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) !== $method) {
        header('Allow: '.$method);
        pcApiOut(405, ['ok'=>false,'message'=>'Metodo nao permitido.']);
    }
}

function pcApiHttps(): 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)pcApiOut(426,['ok'=>false,'message'=>'HTTPS e obrigatorio.']);
}

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

function pcApiText(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 pcApiInt(mixed $value,int $min,int $max,string $field): int
{
    if(!is_int($value)&&!(is_string($value)&&preg_match('/^-?\d+$/',$value)))pcApiOut(422,['ok'=>false,'message'=>'Campo invalido: '.$field.'.']);
    $number=(int)$value;
    if($number<$min||$number>$max)pcApiOut(422,['ok'=>false,'message'=>'Campo fora do limite: '.$field.'.']);
    return $number;
}

function pcApiBind(mysqli_stmt $stmt,string $types,array &$values): void
{
    $refs=[];foreach($values as $key=>&$value)$refs[$key]=&$value;
    $stmt->bind_param($types,...$refs);
}

function pcApiTable(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 pcApiCfg(mysqli $db): array
{
    if(!pcApiTable($db,'ti_pc_config'))pcApiOut(503,['ok'=>false,'message'=>'Inventario Windows ainda nao foi instalado.']);
    $result=$db->query('SELECT * FROM ti_pc_config WHERE id=1 LIMIT 1');$cfg=$result?($result->fetch_assoc()?:[]):[];
    if(!$cfg||empty($cfg['api_enabled']))pcApiOut(503,['ok'=>false,'message'=>'API Windows temporariamente desativada.']);
    return $cfg;
}

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

function pcApiAudit(mysqli $db,string $action,?int $agent=null,?string $actor=null,?string $details=null): void
{
    if(!pcApiTable($db,'ti_pc_auditoria'))return;$ip=pcApiIp();
    $stmt=$db->prepare('INSERT INTO ti_pc_auditoria(agente_id,acao,ator,ip_remoto,detalhes) VALUES(?,?,?,?,?)');
    $stmt->bind_param('issss',$agent,$action,$actor,$ip,$details);$stmt->execute();$stmt->close();
}

function pcApiRate(mysqli $db,string $scope,int $limit=30,int $window=60): void
{
    if(!pcApiTable($db,'ti_pc_rate_limits'))return;$key=hash('sha256',$scope.'|'.pcApiIp());
    $stmt=$db->prepare('SELECT janela_inicio,tentativas FROM ti_pc_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)>=$window;
    if($expired){$stmt=$db->prepare('INSERT INTO ti_pc_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)pcApiOut(429,['ok'=>false,'message'=>'Muitas requisicoes. Aguarde e tente novamente.']);$stmt=$db->prepare('UPDATE ti_pc_rate_limits SET tentativas=tentativas+1 WHERE chave_hash=?');$stmt->bind_param('s',$key);$stmt->execute();$stmt->close();}
}

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

function pcApiEvent(mysqli $db,int $agent,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_pc_eventos(agente_id,tipo,severidade,titulo,valor_anterior,valor_novo,detalhes,origem) VALUES(?,?,?,?,?,?,?,?)');
    $stmt->bind_param('isssssss',$agent,$type,$severity,$title,$old,$new,$detail,$origin);$stmt->execute();$stmt->close();
}

function pcApiAuth(mysqli $db): array
{
    $agent=pcApiText($_SERVER['HTTP_X_AGENT_ID']??'',32);$token=pcApiBearer();$timestamp=(int)($_SERVER['HTTP_X_TIMESTAMP']??0);$nonce=strtolower(pcApiText($_SERVER['HTTP_X_NONCE']??'',64));
    pcApiRate($db,'auth:'.$agent,90,60);
    if(!preg_match('/^PC-\d{4}-\d{7}$/',$agent)||!preg_match('/^[a-f0-9]{64}$/i',$token)||$timestamp<=0||!preg_match('/^[a-f0-9]{32,64}$/',$nonce))pcApiOut(401,['ok'=>false,'message'=>'Credenciais incompletas.']);
    if(abs(time()-$timestamp)>600)pcApiOut(401,['ok'=>false,'message'=>'Relogio fora da janela permitida.']);
    $stmt=$db->prepare('SELECT * FROM ti_pc_agentes 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)))pcApiOut(401,['ok'=>false,'message'=>'Agente nao autorizado.']);
    if(($device['status']??'')==='REVOGADO'){pcApiEvent($db,(int)$device['id'],'TENTATIVA_REVOGADO','CRITICO','Computador revogado tentou sincronizar',null,null,'Token bloqueado pela API.','API');pcApiAudit($db,'TOKEN_REVOGADO_BLOQUEADO',(int)$device['id'],$agent);pcApiOut(403,['ok'=>false,'message'=>'Agente revogado. Procure a Gerencia de Tecnologia.']);}
    if(($device['status']??'')!=='ATIVO')pcApiOut(403,['ok'=>false,'message'=>'Agente inativo.']);
    $id=(int)$device['id'];$stmt=$db->prepare('INSERT INTO ti_pc_nonces(agente_id,nonce) VALUES(?,?)');$stmt->bind_param('is',$id,$nonce);
    try{$stmt->execute();}catch(mysqli_sql_exception $error){$duplicate=$error->getCode()===1062;$stmt->close();pcApiOut($duplicate?409:500,['ok'=>false,'message'=>$duplicate?'Requisicao repetida.':'Falha na validacao da requisicao.']);}$stmt->close();
    return $device;
}

function pcApiFindComputer(mysqli $db,?string $patrimony,?string $serial,?string $hostname): ?int
{
    if(!pcApiTable($db,'ti_computadores'))return null;$sql='SELECT id FROM ti_computadores WHERE 1=0';$types='';$values=[];
    if($patrimony){$sql.=' OR patrimonio=?';$types.='s';$values[]=$patrimony;}
    if($serial){$sql.=' OR numero_serie=?';$types.='s';$values[]=$serial;}
    if($hostname){$sql.=' OR nome_maquina=?';$types.='s';$values[]=$hostname;}
    if($types==='')return null;$sql.=' ORDER BY id LIMIT 1';$stmt=$db->prepare($sql);$stmt->bind_param($types,...$values);$stmt->execute();$row=$stmt->get_result()->fetch_assoc();$stmt->close();return $row?(int)$row['id']:null;
}

pcApiHttps();
