#!/usr/bin/env php
<?php
/**
 * Inbound Mail CLI — email-to-ticket
 *
 * Ingiere correos crudos (RFC822) y crea/actualiza tickets. No requiere la
 * extensión IMAP: el correo llega por una de estas dos vías:
 *
 *   1) Tubería (pipe) desde el MTA:
 *        php bin/inbound-mail --stdin   < correo.eml
 *      (alias en /etc/aliases o .forward:  "|/usr/bin/php /ruta/bin/inbound-mail --stdin")
 *
 *   2) Directorio de buzón (fetchmail/getmail/maildrop dejan .eml ahí):
 *        php bin/inbound-mail            (procesa storage/inbound/*.eml)
 *
 * Uso:
 *   php bin/inbound-mail --stdin
 *   php bin/inbound-mail --dir=storage/inbound
 *   php bin/inbound-mail --dry-run
 *   php bin/inbound-mail --tenant=academico
 *   php bin/inbound-mail --help
 */

$basePath = dirname(__DIR__);
define('BASE_PATH', $basePath . '/');

require $basePath . '/core/helpers/Env.php';
Env::load($basePath . '/.env');

$envGet = function ($key, $default = '') {
    $value = getenv($key);
    if ($value === false || $value === '') {
        $value = isset($_ENV[$key]) ? $_ENV[$key] : (isset($_SERVER[$key]) ? $_SERVER[$key] : false);
    }
    return ($value === false || $value === '') ? $default : (string)$value;
};

$args = array_slice($argv, 1);

if (in_array('--help', $args, true) || in_array('-h', $args, true)) {
    echo "\nKlee Inbound Mail (email-to-ticket)\n";
    echo "Uso:\n";
    echo "  php bin/inbound-mail --stdin            Procesa UN correo crudo desde stdin (pipe del MTA)\n";
    echo "  php bin/inbound-mail                    Procesa storage/inbound/*.eml\n";
    echo "  php bin/inbound-mail --dir=RUTA         Directorio de buzón alternativo\n";
    echo "  php bin/inbound-mail --dry-run          Parsea y muestra qué haría; no escribe\n";
    echo "  php bin/inbound-mail --tenant=KEY       Tenant a procesar\n\n";
    echo "Habilitar con INBOUND_MAIL_ENABLED=true en .env (el --dry-run funciona igual para pruebas).\n\n";
    exit(0);
}

$useStdin = in_array('--stdin', $args, true);
$dryRun = in_array('--dry-run', $args, true);
$tenantKey = '';
$dir = trim((string)$envGet('INBOUND_MAIL_DIR', $basePath . '/storage/inbound'));

foreach ($args as $arg) {
    if (strpos($arg, '--dir=') === 0) {
        $dir = trim((string)substr($arg, 6));
    } elseif (strpos($arg, '--tenant=') === 0) {
        $tenantKey = trim((string)substr($arg, 9));
    }
}

if ($tenantKey !== '') {
    putenv('KLEE_TENANT=' . $tenantKey);
    $_ENV['KLEE_TENANT'] = $tenantKey;
    $_SERVER['KLEE_TENANT'] = $tenantKey;
}

if (file_exists($basePath . '/vendor/autoload.php')) {
    require $basePath . '/vendor/autoload.php';
}

$fallbackAutoloadSearchPaths = array(
    'core/', 'core/traits/', 'core/db/', 'core/middleware/',
    'core/attributes/', 'core/contracts/', 'app/models/', 'app/controllers/',
    'core/helpers/', 'app/config/',
);
$fallbackClassFileMap = array(
    'DB' => 'core/Db.php',
    'Database' => 'core/Db.php',
    'FORM_VALIDATE' => 'core/Formvalidate.php',
    'FormValidator' => 'core/Formvalidate.php',
);

spl_autoload_register(function ($class) use ($basePath, $fallbackAutoloadSearchPaths, $fallbackClassFileMap) {
    if (isset($fallbackClassFileMap[$class])) {
        $mappedPath = $basePath . '/' . $fallbackClassFileMap[$class];
        if (file_exists($mappedPath)) {
            require_once $mappedPath;
            return;
        }
    }
    foreach ($fallbackAutoloadSearchPaths as $relativePath) {
        $classPath = $basePath . '/' . $relativePath . $class . '.php';
        if (file_exists($classPath)) {
            require_once $classPath;
            return;
        }
    }
}, true, true);

if (file_exists($basePath . '/app/config/Config.php')) {
    require $basePath . '/app/config/Config.php';
}
if (file_exists($basePath . '/app/config/ConfigEnv.php')) {
    require $basePath . '/app/config/ConfigEnv.php';
}

if (class_exists('TenantContext')) {
    TenantContext::bootstrap();
}
if (class_exists('Controller')) {
    Controller::syncConfigFromEnv();
}
if (class_exists('Config')) {
    @date_default_timezone_set(Config::getTimezone());
}
if (session_status() === PHP_SESSION_NONE) {
    @session_start();
}

if (!class_exists('TicketsModel') || !class_exists('InboundMail')) {
    fwrite(STDERR, "[error] Clases no disponibles. Verifica el autoload.\n");
    exit(2);
}

$enabled = filter_var((string)$envGet('INBOUND_MAIL_ENABLED', 'false'), FILTER_VALIDATE_BOOLEAN);
if (!$enabled && !$dryRun) {
    fwrite(STDERR, "[abort] Email entrante deshabilitado. Activa INBOUND_MAIL_ENABLED=true o usa --dry-run.\n");
    exit(3);
}

// Lista de direcciones a ignorar (anti-bucle): las propias del sistema.
$ignore = array();
foreach (array($envGet('INBOUND_IGNORE_ADDRESSES', ''), $envGet('MAIL_USER', '')) as $raw) {
    foreach (preg_split('/[\s,;]+/', (string)$raw) as $addr) {
        $addr = strtolower(trim($addr));
        if ($addr !== '') {
            $ignore[$addr] = true;
        }
    }
}

$stats = array('processed' => 0, 'created' => 0, 'replied' => 0, 'skipped' => 0, 'failed' => 0);

$handleOne = function ($raw, $label) use (&$stats, $dryRun, $ignore) {
    $raw = (string)$raw;
    if (trim($raw) === '') {
        echo "[skip] $label: correo vacío.\n";
        $stats['skipped']++;
        return 'skipped';
    }

    $parsed = InboundMail::parse($raw);
    $from = (string)($parsed['from_email'] ?? '');

    if ($from !== '' && isset($ignore[strtolower($from)])) {
        echo "[skip] $label: remitente en lista de ignorados ($from).\n";
        $stats['skipped']++;
        return 'skipped';
    }

    if ($dryRun) {
        $subject = (string)($parsed['subject'] ?? '');
        $isReply = preg_match('/\[(\d{4}-\d{6})\]/', $subject) ? 'respuesta' : 'nuevo';
        $auto = !empty($parsed['is_auto']) ? ' [auto-ignorado]' : '';
        echo "[dry] $label: de=$from asunto=\"" . mb_strimwidth($subject, 0, 60, '…') . "\" -> $isReply$auto\n";
        $stats['processed']++;
        return 'dry';
    }

    $result = TicketsModel::ingestInboundEmail($parsed);
    $stats['processed']++;

    if (!empty($result['ok'])) {
        if (($result['action'] ?? '') === 'reply') {
            echo "[ok] $label: respuesta agregada al caso " . ($result['numeroCaso'] ?? '?') . ".\n";
            $stats['replied']++;
        } else {
            echo "[ok] $label: ticket creado " . ($result['numeroCaso'] ?? '?') . ".\n";
            $stats['created']++;
        }
        return 'ok';
    }

    if (($result['action'] ?? '') === 'skip') {
        echo "[skip] $label: " . ($result['message'] ?? 'ignorado') . "\n";
        $stats['skipped']++;
        return 'skipped';
    }

    echo "[fail] $label: " . ($result['message'] ?? 'error') . "\n";
    $stats['failed']++;
    return 'failed';
};

if ($useStdin) {
    $raw = stream_get_contents(STDIN);
    $handleOne($raw, 'stdin');
} else {
    if (!is_dir($dir)) {
        fwrite(STDERR, "[abort] Directorio de buzón no existe: $dir\n");
        exit(4);
    }

    $processedDir = $dir . '/processed';
    $failedDir = $dir . '/failed';
    if (!$dryRun) {
        @mkdir($processedDir, 0775, true);
        @mkdir($failedDir, 0775, true);
    }

    $files = glob($dir . '/*.eml');
    $files = is_array($files) ? $files : array();
    sort($files);

    if (empty($files)) {
        echo "No hay correos (.eml) en $dir\n";
    }

    foreach ($files as $file) {
        $raw = @file_get_contents($file);
        $outcome = $handleOne($raw, basename($file));

        if ($dryRun) {
            continue;
        }

        $target = ($outcome === 'failed' ? $failedDir : $processedDir) . '/' . date('YmdHis') . '_' . basename($file);
        if (!@rename($file, $target)) {
            @copy($file, $target);
            @unlink($file);
        }
    }
}

echo "\nResumen: procesados={$stats['processed']} creados={$stats['created']} respuestas={$stats['replied']} ignorados={$stats['skipped']} fallidos={$stats['failed']}\n";
if ($dryRun) {
    echo "Modo: dry-run (no se escribió nada)\n";
}

exit($stats['failed'] > 0 ? 1 : 0);
