<?php
require __DIR__ . '/bootstrap.php';

use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Database;
use App\Core\Response;
use App\Core\Security;

$user = Auth::requireUser();
$canRead = Auth::hasPermission('notifications.manage', $user)
    || Auth::hasPermission('walks.manage', $user)
    || Auth::hasPermission('walks.edit', $user);
if (!$canRead) {
    Response::error('You do not have permission to access push notification presets.', 403);
}

$pdo = Database::connection();
$input = request_input();
$action = (string)($input['action'] ?? 'list');
$method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));

function pyrk_push_preset_can_manage(array $user): bool
{
    return Auth::hasPermission('notifications.manage', $user)
        || Auth::hasPermission('walks.manage', $user)
        || Auth::hasPermission('walks.edit', $user);
}

function pyrk_push_preset_require_manage(array $user): void
{
    if (!pyrk_push_preset_can_manage($user)) {
        Response::error('You do not have permission to manage push notification presets.', 403);
    }
}

function pyrk_push_preset_table_exists(PDO $pdo): bool
{
    try {
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?');
        $stmt->execute(['push_notification_presets']);
        return (int)$stmt->fetchColumn() > 0;
    } catch (Throwable) {
        return false;
    }
}

function pyrk_push_preset_use_cases(): array
{
    return [
        'general' => 'General announcement',
        'open_walk' => 'New open walk',
        'invitation_only' => 'Invitation-only walk',
        'walk_update' => 'Walk update',
        'walk_reminder_24h' => '24-hour walk reminder',
        'walk_same_day' => 'Same-day walk reminder',
        'walk_bring_items' => 'Bring items reminder',
        'walk_photo' => 'Photo contest reminder',
        'custom' => 'Custom',
    ];
}

function pyrk_push_preset_allowed_tokens(): array
{
    return [
        '{first_name}' => 'Member first name',
        '{last_name}' => 'Member last name',
        '{full_name}' => 'Member full name',
        '{walk_title}' => 'Walk title',
        '{walk_date}' => 'Walk date',
        '{walk_time}' => 'Walk start time',
        '{route_name}' => 'Route name',
        '{distance_km}' => 'Route distance in KM',
        '{walk_link}' => 'Direct walk link',
    ];
}

function pyrk_push_preset_tokens(string $value): array
{
    preg_match_all('/\{[a-zA-Z0-9_]+\}|\$[a-zA-Z0-9_]+\$/', $value, $matches);
    return array_values(array_unique($matches[0] ?? []));
}

function pyrk_push_preset_unknown_tokens(string $value): array
{
    return array_values(array_diff(pyrk_push_preset_tokens($value), array_keys(pyrk_push_preset_allowed_tokens())));
}

function pyrk_push_preset_clean_text(string $value, int $maxLength): string
{
    $value = trim(str_replace(["\r\n", "\r"], "\n", $value));
    if (function_exists('mb_substr')) return mb_substr($value, 0, $maxLength, 'UTF-8');
    return substr($value, 0, $maxLength);
}

function pyrk_push_preset_rows(PDO $pdo, bool $activeOnly = false): array
{
    $where = $activeOnly ? 'WHERE is_active = 1' : '';
    $stmt = $pdo->query("SELECT id, preset_key, preset_name, use_case, subject_template, message_template, is_default, is_active, sort_order, created_by, updated_by, created_at, updated_at
        FROM push_notification_presets
        {$where}
        ORDER BY is_active DESC, sort_order ASC, preset_name ASC, id ASC");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function pyrk_push_preset_preview(string $subject, string $message): array
{
    $sample = [
        '{first_name}' => 'Alex',
        '{last_name}' => 'Walker',
        '{full_name}' => 'Alex Walker',
        '{walk_title}' => 'Beirut Sunrise Walk',
        '{walk_date}' => date('d/m/Y', strtotime('+7 days')),
        '{walk_time}' => '07:00',
        '{route_name}' => 'Pyrk Waterfront Route',
        '{distance_km}' => '6.4',
        '{walk_link}' => 'https://thepyrk.com/steps/public/walk.php?id=123',
    ];
    return [
        'subject' => strtr($subject, $sample),
        'message' => strtr($message, $sample),
    ];
}

if ($action === 'list' || $action === 'overview') {
    $installed = pyrk_push_preset_table_exists($pdo);
    Response::ok([
        'installed' => $installed ? 1 : 0,
        'presets' => $installed ? pyrk_push_preset_rows($pdo, !empty($input['active_only'])) : [],
        'use_cases' => pyrk_push_preset_use_cases(),
        'variables' => pyrk_push_preset_allowed_tokens(),
        'migration' => 'database/migrations/044_push_notification_presets.sql',
    ]);
}

if (!pyrk_push_preset_table_exists($pdo)) {
    Response::error('Push notification presets are not installed. Import database/migrations/044_push_notification_presets.sql first.');
}

if ($action === 'get') {
    $id = (int)($input['id'] ?? 0);
    if ($id <= 0) Response::error('Choose a preset.');
    $stmt = $pdo->prepare('SELECT * FROM push_notification_presets WHERE id = ? LIMIT 1');
    $stmt->execute([$id]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$row) Response::error('Push notification preset not found.', 404);
    Response::ok(['preset' => $row, 'preview' => pyrk_push_preset_preview((string)$row['subject_template'], (string)$row['message_template'])]);
}

if (in_array($action, ['save','duplicate','toggle','delete'], true)) {
    if ($method !== 'POST') Response::error('This action requires POST.', 405);
    pyrk_push_preset_require_manage($user);
    Csrf::validateFromRequest();
}

if ($action === 'save') {
    $id = (int)($input['id'] ?? 0);
    $isNew = $id <= 0;
    $name = pyrk_push_preset_clean_text((string)($input['preset_name'] ?? ''), 120);
    $useCase = (string)($input['use_case'] ?? 'custom');
    $subject = pyrk_push_preset_clean_text((string)($input['subject_template'] ?? ''), 180);
    $message = pyrk_push_preset_clean_text((string)($input['message_template'] ?? ''), 4000);
    $isDefault = !empty($input['is_default']) ? 1 : 0;
    $isActive = array_key_exists('is_active', $input) ? (!empty($input['is_active']) ? 1 : 0) : 1;
    $sortOrder = max(0, min(9999, (int)($input['sort_order'] ?? 100)));

    if ($name === '') Response::error('Preset name is required.');
    if (!isset(pyrk_push_preset_use_cases()[$useCase])) Response::error('Choose a valid preset use case.');
    if ($subject === '') Response::error('Notification subject is required.');
    if ($message === '') Response::error('Notification message is required.');
    if ($isDefault && !$isActive) Response::error('A default preset must be active.');

    $unknown = array_values(array_unique(array_merge(
        pyrk_push_preset_unknown_tokens($subject),
        pyrk_push_preset_unknown_tokens($message)
    )));
    if ($unknown) Response::error('Unknown variable(s): ' . implode(', ', $unknown) . '. Use only the approved variables shown in the editor.');

    $pdo->beginTransaction();
    try {
        if ($id > 0) {
            $exists = $pdo->prepare('SELECT id FROM push_notification_presets WHERE id = ? FOR UPDATE');
            $exists->execute([$id]);
            if (!$exists->fetchColumn()) throw new RuntimeException('Push notification preset not found.');
            $stmt = $pdo->prepare('UPDATE push_notification_presets
                SET preset_name = ?, use_case = ?, subject_template = ?, message_template = ?, is_default = ?, is_active = ?, sort_order = ?, updated_by = ?, updated_at = NOW()
                WHERE id = ?');
            $stmt->execute([$name, $useCase, $subject, $message, $isDefault, $isActive, $sortOrder, (int)$user['id'], $id]);
        } else {
            $stmt = $pdo->prepare('INSERT INTO push_notification_presets
                (preset_key, preset_name, use_case, subject_template, message_template, is_default, is_active, sort_order, created_by, updated_by, created_at, updated_at)
                VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
            $stmt->execute([$name, $useCase, $subject, $message, $isDefault, $isActive, $sortOrder, (int)$user['id'], (int)$user['id']]);
            $id = (int)$pdo->lastInsertId();
        }
        if ($isDefault) {
            $stmt = $pdo->prepare('UPDATE push_notification_presets SET is_default = 0, updated_at = NOW() WHERE use_case = ? AND id <> ?');
            $stmt->execute([$useCase, $id]);
        }
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage() ?: 'Could not save the push notification preset.');
    }

    try {
        Security::audit((int)$user['id'], $isNew ? 'push_preset_created' : 'push_preset_updated', 'push_notification_presets', $id, [
            'preset_name' => $name, 'use_case' => $useCase, 'is_default' => $isDefault, 'is_active' => $isActive,
        ]);
    } catch (Throwable) {}

    $stmt = $pdo->prepare('SELECT * FROM push_notification_presets WHERE id = ? LIMIT 1');
    $stmt->execute([$id]);
    Response::ok(['preset' => $stmt->fetch(PDO::FETCH_ASSOC), 'presets' => pyrk_push_preset_rows($pdo)], 'Push notification preset saved.');
}

if ($action === 'duplicate') {
    $id = (int)($input['id'] ?? 0);
    if ($id <= 0) Response::error('Choose a preset to duplicate.');
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('SELECT * FROM push_notification_presets WHERE id = ? FOR UPDATE');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) throw new RuntimeException('Push notification preset not found.');
        $name = pyrk_push_preset_clean_text((string)$row['preset_name'] . ' (Copy)', 120);
        $insert = $pdo->prepare('INSERT INTO push_notification_presets
            (preset_key, preset_name, use_case, subject_template, message_template, is_default, is_active, sort_order, created_by, updated_by, created_at, updated_at)
            VALUES (NULL, ?, ?, ?, ?, 0, ?, ?, ?, ?, NOW(), NOW())');
        $insert->execute([$name, (string)$row['use_case'], (string)$row['subject_template'], (string)$row['message_template'], (int)$row['is_active'], min(9999, (int)$row['sort_order'] + 1), (int)$user['id'], (int)$user['id']]);
        $newId = (int)$pdo->lastInsertId();
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage() ?: 'Could not duplicate the push notification preset.');
    }
    try { Security::audit((int)$user['id'], 'push_preset_duplicated', 'push_notification_presets', $newId, ['source_id' => $id]); } catch (Throwable) {}
    $stmt = $pdo->prepare('SELECT * FROM push_notification_presets WHERE id = ? LIMIT 1');
    $stmt->execute([$newId]);
    Response::ok(['preset' => $stmt->fetch(PDO::FETCH_ASSOC), 'presets' => pyrk_push_preset_rows($pdo)], 'Push notification preset duplicated.');
}

if ($action === 'toggle') {
    $id = (int)($input['id'] ?? 0);
    $active = !empty($input['is_active']) ? 1 : 0;
    if ($id <= 0) Response::error('Choose a preset.');
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('SELECT id, preset_name, use_case, is_default FROM push_notification_presets WHERE id = ? FOR UPDATE');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) throw new RuntimeException('Push notification preset not found.');
        $update = $pdo->prepare('UPDATE push_notification_presets SET is_active = ?, is_default = IF(? = 0, 0, is_default), updated_by = ?, updated_at = NOW() WHERE id = ?');
        $update->execute([$active, $active, (int)$user['id'], $id]);
        if (!$active && (int)$row['is_default'] === 1) {
            $replacement = $pdo->prepare('SELECT id FROM push_notification_presets WHERE use_case = ? AND is_active = 1 AND id <> ? ORDER BY sort_order ASC, id ASC LIMIT 1');
            $replacement->execute([(string)$row['use_case'], $id]);
            $replacementId = (int)($replacement->fetchColumn() ?: 0);
            if ($replacementId > 0) {
                $pdo->prepare('UPDATE push_notification_presets SET is_default = 1, updated_by = ?, updated_at = NOW() WHERE id = ?')->execute([(int)$user['id'], $replacementId]);
            }
        }
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage() ?: 'Could not change preset status.');
    }
    try { Security::audit((int)$user['id'], $active ? 'push_preset_activated' : 'push_preset_deactivated', 'push_notification_presets', $id, ['preset_name' => (string)($row['preset_name'] ?? '')]); } catch (Throwable) {}
    Response::ok(['presets' => pyrk_push_preset_rows($pdo)], $active ? 'Push notification preset activated.' : 'Push notification preset deactivated.');
}

if ($action === 'delete') {
    $id = (int)($input['id'] ?? 0);
    if ($id <= 0) Response::error('Choose a preset to delete.');
    $pdo->beginTransaction();
    try {
        $stmt = $pdo->prepare('SELECT id, preset_name, use_case, is_default FROM push_notification_presets WHERE id = ? FOR UPDATE');
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) throw new RuntimeException('Push notification preset not found.');
        $pdo->prepare('DELETE FROM push_notification_presets WHERE id = ?')->execute([$id]);
        if ((int)$row['is_default'] === 1) {
            $replacement = $pdo->prepare('SELECT id FROM push_notification_presets WHERE use_case = ? AND is_active = 1 ORDER BY sort_order ASC, id ASC LIMIT 1');
            $replacement->execute([(string)$row['use_case']]);
            $replacementId = (int)($replacement->fetchColumn() ?: 0);
            if ($replacementId > 0) {
                $pdo->prepare('UPDATE push_notification_presets SET is_default = 1, updated_by = ?, updated_at = NOW() WHERE id = ?')->execute([(int)$user['id'], $replacementId]);
            }
        }
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error($e->getMessage() ?: 'Could not delete the push notification preset.');
    }
    try { Security::audit((int)$user['id'], 'push_preset_deleted', 'push_notification_presets', $id, ['preset_name' => (string)$row['preset_name'], 'use_case' => (string)$row['use_case']]); } catch (Throwable) {}
    Response::ok(['presets' => pyrk_push_preset_rows($pdo)], 'Push notification preset deleted. Existing notifications and histories were not changed.');
}

if ($action === 'preview') {
    $subject = pyrk_push_preset_clean_text((string)($input['subject_template'] ?? ''), 180);
    $message = pyrk_push_preset_clean_text((string)($input['message_template'] ?? ''), 4000);
    $unknown = array_values(array_unique(array_merge(pyrk_push_preset_unknown_tokens($subject), pyrk_push_preset_unknown_tokens($message))));
    if ($unknown) Response::error('Unknown variable(s): ' . implode(', ', $unknown) . '.');
    Response::ok(['preview' => pyrk_push_preset_preview($subject, $message)]);
}

Response::error('Unsupported push notification preset action.', 404);
