<?php
// chat_api/upload.php
declare(strict_types=1);

@header('Content-Type: application/json; charset=utf-8');
@header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
@header('Pragma: no-cache');

function out(array $j, int $code = 200): void {
  http_response_code($code);
  echo json_encode($j, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  exit;
}

function safe_slug(string $s): string {
  $s = trim($s);
  $s = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $s);
  $s = preg_replace('/_+/', '_', $s);
  $s = trim($s, '_');
  if ($s === '') $s = 'geral';
  return substr($s, 0, 40);
}

function safe_filename(string $name, string $fallbackExt): string {
  $name = trim($name);
  $name = preg_replace('/[^\w\.\-]+/u', '_', $name);
  $name = preg_replace('/_+/', '_', $name);
  $name = trim($name, '._-');

  if ($name === '') $name = 'file';

  $name = preg_replace('/\.(php|phtml|phar|cgi|pl|asp|aspx|jsp|sh|bat|cmd|exe|dll)$/i', '', $name);

  if (!preg_match('/\.[a-z0-9]{2,6}$/i', $name)) {
    $name .= '.' . $fallbackExt;
  }
  return substr($name, 0, 140);
}

function finfo_mime(string $path): string {
  $f = @finfo_open(FILEINFO_MIME_TYPE);
  if (!$f) return '';
  $m = (string)@finfo_file($f, $path);
  @finfo_close($f);
  return $m;
}

function ensure_dir(string $dir): void {
  if (!is_dir($dir)) {
    @mkdir($dir, 0755, true);
  }
}

function try_convert_to_webp(string $srcPath, string $dstPath, int $quality = 86): bool {
  if (!function_exists('imagecreatefromstring') || !function_exists('imagewebp')) return false;

  $data = @file_get_contents($srcPath);
  if ($data === false) return false;

  $im = @imagecreatefromstring($data);
  if (!$im) return false;

  $w = imagesx($im);
  $h = imagesy($im);
  $bg = imagecreatetruecolor($w, $h);
  $white = imagecolorallocate($bg, 255, 255, 255);
  imagefilledrectangle($bg, 0, 0, $w, $h, $white);
  imagecopy($bg, $im, 0, 0, 0, 0, $w, $h);

  $ok = @imagewebp($bg, $dstPath, $quality);

  imagedestroy($im);
  imagedestroy($bg);

  return (bool)$ok && file_exists($dstPath) && filesize($dstPath) > 0;
}

function human_bytes(int $bytes): string {
  $b = max(0, $bytes);
  if ($b < 1024) return $b . ' B';
  $kb = $b / 1024;
  if ($kb < 1024) return round($kb) . ' KB';
  $mb = $kb / 1024;
  if ($mb < 1024) return round($mb) . ' MB';
  $gb = $mb / 1024;
  return round($gb, 1) . ' GB';
}

function cmd_exists(string $cmd): bool {
  $out = @shell_exec('command -v ' . escapeshellarg($cmd) . ' 2>/dev/null');
  return is_string($out) && trim($out) !== '';
}

function try_pdf_pages_pdfinfo(string $pdfPath): int {
  if (!cmd_exists('pdfinfo')) return 0;
  $cmd = 'pdfinfo ' . escapeshellarg($pdfPath) . ' 2>/dev/null';
  $txt = @shell_exec($cmd);
  if (!is_string($txt) || $txt === '') return 0;
  if (preg_match('/^Pages:\s+(\d+)/mi', $txt, $m)) return (int)$m[1];
  return 0;
}

function try_pdf_preview_imagick(string $pdfPath, string $dstWebp): bool {
  if (!class_exists('Imagick')) return false;
  try {
    $im = new Imagick();
    $im->setResolution(150, 150);
    $im->readImage($pdfPath . '[0]'); // primeira página
    $im->setImageBackgroundColor('white');
    $im = $im->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);

    // WhatsApp: preview "recortado" (topo)
    $im->setImageFormat('png');

    $w = $im->getImageWidth();
    $h = $im->getImageHeight();

    $targetW = 900;
    if ($w > $targetW) {
      $im->resizeImage($targetW, 0, Imagick::FILTER_LANCZOS, 1);
      $w = $im->getImageWidth();
      $h = $im->getImageHeight();
    }

    $cropH = (int)max(240, min((int)round($h * 0.42), 520));
    $im->cropImage($w, $cropH, 0, 0);
    $im->setImagePage(0, 0, 0, 0);

    $im->setImageFormat('webp');
    if (method_exists($im, 'setImageCompressionQuality')) {
      $im->setImageCompressionQuality(82);
    }

    $ok = $im->writeImage($dstWebp);
    $im->clear();
    $im->destroy();

    return (bool)$ok && file_exists($dstWebp) && filesize($dstWebp) > 0;
  } catch (\Throwable $e) {
    return false;
  }
}

function try_pdf_preview_pdftoppm(string $pdfPath, string $dstWebp): bool {
  if (!cmd_exists('pdftoppm')) return false;

  $tmpBase = sys_get_temp_dir() . '/ej_pdfprev_' . bin2hex(random_bytes(6));
  $pngPath = $tmpBase . '-1.png';

  $cmd = 'pdftoppm -f 1 -l 1 -png -r 150 ' . escapeshellarg($pdfPath) . ' ' . escapeshellarg($tmpBase) . ' 2>/dev/null';
  @shell_exec($cmd);

  if (!file_exists($pngPath) || filesize($pngPath) <= 0) {
    @unlink($pngPath);
    return false;
  }

  if (cmd_exists('cwebp')) {
    $cmd2 = 'cwebp -q 82 ' . escapeshellarg($pngPath) . ' -o ' . escapeshellarg($dstWebp) . ' 2>/dev/null';
    @shell_exec($cmd2);
    @unlink($pngPath);
    return file_exists($dstWebp) && filesize($dstWebp) > 0;
  }

  $ok = false;
  if (function_exists('imagecreatefrompng') && function_exists('imagewebp')) {
    $im = @imagecreatefrompng($pngPath);
    if ($im) {
      $w = imagesx($im);
      $h = imagesy($im);
      $cropH = (int)max(240, min((int)round($h * 0.42), 520));
      $cropped = imagecreatetruecolor($w, $cropH);
      $white = imagecolorallocate($cropped, 255, 255, 255);
      imagefilledrectangle($cropped, 0, 0, $w, $cropH, $white);
      imagecopy($cropped, $im, 0, 0, 0, 0, $w, $cropH);

      $ok = @imagewebp($cropped, $dstWebp, 82);

      imagedestroy($im);
      imagedestroy($cropped);
    }
  }
  @unlink($pngPath);

  return (bool)$ok && file_exists($dstWebp) && filesize($dstWebp) > 0;
}

/**
 * Normaliza $_FILES para uma lista de arquivos.
 * Aceita:
 *  - single: $_FILES['file']['name'] = "a.pdf"
 *  - multi : $_FILES['file']['name'][0..n] quando field name = file[]
 */
function normalize_files(array $f): array {
  $out = [];
  if (!isset($f['name'], $f['tmp_name'], $f['error'], $f['size'])) return $out;

  if (is_array($f['name'])) {
    $n = count($f['name']);
    for ($i = 0; $i < $n; $i++) {
      $out[] = [
        'name' => (string)($f['name'][$i] ?? ''),
        'tmp_name' => (string)($f['tmp_name'][$i] ?? ''),
        'error' => (int)($f['error'][$i] ?? UPLOAD_ERR_NO_FILE),
        'size' => (int)($f['size'][$i] ?? 0),
      ];
    }
  } else {
    $out[] = [
      'name' => (string)$f['name'],
      'tmp_name' => (string)$f['tmp_name'],
      'error' => (int)$f['error'],
      'size' => (int)$f['size'],
    ];
  }
  return $out;
}

// ============================
// validações
// ============================
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  out(['ok' => false, 'error' => 'method_not_allowed'], 405);
}

$room = isset($_POST['room']) ? safe_slug((string)$_POST['room']) : 'geral';
$kind = isset($_POST['kind']) ? strtolower(trim((string)$_POST['kind'])) : '';

$ALLOWED_KINDS = ['image', 'pdf', 'video'];
if (!in_array($kind, $ALLOWED_KINDS, true)) {
  out(['ok' => false, 'error' => 'bad_kind'], 400);
}

// aceita 'file' e, se não existir, tenta achar a única chave em $_FILES
$fileKey = 'file';
if (!isset($_FILES[$fileKey])) {
  if (count($_FILES) === 1) {
    $keys = array_keys($_FILES);
    $fileKey = (string)$keys[0];
  } else {
    out(['ok' => false, 'error' => 'no_file'], 400);
  }
}

$f = $_FILES[$fileKey];
if (!is_array($f)) {
  out(['ok' => false, 'error' => 'bad_upload'], 400);
}

$files = normalize_files($f);
$files = array_values(array_filter($files, fn($it) => !empty($it['tmp_name']) || !empty($it['name'])));

if (count($files) < 1) {
  out(['ok' => false, 'error' => 'no_file'], 400);
}

// limites por tipo
$MAX_IMAGE = 12 * 1024 * 1024;  // 12 MB
$MAX_PDF   = 60 * 1024 * 1024;  // 60 MB
$MAX_VIDEO = 120 * 1024 * 1024; // 120 MB

// limite de quantidade por request (o seu pedido: PDF max 5)
$MAX_COUNT = [
  'pdf'   => 5,
  'image' => 10,
  'video' => 5,
];

$limitCount = $MAX_COUNT[$kind] ?? 1;
if (count($files) > $limitCount) {
  out(['ok' => false, 'error' => 'too_many_files', 'max' => $limitCount], 413);
}

// ============================
// caminho destino
// ============================
$baseDir = __DIR__ . '/uploads/' . $room;
ensure_dir($baseDir);

// .htaccess anti-exec + sem index
$ht = $baseDir . '/.htaccess';
if (!file_exists($ht)) {
  @file_put_contents($ht,
"Options -Indexes\n".
"AddType application/octet-stream .php .phtml .phar\n".
"RemoveHandler .php .phtml .phar\n".
"<FilesMatch \"\\.(php|phtml|phar)$\">\n".
"  Require all denied\n".
"</FilesMatch>\n"
  );
}

$items = [];
$savedPaths = []; // para cleanup em erro

foreach ($files as $idx => $fi) {
  $tmp = (string)$fi['tmp_name'];
  $size = (int)$fi['size'];
  $origName = (string)$fi['name'];
  $err = (int)$fi['error'];

  if ($err !== UPLOAD_ERR_OK) {
    // cleanup do que já foi salvo
    foreach ($savedPaths as $p) { @unlink($p); }
    out(['ok' => false, 'error' => 'upload_error_' . $err, 'index' => $idx], 400);
  }

  if ($tmp === '' || !is_uploaded_file($tmp)) {
    foreach ($savedPaths as $p) { @unlink($p); }
    out(['ok' => false, 'error' => 'not_uploaded_file', 'index' => $idx], 400);
  }

  // tamanho por tipo
  if ($kind === 'image' && $size > $MAX_IMAGE) { foreach ($savedPaths as $p) { @unlink($p); } out(['ok'=>false,'error'=>'image_too_large','index'=>$idx], 413); }
  if ($kind === 'pdf'   && $size > $MAX_PDF)   { foreach ($savedPaths as $p) { @unlink($p); } out(['ok'=>false,'error'=>'pdf_too_large','index'=>$idx], 413); }
  if ($kind === 'video' && $size > $MAX_VIDEO) { foreach ($savedPaths as $p) { @unlink($p); } out(['ok'=>false,'error'=>'video_too_large','index'=>$idx,'max_bytes'=>$MAX_VIDEO,'max_h'=>human_bytes($MAX_VIDEO)], 413); }

  // mime real via finfo
  $mime = finfo_mime($tmp);

  // validações por tipo + extensão final
  $ext = '';
  if ($kind === 'pdf') {
    if ($mime !== 'application/pdf') {
      foreach ($savedPaths as $p) { @unlink($p); }
      out(['ok' => false, 'error' => 'bad_pdf_mime', 'mime' => $mime, 'index' => $idx], 400);
    }
    $ext = 'pdf';
  } elseif ($kind === 'image') {
    if (strpos($mime, 'image/') !== 0) {
      foreach ($savedPaths as $p) { @unlink($p); }
      out(['ok' => false, 'error' => 'bad_image_mime', 'mime' => $mime, 'index' => $idx], 400);
    }
    $ext = 'webp';
  } elseif ($kind === 'video') {
    if (strpos($mime, 'video/') !== 0) {
      foreach ($savedPaths as $p) { @unlink($p); }
      out(['ok' => false, 'error' => 'bad_video_mime', 'mime' => $mime, 'index' => $idx], 400);
    }
    $nExt = strtolower((string)pathinfo($origName, PATHINFO_EXTENSION));
    if (!preg_match('/^(mp4|webm|mov|m4v|ogg)$/', $nExt)) $nExt = 'mp4';
    $ext = $nExt;
  }

  $rand = bin2hex(random_bytes(8));
  $ts = date('Ymd_His');

  $finalName = safe_filename($origName, $ext);
  $finalName = $ts . '_' . $rand . '_' . $finalName;
  $dstPath = $baseDir . '/' . $finalName;

  $pages = 0;
  $previewUrl = '';

  // grava e pós-processa
  if ($kind === 'image') {
    $tmpWebp = $dstPath;
    $ok = try_convert_to_webp($tmp, $tmpWebp, 86);

    if (!$ok) {
      // fallback: salva original
      $nExt = strtolower((string)pathinfo($origName, PATHINFO_EXTENSION));
      if (!preg_match('/^(jpg|jpeg|png|gif|webp|bmp|heic)$/', $nExt)) $nExt = 'bin';
      $finalName = $ts . '_' . $rand . '_' . safe_filename($origName, $nExt);
      $dstPath = $baseDir . '/' . $finalName;

      if (!@move_uploaded_file($tmp, $dstPath)) {
        foreach ($savedPaths as $p) { @unlink($p); }
        out(['ok' => false, 'error' => 'move_fail', 'index' => $idx], 500);
      }
    }
  } else {
    if (!@move_uploaded_file($tmp, $dstPath)) {
      foreach ($savedPaths as $p) { @unlink($p); }
      out(['ok' => false, 'error' => 'move_fail', 'index' => $idx], 500);
    }

    if ($kind === 'pdf') {
      $pages = try_pdf_pages_pdfinfo($dstPath);

      $previewName = preg_replace('/\.pdf$/i', '', basename($dstPath)) . '_p1.webp';
      $previewPath = $baseDir . '/' . $previewName;

      $okPrev = try_pdf_preview_imagick($dstPath, $previewPath);
      if (!$okPrev) $okPrev = try_pdf_preview_pdftoppm($dstPath, $previewPath);

      if ($okPrev) {
        @chmod($previewPath, 0644);
        $previewUrl = '/chat_api/uploads/' . rawurlencode($room) . '/' . rawurlencode($previewName);
        $savedPaths[] = $previewPath;
      }
    }
  }

  @chmod($dstPath, 0644);
  $savedPaths[] = $dstPath;

  $publicUrl = '/chat_api/uploads/' . rawurlencode($room) . '/' . rawurlencode(basename($dstPath));

  $items[] = [
    'kind' => $kind,
    'room' => $room,
    'url' => $publicUrl,
    'preview_url' => $previewUrl,
    'pages' => $pages,
    'size' => $size,
    'size_h' => human_bytes($size),
    'mime' => $mime,
  ];
}

// resposta compatível:
// - 1 arquivo: mantém o formato antigo
// - múltiplos: retorna items[]
if (count($items) === 1) {
  $one = $items[0];
  out([
    'ok' => true,
    'kind' => $one['kind'],
    'room' => $one['room'],
    'url' => $one['url'],
    'preview_url' => $one['preview_url'],
    'pages' => $one['pages'],
    'size' => $one['size'],
    'size_h' => $one['size_h'],
    'mime' => $one['mime'],
  ]);
}

out([
  'ok' => true,
  'multi' => true,
  'kind' => $kind,
  'room' => $room,
  'count' => count($items),
  'items' => $items,
]);