<?php
ini_set('display_errors', 1);
/**
 * Renombrado masivo de imágenes en FTP/FTPS
 * Regla: tomar SKU = texto antes del primer "_" o "-" en el nombre.
 * Si un SKU se repite, asignar secuencia 1..N, idealmente ordenada por "ImagenN" si existe.
 * Formato final: SKU-<secuencia>.<ext> (ext preservada, configurable).
 * Seguro: dry-run por defecto, log CSV, dos fases de rename, evita colisiones.
 */

/* ============ CONFIG ============ */
const FTP_HOST    = 'c2850655.ferozo.com';
const FTP_USER    = 'c2850655';
const FTP_PASS    = 'kiwusaNE21';
const FTP_PORT    = 21;
const USE_FTPS    = true;          // true = ftps (recomendado), false = ftp plano
const PASSIVE_MODE= true;          // suele ser necesario tras firewalls/NAT
const REMOTE_DIR  = '/img'; // carpeta en el FTP con las fotos

const DRY_RUN     = true;          // ensayo: NO renombra, solo simula y loguea
const FORCE_EXT   = null;          // null = conservar extensión; 'jpeg' = forzar .jpeg
const LOG_CSV     = 'renombrado_ftp_log.csv'; // log local
/* ================================= */

date_default_timezone_set('UTC');

/* ---- Conexión FTP/FTPS ---- */
$conn = true;
//$conn = null;


if (USE_FTPS) {
    if (!function_exists('ftp_ssl_connect')) {
        exit("ERROR: PHP no tiene ftp_ssl_connect (FTPS). Cambia USE_FTPS=false o habilita FTPS.\n");
    }
    $conn = @ftp_ssl_connect(FTP_HOST, FTP_PORT, 30);
} else {
    $conn = @ftp_connect(FTP_HOST, FTP_PORT, 30);
}


if (!$conn) exit("ERROR: No se pudo conectar a ".FTP_HOST.":".FTP_PORT."\n");
if (!@ftp_login($conn, FTP_USER, FTP_PASS)) exit("ERROR: Login FTP falló.\n");
@ftp_pasv($conn, PASSIVE_MODE);
echo "Conectado a ".FTP_HOST." (".(USE_FTPS?'FTPS':'FTP').")\n";

/* ---- Verificar carpeta ---- */
if (!@ftp_chdir($conn, REMOTE_DIR)) exit("ERROR: No existe o no hay permiso en REMOTE_DIR: ".REMOTE_DIR."\n");
$cwd = @ftp_pwd($conn);
echo "Directorio remoto: $cwd\n";

/* ---- Utilidades ---- */
function ftp_list_files($conn): array {
    // Obtenemos listado y filtramos solo archivos (ftp_size > -1).
    $items = @ftp_nlist($conn, ".");
    if ($items === false) return [];
    $files = [];
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') continue;
        $size = @ftp_size($conn, $item);
        if ($size > -1) $files[] = $item; // es archivo
    }
    sort($files, SORT_NATURAL | SORT_FLAG_CASE);
    return $files;
}

function parse_filename(string $fname): array {
    // SKU = todo hasta el primer "_" o "-" (si hay).
    if (preg_match('/^([^_-]+)/u', $fname, $m)) {
        $sku = $m[1];
    } else {
        $sku = null;
    }
    // ImagenN en cualquier parte: Imagen2, imagen 3, _imagen4, -Imagen10, etc.
    if (preg_match('/(?i)(?:^|[_-])imagen\s*(\d+)/u', $fname, $m)) {
        $imgNum = intval($m[1]);
    } else {
        $imgNum = null;
    }
    // Extensión
    $ext = strtolower(pathinfo($fname, PATHINFO_EXTENSION));
    return [$sku, $imgNum, $ext];
}

function is_image_ext(?string $ext): bool {
    static $allowed = ['jpg','jpeg','png','webp','gif','tif','tiff','bmp','heic'];
    return $ext !== null && in_array(strtolower($ext), $allowed, true);
}

/* ---- 1) Leer archivos ---- */
$files = ftp_list_files($conn);
if (empty($files)) {
    echo "No se encontraron archivos en $cwd\n";
    ftp_close($conn);
    exit;
}
echo "Total archivos encontrados: ".count($files)."\n";

/* ---- 2) Analizar y agrupar por SKU ---- */
$rows = []; // para log
$bySku = []; // sku => [ [fname, imgNum, ext], ... ]
foreach ($files as $fname) {
    [$sku, $imgNum, $ext] = parse_filename($fname);
    if (!is_image_ext($ext)) {
        $rows[] = [$fname, '', '', '', 'NoImagenExt'];
        continue;
    }
    if (!$sku) {
        $rows[] = [$fname, '', '', '', 'NoSKU'];
        continue;
    }
    $bySku[$sku][] = ['fname'=>$fname, 'imgNum'=>$imgNum, 'ext'=>$ext];
}

/* ---- 3) Construir mapping final (old -> target) ---- */
$mapping = []; // old => finalName
foreach ($bySku as $sku => $items) {
    // Orden: primero con imgNum (asc), luego sin imgNum por nombre
    $with = array_values(array_filter($items, fn($x)=>$x['imgNum'] !== null));
    $without = array_values(array_filter($items, fn($x)=>$x['imgNum'] === null));
    usort($with, function($a,$b){
        return $a['imgNum'] <=> $b['imgNum'];
    });
    usort($without, function($a,$b){
        return strnatcasecmp($a['fname'], $b['fname']);
    });
    $ordered = array_merge($with, $without);

    $seq = 1;
    foreach ($ordered as $it) {
        $old = $it['fname'];
        $ext = FORCE_EXT ? strtolower(FORCE_EXT) : $it['ext'];
        $finalName = sprintf('%s-%d.%s', $sku, $seq, $ext ?: 'jpeg');
        $mapping[$old] = $finalName;
        $seq++;
    }
}

/* ---- 4) Chequeo de colisiones de destino ---- */
$targets = [];
$collisions = [];
foreach ($mapping as $old => $new) {
    if (isset($targets[$new])) {
        $collisions[] = $new;
    } else {
        $targets[$new] = $old;
    }
}
if ($collisions) {
    echo "ADVERTENCIA: colisiones de destino planificadas en nombres: ".implode(', ', array_unique($collisions))."\n";
    echo "Se resolverán automáticamente añadiendo sufijo __DUP__.\n";
}

/* ---- 5) Log preliminar ---- */
foreach ($files as $fname) {
    if (isset($mapping[$fname])) {
        $rows[] = [$fname, '', '', $mapping[$fname], 'OK'];
    } else {
        // ya se registró NoImagenExt o NoSKU arriba; si no, es "NoProcesado"
        $ya = false;
        for ($i=0;$i<count($rows);$i++){
            if ($rows[$i][0] === $fname) { $ya = true; break; }
        }
        if (!$ya) $rows[] = [$fname, '', '', '', 'NoProcesado'];
    }
}

/* ---- 6) Guardar log CSV ---- */
$fp = fopen(LOG_CSV, 'w');
fputcsv($fp, ['OriginalName','SKU','ImageNum','TargetName','Status']);
foreach ($rows as $r) fputcsv($fp, $r);
fclose($fp);
echo "Log generado: ".LOG_CSV."\n";

/* ---- 7) Si DRY_RUN, terminar aquí ---- */
if (DRY_RUN) {
    echo "DRY-RUN activo: no se renombró nada. Revisa el CSV y si está OK, pon DRY_RUN=false.\n";
    ftp_close($conn);
    exit;
}

/* ---- 8) Renombrado en dos fases (seguro) ---- */
// 8a. Fase temporal: old -> __TMP__<uniqid>.<ext>
$tmpMap = []; // tmp => final
foreach ($mapping as $old => $new) {
    $pathTmp = "__TMP__".substr(md5(uniqid('', true)),0,8).".".pathinfo($old, PATHINFO_EXTENSION);
    // evitar que tmp exista
    while (@ftp_size($conn, $pathTmp) > -1) {
        $pathTmp = "__TMP__".substr(md5(uniqid('', true)),0,8).".".pathinfo($old, PATHINFO_EXTENSION);
    }
    if (!@ftp_rename($conn, $old, $pathTmp)) {
        echo "ERROR al renombrar temporalmente: $old -> $pathTmp\n";
        continue;
    }
    $tmpMap[$pathTmp] = $new;
    echo "TMP: $old -> $pathTmp\n";
}

// 8b. Fase final: tmp -> final (resolviendo colisiones existentes en servidor)
foreach ($tmpMap as $tmp => $final) {
    $dest = $final;
    // si ya existe en el servidor, agregar sufijo __DUP__
    if (@ftp_size($conn, $dest) > -1) {
        $pi = pathinfo($dest);
        $dest = $pi['filename'].'__DUP__'.substr(md5(uniqid('', true)),0,4).'.'.($pi['extension'] ?? 'jpeg');
        echo "Colisión resuelta: $final -> $dest\n";
    }
    if (!@ftp_rename($conn, $tmp, $dest)) {
        echo "ERROR al renombrar final: $tmp -> $dest\n";
    } else {
        echo "OK: $tmp -> $dest\n";
    }
}

ftp_close($conn);
echo "Proceso completado.\n";