<?php

declare(strict_types=1);

require_once dirname(__DIR__) . '/app/bootstrap.php';

$path = current_path();
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';

try {
    if ($method === 'POST' && !str_starts_with($path, '/webhooks/postmark/')) {
        require_post_csrf();
    }

    route($method, $path);
} catch (Throwable $e) {
    http_response_code(500);
    render('Error', function () use ($e): void {
        ?>
        <section class="panel">
            <h1>Error</h1>
            <p><?php echo h($e->getMessage()); ?></p>
        </section>
        <?php
    });
}

function route(string $method, string $path): void
{
    if ($path === '/') {
        redirect_to('/dashboard');
    }

    if ($path === '/login') {
        login_route();
        return;
    }

    if ($path === '/logout') {
        Auth::logout();
        flash('success', 'Sesion cerrada.');
        redirect_to('/login');
    }

    if (str_starts_with($path, '/webhooks/postmark/')) {
        postmark_webhook_route(substr($path, strlen('/webhooks/postmark/')));
        return;
    }

    Auth::requireLogin();

    match ($path) {
        '/dashboard' => dashboard_route(),
        '/admin/settings' => admin_settings_route(),
        '/admin/templates' => admin_templates_route(),
        '/admin/logs' => admin_logs_route(),
        '/csv' => csv_route(),
        '/manual' => manual_route(),
        '/send' => send_route(),
        '/history' => history_route(),
        '/message' => message_route(),
        default => not_found_route(),
    };
}

function login_route(): void
{
    if (!empty($_GET['token'])) {
        try {
            Auth::loginWithDrupalToken((string) $_GET['token']);
            flash('success', 'Ingreso correcto.');
            $return = safe_panel_return($_GET['return'] ?? '');
            if ($return !== '') {
                header('Location: ' . url($return));
                exit;
            }
            redirect_to('/dashboard');
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
        }
    } elseif (Auth::user()) {
        $return = safe_panel_return($_GET['return'] ?? '');
        if ($return !== '') {
            header('Location: ' . url($return));
            exit;
        }
        redirect_to('/dashboard');
    } else {
        $return = safe_panel_return($_GET['return'] ?? '');
        $returnUrl = $return !== '' ? absolute_url($return) : absolute_url('/dashboard');
        Auth::redirectToDrupalAccess($returnUrl);
    }

    render('Ingreso', function (): void {
        ?>
        <section class="panel">
            <h1>Ingreso</h1>
            <p>Este sistema se abre desde Drupal mediante token firmado.</p>
            <p class="muted">Si llegaste aqui manualmente, vuelve al enlace configurado en Drupal.</p>
        </section>
        <?php
    });
}

function dashboard_route(): void
{
    $pdo = Database::pdo();
    $statuses = $pdo->query('SELECT status, COUNT(*) total FROM email_recipients GROUP BY status')->fetchAll();
    $pending = (int) $pdo->query("SELECT COUNT(*) FROM email_recipients WHERE status = 'pendiente'")->fetchColumn();
    $templates = (int) $pdo->query('SELECT COUNT(*) FROM templates WHERE is_active = 1')->fetchColumn();
    $events = (int) $pdo->query('SELECT COUNT(*) FROM email_events')->fetchColumn();

    render('Inicio', function () use ($statuses, $pending, $templates, $events): void {
        ?>
        <section class="grid">
            <div class="metric"><span>Pendientes</span><strong><?php echo h($pending); ?></strong></div>
            <div class="metric"><span>Plantillas activas</span><strong><?php echo h($templates); ?></strong></div>
            <div class="metric"><span>Eventos Postmark</span><strong><?php echo h($events); ?></strong></div>
        </section>
        <section class="panel">
            <h1>Estados</h1>
            <?php if (!$statuses): ?>
                <p class="muted">Aun no hay registros cargados.</p>
            <?php else: ?>
                <table>
                    <thead><tr><th>Estado</th><th>Total</th></tr></thead>
                    <tbody>
                        <?php foreach ($statuses as $row): ?>
                            <tr><td><span class="status <?php echo h($row['status']); ?>"><?php echo h($row['status']); ?></span></td><td><?php echo h($row['total']); ?></td></tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            <?php endif; ?>
        </section>
        <?php
    });
}

function admin_settings_route(): void
{
    Auth::requireAdmin();

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $keys = array(
            'postmark_api_base',
            'postmark_token',
            'postmark_from',
            'postmark_message_stream',
            'webhook_message_stream_filter',
            'drupal_shared_secret',
            'drupal_access_url',
            'drupal_token_ttl',
            'auth_provider',
            'webhook_secret',
        );
        foreach ($keys as $key) {
            Settings::set($key, trim((string) ($_POST[$key] ?? '')));
        }
        Settings::set('future_auth_provider_enabled', !empty($_POST['future_auth_provider_enabled']) ? '1' : '0');
        Audit::log('settings_update', 'settings', 'global', array('keys' => $keys));
        flash('success', 'Configuracion guardada.');
        redirect_to('/admin/settings');
    }

    render('Configuracion', function (): void {
        ?>
        <section class="panel">
            <h1>Configuracion general</h1>
            <form method="post">
                <?php echo csrf_field(); ?>
                <h2>Postmark</h2>
                <label>URL base API Postmark</label>
                <input name="postmark_api_base" value="<?php echo h(Settings::get('postmark_api_base', 'https://api.postmarkapp.com')); ?>" required>

                <label>Server token Postmark</label>
                <input name="postmark_token" value="<?php echo h(Settings::get('postmark_token')); ?>" autocomplete="off">

                <label>Correo remitente</label>
                <input name="postmark_from" type="email" value="<?php echo h(Settings::get('postmark_from')); ?>">

                <label>MessageStream / canal</label>
                <input name="postmark_message_stream" value="<?php echo h(Settings::get('postmark_message_stream', 'outbound')); ?>">

                <label>Canal aceptado en webhook</label>
                <input name="webhook_message_stream_filter" value="<?php echo h(Settings::get('webhook_message_stream_filter')); ?>" placeholder="clases-sincronicas">
                <p class="muted">Si se completa, solo se registraran eventos Postmark de ese MessageStream. Si queda vacio, se aceptan los eventos vinculados a mensajes enviados por este panel.</p>

                <h2>Autenticacion</h2>
                <label>Proveedor activo</label>
                <select name="auth_provider">
                    <option value="drupal_token" selected>Token Drupal 7</option>
                </select>

                <label>Secreto compartido Drupal</label>
                <input name="drupal_shared_secret" value="<?php echo h(Settings::get('drupal_shared_secret')); ?>" autocomplete="off">

                <label>URL de acceso Drupal</label>
                <input name="drupal_access_url" value="<?php echo h(Settings::get('drupal_access_url')); ?>" placeholder="https://tudrupal.cl/acceso-sincronicas-correo">
                <p class="muted">Si alguien entra directo al panel sin sesion, sera enviado a esta URL para iniciar sesion en Drupal.</p>

                <label>TTL token Drupal en segundos</label>
                <input name="drupal_token_ttl" type="number" min="60" max="3600" value="<?php echo h(Settings::get('drupal_token_ttl', '300')); ?>">

                <label><input style="width:auto" type="checkbox" name="future_auth_provider_enabled" disabled> Otro proveedor de acceso reservado</label>

                <h2>Webhook</h2>
                <label>Secreto URL webhook</label>
                <input name="webhook_secret" value="<?php echo h(Settings::get('webhook_secret')); ?>" autocomplete="off">
                <p class="muted">URL: <?php echo h(absolute_url('/webhooks/postmark/' . (Settings::get('webhook_secret') ?: 'TU_SECRETO'))); ?></p>

                <button class="btn" type="submit">Guardar configuracion</button>
            </form>
        </section>
        <?php
    });
}

function admin_templates_route(): void
{
    Auth::requireAdmin();
    $edit = null;

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (($_POST['action'] ?? '') === 'toggle') {
            $stmt = Database::pdo()->prepare('UPDATE templates SET is_active = IF(is_active = 1, 0, 1) WHERE id = ?');
            $stmt->execute(array((int) $_POST['id']));
            Audit::log('template_toggle', 'templates', (string) $_POST['id']);
            flash('success', 'Estado de plantilla actualizado.');
            redirect_to('/admin/templates');
        }

        if (trim((string) ($_POST['name'] ?? '')) === '' || trim((string) ($_POST['postmark_template_key'] ?? '')) === '') {
            flash('error', 'Nombre e ID/Alias Postmark son obligatorios.');
            redirect_to('/admin/templates');
        }

        $id = Templates::save($_POST);
        Audit::log('template_save', 'templates', (string) $id);
        flash('success', 'Plantilla guardada.');
        redirect_to('/admin/templates');
    }

    if (!empty($_GET['edit'])) {
        $edit = Templates::find((int) $_GET['edit']);
    }

    $templates = Templates::all();
    render('Plantillas', function () use ($templates, $edit): void {
        ?>
        <section class="panel">
            <h1>Plantillas Postmark</h1>
            <p class="muted">Registra solo referencias a plantillas existentes. La plantilla se edita en Postmark.</p>
            <form method="post">
                <?php echo csrf_field(); ?>
                <input type="hidden" name="id" value="<?php echo h($edit['id'] ?? ''); ?>">
                <label>Nombre visible</label>
                <input name="name" value="<?php echo h($edit['name'] ?? ''); ?>" required>

                <label>ID numerico o Alias de Postmark</label>
                <input name="postmark_template_key" value="<?php echo h($edit['postmark_template_key'] ?? ''); ?>" required>

                <label>Variables manuales separadas por coma</label>
                <input name="variables" value="<?php echo h(implode(', ', $edit ? Templates::variables($edit) : array())); ?>">

                <label>Asunto de referencia opcional</label>
                <textarea name="subject_reference"><?php echo h($edit['subject_reference'] ?? ''); ?></textarea>

                <label>Cuerpo de referencia opcional</label>
                <textarea name="body_reference"><?php echo h($edit['body_reference'] ?? ''); ?></textarea>

                <label><input style="width:auto" type="checkbox" name="is_active" value="1" <?php echo (!$edit || (int) $edit['is_active'] === 1) ? 'checked' : ''; ?>> Activa</label>
                <button class="btn" type="submit">Guardar plantilla</button>
                <?php if ($edit): ?><a class="btn secondary" href="<?php echo h(url('/admin/templates')); ?>">Cancelar</a><?php endif; ?>
            </form>
        </section>
        <section class="panel">
            <h2>Plantillas registradas</h2>
            <table>
                <thead><tr><th>Nombre</th><th>ID/Alias Postmark</th><th>Variables</th><th>Estado</th><th>Acciones</th></tr></thead>
                <tbody>
                <?php foreach ($templates as $template): ?>
                    <tr>
                        <td><?php echo h($template['name']); ?></td>
                        <td><?php echo h($template['postmark_template_key']); ?></td>
                        <td><?php echo h(implode(', ', Templates::variables($template))); ?></td>
                        <td><span class="status"><?php echo ((int) $template['is_active'] === 1) ? 'activa' : 'inactiva'; ?></span></td>
                        <td>
                            <a href="<?php echo h(url('/admin/templates?edit=' . $template['id'])); ?>">Editar</a>
                            <form method="post" style="display:inline">
                                <?php echo csrf_field(); ?>
                                <input type="hidden" name="action" value="toggle">
                                <input type="hidden" name="id" value="<?php echo h($template['id']); ?>">
                                <button class="btn secondary" type="submit"><?php echo ((int) $template['is_active'] === 1) ? 'Desactivar' : 'Activar'; ?></button>
                            </form>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </section>
        <?php
    });
}

function csv_route(): void
{
    Auth::requireEdit();
    $templates = Templates::active();

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (empty($_FILES['csv']['tmp_name']) || !is_uploaded_file($_FILES['csv']['tmp_name'])) {
            flash('error', 'Selecciona un archivo CSV valido.');
            redirect_to('/csv');
        }
        try {
            $result = CsvImporter::import(
                $_FILES['csv']['tmp_name'],
                $_FILES['csv']['name'] ?? 'carga.csv',
                (int) $_POST['template_id'],
                trim((string) ($_POST['email_column'] ?? '')) ?: null
            );
            flash('success', 'CSV cargado. Filas: ' . $result['total'] . ', validas: ' . $result['valid'] . ', invalidas: ' . $result['invalid'] . '.');
            redirect_to('/send');
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
            redirect_to('/csv');
        }
    }

    render('Cargar CSV', function () use ($templates): void {
        ?>
        <section class="panel">
            <h1>Cargar CSV</h1>
            <?php if (!$templates): ?>
                <p class="alert warning">Primero registra una plantilla activa en administracion.</p>
            <?php endif; ?>
            <form method="post" enctype="multipart/form-data">
                <?php echo csrf_field(); ?>
                <label>Plantilla</label>
                <select name="template_id" required>
                    <?php foreach ($templates as $template): ?>
                        <option value="<?php echo h($template['id']); ?>"><?php echo h($template['name']); ?></option>
                    <?php endforeach; ?>
                </select>
                <label>Archivo CSV</label>
                <input type="file" name="csv" accept=".csv,text/csv" required>
                <label>Columna de correo opcional</label>
                <input name="email_column" placeholder="correo, email o mail">
                <p class="muted">Todas las columnas del CSV se enviaran como variables del TemplateModel.</p>
                <button class="btn" type="submit">Cargar pendientes</button>
            </form>
        </section>
        <?php
    });
}

function manual_route(): void
{
    Auth::requireEdit();
    $templates = Templates::active();
    $selectedId = (int) ($_POST['template_id'] ?? $_GET['template_id'] ?? ($templates[0]['id'] ?? 0));
    $template = $selectedId ? Templates::find($selectedId) : null;

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!$template) {
            flash('error', 'Selecciona una plantilla valida.');
            redirect_to('/manual');
        }
        $email = trim((string) ($_POST['email'] ?? ''));
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            flash('error', 'Correo invalido.');
            redirect_to('/manual?template_id=' . $selectedId);
        }

        $model = array();
        foreach (Templates::variables($template) as $var) {
            $model[$var] = trim((string) ($_POST['vars'][$var] ?? ''));
        }
        if (!$model && trim((string) ($_POST['json_model'] ?? '')) !== '') {
            $decoded = json_decode((string) $_POST['json_model'], true);
            if (!is_array($decoded)) {
                flash('error', 'El JSON de variables no es valido.');
                redirect_to('/manual?template_id=' . $selectedId);
            }
            $model = $decoded;
        }
        $model['email'] = $email;
        if (!empty($_POST['display_name'])) {
            $model['nombre'] = trim((string) $_POST['display_name']);
        }

        $pdo = Database::pdo();
        $pdo->beginTransaction();
        $user = Auth::user();
        $stmt = $pdo->prepare(
            'INSERT INTO email_batches (source, template_id, total_rows, valid_rows, created_by_uid, created_by_name)
             VALUES (?, ?, 1, 1, ?, ?)'
        );
        $stmt->execute(array('manual', $selectedId, $user['uid'] ?? null, $user['username'] ?? null));
        $batchId = (int) $pdo->lastInsertId();
        $stmt = $pdo->prepare(
            'INSERT INTO email_recipients (batch_id, template_id, email, display_name, status, template_model_json)
             VALUES (?, ?, ?, ?, ?, ?)'
        );
        $stmt->execute(array($batchId, $selectedId, $email, trim((string) ($_POST['display_name'] ?? '')), 'pendiente', json_encode($model, JSON_UNESCAPED_UNICODE)));
        $recipientId = (int) $pdo->lastInsertId();
        $pdo->commit();

        Audit::log('manual_create', 'email_recipients', (string) $recipientId, array('template_id' => $selectedId));
        flash('success', 'Registro manual creado como pendiente.');
        redirect_to('/send');
    }

    render('Ingreso manual', function () use ($templates, $selectedId, $template): void {
        $variables = $template ? Templates::variables($template) : array();
        ?>
        <section class="panel">
            <h1>Ingreso manual</h1>
            <form method="get" class="inline">
                <div>
                    <label>Plantilla</label>
                    <select name="template_id" onchange="this.form.submit()">
                        <?php foreach ($templates as $item): ?>
                            <option value="<?php echo h($item['id']); ?>" <?php echo ((int) $item['id'] === $selectedId) ? 'selected' : ''; ?>><?php echo h($item['name']); ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
            </form>
            <form method="post">
                <?php echo csrf_field(); ?>
                <input type="hidden" name="template_id" value="<?php echo h($selectedId); ?>">
                <label>Correo destinatario</label>
                <input name="email" type="email" required>
                <label>Nombre visible opcional</label>
                <input name="display_name">

                <?php if ($variables): ?>
                    <h2>Variables</h2>
                    <?php foreach ($variables as $var): ?>
                        <label><?php echo h($var); ?></label>
                        <input name="vars[<?php echo h($var); ?>]">
                    <?php endforeach; ?>
                <?php else: ?>
                    <label>Variables JSON</label>
                    <textarea name="json_model" placeholder='{"Fecha":"2026-06-27","Nombre del Diplomado":"Diplomado"}'></textarea>
                <?php endif; ?>

                <button class="btn" type="submit">Guardar pendiente</button>
            </form>
        </section>
        <?php
    });
}

function send_route(): void
{
    Auth::requireSend();
    $pdo = Database::pdo();

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $ids = array_filter(array_map('intval', $_POST['recipient_ids'] ?? array()));
        if (!$ids && ($_POST['send_all'] ?? '') === '1') {
            $ids = $pdo->query("SELECT id FROM email_recipients WHERE status = 'pendiente'")->fetchAll(PDO::FETCH_COLUMN);
        }
        if (!$ids) {
            flash('warning', 'No hay registros seleccionados para enviar.');
            redirect_to('/send');
        }

        $sent = 0;
        $errors = 0;
        $client = new PostmarkClient();
        $stmtSelect = $pdo->prepare(
            'SELECT r.*, t.name template_name, t.postmark_template_key
             FROM email_recipients r INNER JOIN templates t ON t.id = r.template_id
             WHERE r.id = ? AND r.status = \'pendiente\''
        );
        $stmtMsg = $pdo->prepare(
            'INSERT INTO email_messages (recipient_id, postmark_message_id, message_stream, status, response_json, sent_by_uid, sent_by_name)
             VALUES (?, ?, ?, ?, ?, ?, ?)'
        );
        $stmtUpdate = $pdo->prepare('UPDATE email_recipients SET status = ?, error_message = ? WHERE id = ?');
        $user = Auth::user();

        foreach ($ids as $id) {
            $stmtSelect->execute(array($id));
            $recipient = $stmtSelect->fetch();
            if (!$recipient) {
                continue;
            }
            $template = array(
                'postmark_template_key' => $recipient['postmark_template_key'],
                'name' => $recipient['template_name'],
            );
            try {
                [$response, $stream] = $client->sendTemplate($template, $recipient);
                $messageId = $response['MessageID'] ?? null;
                $stmtMsg->execute(array($id, $messageId, $stream, 'enviado', json_encode($response, JSON_UNESCAPED_UNICODE), $user['uid'] ?? null, $user['username'] ?? null));
                $stmtUpdate->execute(array('enviado', null, $id));
                $sent++;
            } catch (Throwable $e) {
                $stmtMsg->execute(array($id, null, Settings::get('postmark_message_stream', 'outbound'), 'error', json_encode(array('error' => $e->getMessage()), JSON_UNESCAPED_UNICODE), $user['uid'] ?? null, $user['username'] ?? null));
                $stmtUpdate->execute(array('error', $e->getMessage(), $id));
                $errors++;
            }
        }
        Audit::log('send_pending', 'email_recipients', null, array('requested' => count($ids), 'sent' => $sent, 'errors' => $errors));
        flash($errors ? 'warning' : 'success', 'Envio terminado. Enviados: ' . $sent . ', errores: ' . $errors . '.');
        redirect_to('/send');
    }

    $pending = $pdo->query(
        'SELECT r.*, t.name template_name
         FROM email_recipients r INNER JOIN templates t ON t.id = r.template_id
         WHERE r.status = \'pendiente\'
         ORDER BY r.created_at ASC
         LIMIT 100'
    )->fetchAll();

    render('Enviar pendientes', function () use ($pending): void {
        ?>
        <section class="panel">
            <h1>Enviar pendientes</h1>
            <form method="post">
                <?php echo csrf_field(); ?>
                <?php if (!$pending): ?>
                    <p class="muted">No hay registros pendientes.</p>
                <?php else: ?>
                    <table>
                        <thead><tr><th></th><th>Correo</th><th>Plantilla</th><th>Fecha</th></tr></thead>
                        <tbody>
                        <?php foreach ($pending as $row): ?>
                            <tr>
                                <td><input style="width:auto" type="checkbox" name="recipient_ids[]" value="<?php echo h($row['id']); ?>"></td>
                                <td><?php echo h($row['email']); ?></td>
                                <td><?php echo h($row['template_name']); ?></td>
                                <td><?php echo h($row['created_at']); ?></td>
                            </tr>
                        <?php endforeach; ?>
                        </tbody>
                    </table>
                    <p class="muted">Se muestran los primeros 100 pendientes.</p>
                    <button class="btn" type="submit">Enviar seleccionados</button>
                    <button class="btn secondary" type="submit" name="send_all" value="1">Enviar todos los pendientes</button>
                <?php endif; ?>
            </form>
        </section>
        <?php
    });
}

function history_route(): void
{
    Auth::requireLogin();
    if (!Auth::canView()) {
        http_response_code(403);
        echo 'Sin permiso';
        return;
    }

    $month = $_GET['month'] ?? date('m');
    $year = $_GET['year'] ?? date('Y');
    $status = trim((string) ($_GET['status'] ?? ''));
    $email = trim((string) ($_GET['email'] ?? ''));
    $templateId = (int) ($_GET['template_id'] ?? 0);
    $perPage = 50;
    $page = max(1, (int) ($_GET['page'] ?? 1));

    $where = array('MONTH(r.created_at) = ?', 'YEAR(r.created_at) = ?');
    $params = array($month, $year);
    if ($status !== '') {
        $where[] = 'r.status = ?';
        $params[] = $status;
    }
    if ($email !== '') {
        $where[] = 'r.email LIKE ?';
        $params[] = '%' . $email . '%';
    }
    if ($templateId > 0) {
        $where[] = 'r.template_id = ?';
        $params[] = $templateId;
    }

    $stmtTotal = Database::pdo()->prepare(
        'SELECT COUNT(*)
         FROM email_recipients r
         WHERE ' . implode(' AND ', $where)
    );
    $stmtTotal->execute($params);
    $total = (int) $stmtTotal->fetchColumn();
    $page = min($page, max(1, (int) ceil($total / $perPage)));
    $offset = ($page - 1) * $perPage;

    $stmt = Database::pdo()->prepare(
        'SELECT r.*, t.name template_name,
            (SELECT m.postmark_message_id
             FROM email_messages m
             WHERE m.recipient_id = r.id
             ORDER BY m.sent_at DESC
             LIMIT 1) postmark_message_id
         FROM email_recipients r
         INNER JOIN templates t ON t.id = r.template_id
         WHERE ' . implode(' AND ', $where) . '
         ORDER BY r.created_at DESC
         LIMIT ? OFFSET ?'
    );
    foreach (array_values($params) as $idx => $value) {
        $stmt->bindValue($idx + 1, $value);
    }
    $stmt->bindValue(count($params) + 1, $perPage, PDO::PARAM_INT);
    $stmt->bindValue(count($params) + 2, $offset, PDO::PARAM_INT);
    $stmt->execute();
    $rows = $stmt->fetchAll();
    $templates = Templates::all();

    render('Historial', function () use ($rows, $templates, $month, $year, $status, $email, $templateId, $page, $total, $perPage): void {
        $statuses = array('', 'pendiente', 'enviado', 'procesado', 'entregado', 'abierto', 'click', 'rebote', 'error');
        $paginationQuery = array(
            'month' => $month,
            'year' => $year,
            'status' => $status,
            'template_id' => $templateId,
            'email' => $email,
        );
        ?>
        <section class="panel">
            <h1>Historial</h1>
            <form method="get" class="inline">
                <div><label>Mes</label><input name="month" value="<?php echo h($month); ?>" maxlength="2"></div>
                <div><label>Anio</label><input name="year" value="<?php echo h($year); ?>" maxlength="4"></div>
                <div><label>Estado</label><select name="status">
                    <?php foreach ($statuses as $item): ?><option value="<?php echo h($item); ?>" <?php echo $status === $item ? 'selected' : ''; ?>><?php echo h($item ?: 'Todos'); ?></option><?php endforeach; ?>
                </select></div>
                <div><label>Plantilla</label><select name="template_id">
                    <option value="0">Todas</option>
                    <?php foreach ($templates as $template): ?><option value="<?php echo h($template['id']); ?>" <?php echo $templateId === (int) $template['id'] ? 'selected' : ''; ?>><?php echo h($template['name']); ?></option><?php endforeach; ?>
                </select></div>
                <div><label>Correo</label><input name="email" value="<?php echo h($email); ?>"></div>
                <button class="btn" type="submit">Filtrar</button>
            </form>
        </section>
        <section class="panel">
            <table>
                <thead><tr><th>Fecha</th><th>Correo</th><th>Plantilla</th><th>Estado</th><th>MessageID</th><th></th></tr></thead>
                <tbody>
                <?php foreach ($rows as $row): ?>
                    <tr>
                        <td><?php echo h($row['created_at']); ?></td>
                        <td><?php echo h($row['email']); ?></td>
                        <td><?php echo h($row['template_name']); ?></td>
                        <td><span class="status <?php echo h($row['status']); ?>"><?php echo h($row['status']); ?></span></td>
                        <td><?php echo h($row['postmark_message_id']); ?></td>
                        <td><a href="<?php echo h(url('/message?id=' . $row['id'])); ?>">Detalle</a></td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
            <?php echo pagination_links('/history', $page, $total, $perPage, $paginationQuery); ?>
        </section>
        <?php
    });
}

function message_route(): void
{
    Auth::requireLogin();
    $id = (int) ($_GET['id'] ?? 0);
    $stmt = Database::pdo()->prepare(
        'SELECT r.*, t.name template_name FROM email_recipients r INNER JOIN templates t ON t.id = r.template_id WHERE r.id = ?'
    );
    $stmt->execute(array($id));
    $recipient = $stmt->fetch();
    if (!$recipient) {
        not_found_route();
        return;
    }

    $stmt = Database::pdo()->prepare('SELECT * FROM email_messages WHERE recipient_id = ? ORDER BY sent_at DESC');
    $stmt->execute(array($id));
    $messages = $stmt->fetchAll();

    $stmt = Database::pdo()->prepare('SELECT * FROM email_events WHERE recipient_id = ? ORDER BY COALESCE(event_at, created_at) DESC');
    $stmt->execute(array($id));
    $events = $stmt->fetchAll();
    Audit::log('message_view', 'email_recipients', (string) $id);

    render('Detalle', function () use ($recipient, $messages, $events): void {
        ?>
        <section class="panel">
            <h1>Detalle del correo</h1>
            <p><strong>Correo:</strong> <?php echo h($recipient['email']); ?></p>
            <p><strong>Plantilla:</strong> <?php echo h($recipient['template_name']); ?></p>
            <p><strong>Estado:</strong> <span class="status <?php echo h($recipient['status']); ?>"><?php echo h($recipient['status']); ?></span></p>
            <details><summary>TemplateModel</summary><pre><?php echo h(pretty_json($recipient['template_model_json'])); ?></pre></details>
        </section>
        <section class="panel">
            <h2>Mensajes</h2>
            <table><thead><tr><th>Fecha</th><th>MessageID</th><th>Stream</th><th>Estado</th><th>Respuesta</th></tr></thead><tbody>
            <?php foreach ($messages as $message): ?>
                <tr>
                    <td><?php echo h($message['sent_at']); ?></td>
                    <td><?php echo h($message['postmark_message_id']); ?></td>
                    <td><?php echo h($message['message_stream']); ?></td>
                    <td><?php echo h($message['status']); ?></td>
                    <td><details><summary>JSON</summary><pre><?php echo h(pretty_json($message['response_json'])); ?></pre></details></td>
                </tr>
            <?php endforeach; ?>
            </tbody></table>
        </section>
        <section class="panel">
            <h2>Eventos</h2>
            <table><thead><tr><th>Fecha</th><th>Evento</th><th>Estado</th><th>JSON</th></tr></thead><tbody>
            <?php foreach ($events as $event): ?>
                <tr>
                    <td><?php echo h($event['event_at'] ?: $event['created_at']); ?></td>
                    <td><?php echo h($event['event_type']); ?></td>
                    <td><span class="status <?php echo h($event['event_status']); ?>"><?php echo h($event['event_status']); ?></span></td>
                    <td><details><summary>Ver JSON</summary><pre><?php echo h(pretty_json($event['raw_json'])); ?></pre></details></td>
                </tr>
            <?php endforeach; ?>
            </tbody></table>
        </section>
        <?php
    });
}

function postmark_webhook_route(string $secret): void
{
    $expected = Settings::get('webhook_secret', '');
    if ($expected === '' || !hash_equals($expected, $secret)) {
        json_response(array('error' => 'forbidden'), 403);
    }

    $raw = file_get_contents('php://input') ?: '';
    $payload = json_decode($raw, true);
    if (!is_array($payload)) {
        json_response(array('error' => 'invalid_json'), 400);
    }

    $messageId = (string) ($payload['MessageID'] ?? $payload['MessageId'] ?? '');
    $recordType = (string) ($payload['RecordType'] ?? $payload['Type'] ?? 'unknown');
    $status = StatusMapper::fromPostmarkEvent($recordType);
    $eventAt = $payload['ReceivedAt'] ?? $payload['DeliveredAt'] ?? $payload['BouncedAt'] ?? $payload['ClickedAt'] ?? $payload['OpenedAt'] ?? null;

    $recipientId = null;
    $storedStream = null;
    if ($messageId !== '') {
        $stmt = Database::pdo()->prepare('SELECT recipient_id, message_stream FROM email_messages WHERE postmark_message_id = ? ORDER BY id DESC LIMIT 1');
        $stmt->execute(array($messageId));
        $messageRow = $stmt->fetch();
        if ($messageRow) {
            $recipientId = $messageRow['recipient_id'] ?: null;
            $storedStream = $messageRow['message_stream'] ?: null;
        }
    }

    $acceptedStream = trim(Settings::get('webhook_message_stream_filter', ''));
    $payloadStream = webhook_payload_stream($payload);
    if ($acceptedStream !== '') {
        $streamToCheck = $payloadStream !== '' ? $payloadStream : (string) $storedStream;
        if ($streamToCheck === '' || strcasecmp($streamToCheck, $acceptedStream) !== 0) {
            json_response(array('ok' => true, 'ignored' => true, 'reason' => 'message_stream_mismatch'));
        }
    }

    $stmt = Database::pdo()->prepare(
        'INSERT INTO email_events (postmark_message_id, recipient_id, event_type, event_status, event_at, raw_json)
         VALUES (?, ?, ?, ?, ?, ?)'
    );
    $stmt->execute(array($messageId ?: null, $recipientId, $recordType, $status, $eventAt ? date('Y-m-d H:i:s', strtotime((string) $eventAt)) : null, json_encode($payload, JSON_UNESCAPED_UNICODE)));

    if ($recipientId) {
        $stmt = Database::pdo()->prepare('UPDATE email_recipients SET status = ? WHERE id = ?');
        $stmt->execute(array($status, $recipientId));
        $stmt = Database::pdo()->prepare('UPDATE email_messages SET status = ? WHERE postmark_message_id = ?');
        $stmt->execute(array($status, $messageId));
    }

    Audit::log('postmark_webhook', 'email_messages', $messageId ?: null, array('event' => $recordType, 'status' => $status));
    json_response(array('ok' => true));
}

function webhook_payload_stream(array $payload): string
{
    foreach (array('MessageStream', 'MessageStreamID', 'MessageStreamId', 'MessageStreamName') as $key) {
        if (!empty($payload[$key])) {
            return trim((string) $payload[$key]);
        }
    }
    return '';
}

function admin_logs_route(): void
{
    Auth::requireAdmin();

    $date = trim((string) ($_GET['date'] ?? ''));
    $username = trim((string) ($_GET['username'] ?? ''));
    $action = trim((string) ($_GET['action'] ?? ''));
    $entity = trim((string) ($_GET['entity'] ?? ''));
    $perPage = 50;
    $page = max(1, (int) ($_GET['page'] ?? 1));

    $where = array('1 = 1');
    $params = array();
    if ($date !== '') {
        $where[] = 'DATE(created_at) = ?';
        $params[] = $date;
    }
    if ($username !== '') {
        $where[] = 'username LIKE ?';
        $params[] = '%' . $username . '%';
    }
    if ($action !== '') {
        $where[] = 'action LIKE ?';
        $params[] = '%' . $action . '%';
    }
    if ($entity !== '') {
        $where[] = "CONCAT(COALESCE(entity_type, ''), ' ', COALESCE(entity_id, '')) LIKE ?";
        $params[] = '%' . $entity . '%';
    }

    $stmtTotal = Database::pdo()->prepare('SELECT COUNT(*) FROM audit_logs WHERE ' . implode(' AND ', $where));
    $stmtTotal->execute($params);
    $total = (int) $stmtTotal->fetchColumn();
    $page = min($page, max(1, (int) ceil($total / $perPage)));
    $offset = ($page - 1) * $perPage;

    $stmt = Database::pdo()->prepare(
        'SELECT * FROM audit_logs
         WHERE ' . implode(' AND ', $where) . '
         ORDER BY created_at DESC
         LIMIT ? OFFSET ?'
    );
    foreach (array_values($params) as $idx => $value) {
        $stmt->bindValue($idx + 1, $value);
    }
    $stmt->bindValue(count($params) + 1, $perPage, PDO::PARAM_INT);
    $stmt->bindValue(count($params) + 2, $offset, PDO::PARAM_INT);
    $stmt->execute();
    $rows = $stmt->fetchAll();

    render('Logs', function () use ($rows, $date, $username, $action, $entity, $page, $total, $perPage): void {
        $paginationQuery = array(
            'date' => $date,
            'username' => $username,
            'action' => $action,
            'entity' => $entity,
        );
        ?>
        <section class="panel">
            <h1>Logs de auditoria</h1>
            <form method="get" class="inline">
                <div><label>Fecha</label><input type="date" name="date" value="<?php echo h($date); ?>"></div>
                <div><label>Usuario</label><input name="username" value="<?php echo h($username); ?>"></div>
                <div><label>Accion</label><input name="action" value="<?php echo h($action); ?>"></div>
                <div><label>Entidad</label><input name="entity" value="<?php echo h($entity); ?>"></div>
                <button class="btn" type="submit">Filtrar</button>
                <a class="btn secondary" href="<?php echo h(url('/admin/logs')); ?>">Limpiar</a>
            </form>
        </section>
        <section class="panel">
            <table>
                <thead><tr><th>Fecha</th><th>Usuario</th><th>Accion</th><th>Entidad</th><th>Metadata</th></tr></thead>
                <tbody>
                <?php foreach ($rows as $row): ?>
                    <tr>
                        <td><?php echo h($row['created_at']); ?></td>
                        <td><?php echo h($row['username']); ?></td>
                        <td><?php echo h($row['action']); ?></td>
                        <td><?php echo h(trim(($row['entity_type'] ?? '') . ' ' . ($row['entity_id'] ?? ''))); ?></td>
                        <td><details><summary>JSON</summary><pre><?php echo h(pretty_json($row['metadata_json'])); ?></pre></details></td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
            <?php echo pagination_links('/admin/logs', $page, $total, $perPage, $paginationQuery); ?>
        </section>
        <?php
    });
}

function not_found_route(): void
{
    http_response_code(404);
    render('No encontrado', function (): void {
        echo '<section class="panel"><h1>No encontrado</h1><p>La pagina solicitada no existe.</p></section>';
    });
}
