<?php
declare(strict_types=1);

require_once __DIR__ . '/admin_bootstrap.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/file_api_client.php';

$pdo = admin_get_pdo();
$auth = admin_require_auth($pdo);

$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';

function admin_chat_select_sql(): string
{
    return 'SELECT '
    . 'm.id, m.user_id, m.username, m.message, m.created_at, m.updated_at, UNIX_TIMESTAMP(m.updated_at) AS updated_at_ts, '
        . 'm.reply_to_id, '
        . 'm.is_hearted, m.hearted_by_user_id, m.hearted_by_username, m.hearted_at, '
        . '(m.file_name IS NOT NULL AND m.file_name <> \'\') AS has_file, m.file_name, m.file_mime, m.file_size, '
        . 'r.username AS reply_username, r.message AS reply_message, r.created_at AS reply_created_at '
        . 'FROM admin_chat_messages m '
        . 'LEFT JOIN admin_chat_messages r ON r.id = m.reply_to_id ';
}

function admin_chat_normalize_row(array $row): array
{
    $row['id'] = isset($row['id']) ? (int)$row['id'] : 0;
    $row['user_id'] = isset($row['user_id']) ? (int)$row['user_id'] : 0;
    $row['has_file'] = (bool)((int)($row['has_file'] ?? 0));
    $row['reply_to_id'] = isset($row['reply_to_id']) ? (int)$row['reply_to_id'] : null;
    $row['file_size'] = isset($row['file_size']) && $row['file_size'] !== null ? (int)$row['file_size'] : null;
    $row['updated_at_ts'] = isset($row['updated_at_ts']) && $row['updated_at_ts'] !== null ? (int)$row['updated_at_ts'] : null;
    $row['is_hearted'] = (bool)((int)($row['is_hearted'] ?? 0));
    $row['hearted_by_user_id'] = isset($row['hearted_by_user_id']) && $row['hearted_by_user_id'] !== null ? (int)$row['hearted_by_user_id'] : null;
    $row['hearted_by_username'] = isset($row['hearted_by_username']) && $row['hearted_by_username'] !== null ? (string)$row['hearted_by_username'] : null;

    return $row;
}

function admin_chat_normalize_rows(array $rows): array
{
    foreach ($rows as $k => $r) {
        if (is_array($r)) {
            $rows[$k] = admin_chat_normalize_row($r);
        }
    }
    return $rows;
}

if ($method === 'GET') {
    $beforeId = isset($_GET['before_id']) ? (int)$_GET['before_id'] : 0;
    $afterId = isset($_GET['after_id']) ? (int)$_GET['after_id'] : 0;
    $updatedAfter = isset($_GET['updated_after']) ? (int)$_GET['updated_after'] : 0;
    $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 100;
    $limit = max(1, min(200, $limit));

    try {
        if ($afterId > 0) {
            // Nowe wiadomości (do dopisywania na dole)
            $stmt = $pdo->prepare(
                admin_chat_select_sql()
                . 'WHERE m.id > :after_id '
                . 'ORDER BY m.id ASC '
                . 'LIMIT :limit'
            );
            $stmt->bindValue(':after_id', $afterId, PDO::PARAM_INT);
            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
            $stmt->execute();
            $rows = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));

            admin_json_response([
                'success' => true,
                'data' => $rows,
                'count' => count($rows),
            ]);
        }

        if ($updatedAfter > 0) {
            // Zmienione (edytowane) wiadomości do odświeżenia w UI
            $stmt = $pdo->prepare(
                admin_chat_select_sql()
                . 'WHERE m.updated_at IS NOT NULL AND m.updated_at > FROM_UNIXTIME(:updated_after) '
                . 'ORDER BY m.id ASC '
                . 'LIMIT :limit'
            );
            $stmt->bindValue(':updated_after', $updatedAfter, PDO::PARAM_INT);
            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
            $stmt->execute();
            $rows = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));

            admin_json_response([
                'success' => true,
                'data' => $rows,
                'count' => count($rows),
            ]);
        }

        if ($beforeId > 0) {
            // Starsze wiadomości (do wczytywania przy scrollu w górę)
            $stmt = $pdo->prepare(
                admin_chat_select_sql()
                . 'WHERE m.id < :before_id '
                . 'ORDER BY m.id DESC '
                . 'LIMIT :limit'
            );
            $stmt->bindValue(':before_id', $beforeId, PDO::PARAM_INT);
            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
            $stmt->execute();
            $rowsDesc = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));

            admin_json_response([
                'success' => true,
                'data' => $rowsDesc,
                'count' => count($rowsDesc),
                'hasMore' => count($rowsDesc) === $limit,
            ]);
        }

        // Początkowe wczytanie: 100 najnowszych
        $stmt = $pdo->prepare(
            admin_chat_select_sql()
            . 'ORDER BY m.id DESC '
            . 'LIMIT :limit'
        );
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        $rowsDesc = admin_chat_normalize_rows($stmt->fetchAll(PDO::FETCH_ASSOC));

        admin_json_response([
            'success' => true,
            'data' => $rowsDesc,
            'count' => count($rowsDesc),
            'hasMore' => count($rowsDesc) === $limit,
        ]);
    } catch (Throwable $e) {
        $msg = (string)$e->getMessage();
        if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
            admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
        }
        admin_json_error('Błąd pobierania wiadomości', 500);
    }
}

if ($method === 'POST') {
    $contentType = (string)($_SERVER['CONTENT_TYPE'] ?? '');
    $isJson = stripos($contentType, 'application/json') !== false;

    $RECALLED_TEXT = 'Wiadomość cofnięta';

    $message = '';
    $replyToId = null;
    $fileName = null;
    $fileMime = null;
    $fileSize = null;
    $fileData = null;  // fallback BLOB (do usunięcia po pełnej migracji)
    $filePath = null;  // nowa ścieżka dyskowa

    if ($isJson) {
        $payload = admin_read_json_body();
        $action = isset($payload['action']) ? (string)$payload['action'] : '';

        // Recall existing message (revoke own)
        if ($action === 'recall') {
            $id = isset($payload['id']) ? (int)$payload['id'] : 0;
            if ($id <= 0) {
                admin_json_error('Nieprawidłowe id', 422);
            }

            try {
                $stmt = $pdo->prepare('SELECT id, user_id FROM admin_chat_messages WHERE id = :id');
                $stmt->execute([':id' => $id]);
                $cur = $stmt->fetch(PDO::FETCH_ASSOC);
                if (!$cur) {
                    admin_json_error('Nie znaleziono wiadomości', 404);
                }
                if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
                    admin_json_error('Nie możesz cofnąć cudzej wiadomości', 403);
                }

                $up = $pdo->prepare(
                    'UPDATE admin_chat_messages '
                    . 'SET message = :message, file_name = NULL, file_mime = NULL, file_size = NULL, file_path = NULL, updated_at = NOW() '
                    . 'WHERE id = :id'
                );
                $up->execute([':message' => $RECALLED_TEXT, ':id' => $id]);

                $select = $pdo->prepare(
                    admin_chat_select_sql()
                    . 'WHERE m.id = :id LIMIT 1'
                );
                $select->execute([':id' => $id]);
                $row = $select->fetch(PDO::FETCH_ASSOC);
                if (is_array($row)) {
                    $row = admin_chat_normalize_row($row);
                }

                admin_json_response(['success' => true, 'data' => $row]);
            } catch (Throwable $e) {
                $msg = (string)$e->getMessage();
                if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
                    admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
                }
                admin_json_error('Błąd cofania wiadomości', 500);
            }
        }

        // Update existing message (edit own)
        if ($action === 'update') {
            $id = isset($payload['id']) ? (int)$payload['id'] : 0;
            $newMessage = isset($payload['message']) ? trim((string)$payload['message']) : '';

            if ($id <= 0) {
                admin_json_error('Nieprawidłowe id', 422);
            }
            if ($newMessage === '') {
                admin_json_error('Wiadomość nie może być pusta', 422);
            }
            if (mb_strlen($newMessage) > 1500) {
                admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
            }

            try {
                $stmt = $pdo->prepare('SELECT id, user_id, message FROM admin_chat_messages WHERE id = :id');
                $stmt->execute([':id' => $id]);
                $cur = $stmt->fetch(PDO::FETCH_ASSOC);
                if (!$cur) {
                    admin_json_error('Nie znaleziono wiadomości', 404);
                }
                if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
                    admin_json_error('Nie możesz edytować cudzej wiadomości', 403);
                }
                if (((string)($cur['message'] ?? '')) === $RECALLED_TEXT) {
                    admin_json_error('Nie możesz edytować cofniętej wiadomości', 422);
                }

                $up = $pdo->prepare('UPDATE admin_chat_messages SET message = :message, updated_at = NOW() WHERE id = :id');
                $up->execute([':message' => $newMessage, ':id' => $id]);

                $select = $pdo->prepare(
                    admin_chat_select_sql()
                    . 'WHERE m.id = :id LIMIT 1'
                );
                $select->execute([':id' => $id]);
                $row = $select->fetch(PDO::FETCH_ASSOC);
                if (is_array($row)) {
                    $row = admin_chat_normalize_row($row);
                }

                admin_json_response(['success' => true, 'data' => $row]);
            } catch (Throwable $e) {
                $msg = (string)$e->getMessage();
                if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
                    admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
                }
                admin_json_error('Błąd edycji wiadomości', 500);
            }
        }

        if ($action === 'toggle_heart') {
            $id = isset($payload['id']) ? (int)$payload['id'] : 0;
            if ($id <= 0) {
                admin_json_error('Nieprawidłowe id', 422);
            }

            try {
                $stmt = $pdo->prepare('SELECT id, is_hearted FROM admin_chat_messages WHERE id = :id');
                $stmt->execute([':id' => $id]);
                $cur = $stmt->fetch(PDO::FETCH_ASSOC);
                if (!$cur) {
                    admin_json_error('Nie znaleziono wiadomości', 404);
                }

                $isHearted = (int)($cur['is_hearted'] ?? 0) === 1;
                if ($isHearted) {
                    $up = $pdo->prepare(
                        'UPDATE admin_chat_messages '
                        . 'SET is_hearted = 0, hearted_by_user_id = NULL, hearted_by_username = NULL, hearted_at = NULL, updated_at = NOW() '
                        . 'WHERE id = :id'
                    );
                    $up->execute([':id' => $id]);
                } else {
                    $up = $pdo->prepare(
                        'UPDATE admin_chat_messages '
                        . 'SET is_hearted = 1, hearted_by_user_id = :hearted_by_user_id, hearted_by_username = :hearted_by_username, hearted_at = NOW(), updated_at = NOW() '
                        . 'WHERE id = :id'
                    );
                    $up->execute([
                        ':id' => $id,
                        ':hearted_by_user_id' => (int)$auth['user_id'],
                        ':hearted_by_username' => (string)$auth['username'],
                    ]);
                }

                $select = $pdo->prepare(
                    admin_chat_select_sql()
                    . 'WHERE m.id = :id LIMIT 1'
                );
                $select->execute([':id' => $id]);
                $row = $select->fetch(PDO::FETCH_ASSOC);
                if (is_array($row)) {
                    $row = admin_chat_normalize_row($row);
                }

                admin_json_response(['success' => true, 'data' => $row]);
            } catch (Throwable $e) {
                $msg = (string)$e->getMessage();
                if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
                    admin_json_error('Brak wymaganych kolumn/tabel (np. is_hearted). Uruchom /administration/install_notes_chat.php', 500);
                }
                admin_json_error('Błąd zmiany serduszka', 500);
            }
        }

        $message = isset($payload['message']) ? trim((string)$payload['message']) : '';
        $replyToId = isset($payload['reply_to_id']) ? (int)$payload['reply_to_id'] : null;
    } else {
        $action = isset($_POST['action']) ? (string)$_POST['action'] : '';
        $message = isset($_POST['message']) ? trim((string)$_POST['message']) : '';
        $replyToId = isset($_POST['reply_to_id']) && $_POST['reply_to_id'] !== '' ? (int)$_POST['reply_to_id'] : null;
        $clearFile = isset($_POST['clear_file']) ? (bool)((int)$_POST['clear_file']) : false;

        // Recall existing message (revoke own)
        if ($action === 'recall') {
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
            if ($id <= 0) {
                admin_json_error('Nieprawidłowe id', 422);
            }

            try {
                $stmt = $pdo->prepare('SELECT id, user_id FROM admin_chat_messages WHERE id = :id');
                $stmt->execute([':id' => $id]);
                $cur = $stmt->fetch(PDO::FETCH_ASSOC);
                if (!$cur) {
                    admin_json_error('Nie znaleziono wiadomości', 404);
                }
                if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
                    admin_json_error('Nie możesz cofnąć cudzej wiadomości', 403);
                }

                // Usuń plik z dysku jeśli istnieje
                try {
                    $recallFile = $pdo->prepare('SELECT file_path FROM admin_chat_messages WHERE id = :id LIMIT 1');
                    $recallFile->execute([':id' => $id]);
                    $recallRow = $recallFile->fetch(PDO::FETCH_ASSOC);
                    if (!empty($recallRow['file_path'])) {
                        $rSub  = dirname((string)$recallRow['file_path']);
                        $rName = basename((string)$recallRow['file_path']);
                        get_file_api_client()->deleteFile($rSub, $rName);
                    }
                } catch (Throwable $ignored) { /* nie blokuj cofania przy błędzie serwisu */ }

                $up = $pdo->prepare(
                    'UPDATE admin_chat_messages '
                    . 'SET message = :message, file_name = NULL, file_mime = NULL, file_size = NULL, file_path = NULL, updated_at = NOW() '
                    . 'WHERE id = :id'
                );
                $up->execute([':message' => $RECALLED_TEXT, ':id' => $id]);

                $select = $pdo->prepare(
                    admin_chat_select_sql()
                    . 'WHERE m.id = :id LIMIT 1'
                );
                $select->execute([':id' => $id]);
                $row = $select->fetch(PDO::FETCH_ASSOC);
                if (is_array($row)) {
                    $row = admin_chat_normalize_row($row);
                }

                admin_json_response(['success' => true, 'data' => $row]);
            } catch (Throwable $e) {
                $msg = (string)$e->getMessage();
                if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
                    admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
                }
                admin_json_error('Błąd cofania wiadomości', 500);
            }
        }

        if (!empty($_FILES['file']) && is_array($_FILES['file'])) {
            $upload = $_FILES['file'];
            if (($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
                if (($upload['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
                    admin_json_error('Błąd uploadu pliku (kod: ' . (int)$upload['error'] . ')', 422);
                }
                if (empty($upload['tmp_name']) || !is_uploaded_file($upload['tmp_name'])) {
                    admin_json_error('Nieprawidłowy upload pliku', 422);
                }

                $fileSize = isset($upload['size']) ? (int)$upload['size'] : null;
                if ($fileSize !== null && $fileSize > 5 * 1024 * 1024) {
                    admin_json_error('Plik jest za duży (max 5MB)', 422);
                }

                $fileName = (string)($upload['name'] ?? 'plik');

                // Rozpoznaj MIME bezpieczniej niż "type" z przeglądarki
                $detectedMime = null;
                if (function_exists('finfo_open')) {
                    $fi = finfo_open(FILEINFO_MIME_TYPE);
                    if ($fi) {
                        $detectedMime = finfo_file($fi, $upload['tmp_name']);
                        finfo_close($fi);
                    }
                }
                $fileMime = (string)($detectedMime ?: ($upload['type'] ?? 'application/octet-stream'));

                $allowed = false;
                if (stripos($fileMime, 'image/') === 0) {
                    $allowed = true;
                }
                if (in_array($fileMime, ['application/pdf', 'text/plain'], true)) {
                    $allowed = true;
                }
                if (!$allowed) {
                    admin_json_error('Niedozwolony typ pliku: ' . $fileMime, 422);
                }

                // Wyślij plik do serwisu plików – zapisze na dysku
                try {
                    $fileApiResult = get_file_api_client()->upload(
                        'admin_chat',
                        $upload['tmp_name'],
                        $fileName,
                        $fileMime
                    );
                    $filePath = (string)($fileApiResult['path'] ?? '');
                    if ($filePath === '') {
                        admin_json_error('Serwis plików nie zwrócił ścieżki', 500);
                    }
                } catch (RuntimeException $e) {
                    $status = (int)$e->getCode();
                    if ($status < 400 || $status > 599) {
                        $status = 500;
                    }
                    admin_json_error('Błąd zapisu pliku: ' . $e->getMessage(), $status);
                }
            }
        }

        // Update existing message (edit own) with optional file changes
        if ($action === 'update') {
            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
            $newMessage = $message;

            if ($id <= 0) {
                admin_json_error('Nieprawidłowe id', 422);
            }
            if ($newMessage === '') {
                admin_json_error('Wiadomość nie może być pusta', 422);
            }
            if (mb_strlen($newMessage) > 1500) {
                admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
            }

            try {
                $stmt = $pdo->prepare('SELECT id, user_id, message FROM admin_chat_messages WHERE id = :id');
                $stmt->execute([':id' => $id]);
                $cur = $stmt->fetch(PDO::FETCH_ASSOC);
                if (!$cur) {
                    admin_json_error('Nie znaleziono wiadomości', 404);
                }
                if ((int)$cur['user_id'] !== (int)$auth['user_id']) {
                    admin_json_error('Nie możesz edytować cudzej wiadomości', 403);
                }
                if (((string)($cur['message'] ?? '')) === $RECALLED_TEXT) {
                    admin_json_error('Nie możesz edytować cofniętej wiadomości', 422);
                }

                $fields = ['message = :message', 'updated_at = NOW()'];
                $params = [':message' => $newMessage, ':id' => $id];

                if ($clearFile) {
                    // Usuń stary plik z dysku jeśli istnieje
                    try {
                        $clearFileRow = $pdo->prepare('SELECT file_path FROM admin_chat_messages WHERE id = :id LIMIT 1');
                        $clearFileRow->execute([':id' => $id]);
                        $cfr = $clearFileRow->fetch(PDO::FETCH_ASSOC);
                        if (!empty($cfr['file_path'])) {
                            get_file_api_client()->deleteFile(
                                dirname((string)$cfr['file_path']),
                                basename((string)$cfr['file_path'])
                            );
                        }
                    } catch (Throwable $ignored) { /* nie blokuj aktualizacji */ }

                    $fields[] = 'file_name = NULL';
                    $fields[] = 'file_mime = NULL';
                    $fields[] = 'file_size = NULL';
                    $fields[] = 'file_path = NULL';
                } elseif ($filePath !== null) {
                    $fields[] = 'file_name = :file_name';
                    $fields[] = 'file_mime = :file_mime';
                    $fields[] = 'file_size = :file_size';
                    $fields[] = 'file_path = :file_path';
                    $params[':file_name'] = $fileName;
                    $params[':file_mime'] = $fileMime;
                    $params[':file_size'] = $fileSize;
                    $params[':file_path'] = $filePath;
                }

                $sql = 'UPDATE admin_chat_messages SET ' . implode(', ', $fields) . ' WHERE id = :id';
                $up = $pdo->prepare($sql);
                $up->bindValue(':message', $params[':message'], PDO::PARAM_STR);
                $up->bindValue(':id', (int)$params[':id'], PDO::PARAM_INT);

                if (array_key_exists(':file_name', $params)) {
                    $up->bindValue(':file_name', (string)$params[':file_name'], PDO::PARAM_STR);
                    $up->bindValue(':file_mime', (string)$params[':file_mime'], PDO::PARAM_STR);
                    $up->bindValue(':file_size', $params[':file_size'] !== null ? (int)$params[':file_size'] : null, $params[':file_size'] !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
                    $up->bindValue(':file_path', (string)$params[':file_path'], PDO::PARAM_STR);
                }

                $up->execute();

                $select = $pdo->prepare(
                    admin_chat_select_sql()
                    . 'WHERE m.id = :id LIMIT 1'
                );
                $select->execute([':id' => $id]);
                $row = $select->fetch(PDO::FETCH_ASSOC);
                if (is_array($row)) {
                    $row = admin_chat_normalize_row($row);
                }

                admin_json_response(['success' => true, 'data' => $row]);
            } catch (Throwable $e) {
                $msg = (string)$e->getMessage();
                if (stripos($msg, 'Unknown column') !== false || stripos($msg, "doesn't exist") !== false) {
                    admin_json_error('Brak wymaganych kolumn/tabel (np. updated_at). Uruchom /administration/install_notes_chat.php', 500);
                }
                admin_json_error('Błąd edycji wiadomości', 500);
            }
        }
    }

    if ($replyToId !== null && $replyToId <= 0) {
        $replyToId = null;
    }

    if ($message === '' && $filePath === null) {
        admin_json_error('Wiadomość lub plik są wymagane', 422);
    }

    if ($message !== '' && mb_strlen($message) > 1500) {
        admin_json_error('Wiadomość jest zbyt długa (max 1500 znaków)', 422);
    }

    try {
        // Server-side dedupe: jeśli user kliknie "Wyślij" kilka razy, nie duplikuj tego samego wpisu
        $dedupeStmt = $pdo->prepare(
            'SELECT id FROM admin_chat_messages '
            . 'WHERE user_id = :uid '
            . 'AND (message <=> :message) '
            . 'AND (reply_to_id <=> :reply_to_id) '
            . 'AND (file_name <=> :file_name) '
            . 'AND (file_size <=> :file_size) '
            . 'AND created_at >= (NOW() - INTERVAL 3 SECOND) '
            . 'ORDER BY id DESC LIMIT 1'
        );
        $dedupeStmt->bindValue(':uid', (int)$auth['user_id'], PDO::PARAM_INT);
        // Keep compatibility with schemas where message is NOT NULL: use empty string instead of NULL.
        $dedupeStmt->bindValue(':message', $message, PDO::PARAM_STR);
        $dedupeStmt->bindValue(':reply_to_id', $replyToId, $replyToId !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
        $dedupeStmt->bindValue(':file_name', $fileName, $fileName !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
        $dedupeStmt->bindValue(':file_size', $fileSize, $fileSize !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
        $dedupeStmt->execute();
        $existingId = (int)($dedupeStmt->fetchColumn() ?: 0);

        if ($existingId > 0) {
            $select = $pdo->prepare(
                admin_chat_select_sql()
                . 'WHERE m.id = :id LIMIT 1'
            );
            $select->execute([':id' => $existingId]);
            $row = $select->fetch(PDO::FETCH_ASSOC);
            if (is_array($row)) {
                $row = admin_chat_normalize_row($row);
            }

            admin_json_response([
                'success' => true,
                'deduped' => true,
                'data' => $row,
            ]);
        }

        $stmt = $pdo->prepare(
            'INSERT INTO admin_chat_messages (user_id, username, message, reply_to_id, file_name, file_mime, file_size, file_path) '
            . 'VALUES (:user_id, :username, :message, :reply_to_id, :file_name, :file_mime, :file_size, :file_path)'
        );

        $stmt->bindValue(':user_id', (int)$auth['user_id'], PDO::PARAM_INT);
        $stmt->bindValue(':username', (string)$auth['username'], PDO::PARAM_STR);
        // Keep compatibility with schemas where message is NOT NULL: use empty string instead of NULL.
        $stmt->bindValue(':message', $message, PDO::PARAM_STR);
        $stmt->bindValue(':reply_to_id', $replyToId, $replyToId !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
        $stmt->bindValue(':file_name', $fileName, $fileName !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
        $stmt->bindValue(':file_mime', $fileMime, $fileMime !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);
        $stmt->bindValue(':file_size', $fileSize, $fileSize !== null ? PDO::PARAM_INT : PDO::PARAM_NULL);
        $stmt->bindValue(':file_path', $filePath, $filePath !== null ? PDO::PARAM_STR : PDO::PARAM_NULL);

        $stmt->execute();

        $id = (int)$pdo->lastInsertId();
        $select = $pdo->prepare(
            admin_chat_select_sql()
            . 'WHERE m.id = :id LIMIT 1'
        );
        $select->execute([':id' => $id]);
        $row = $select->fetch(PDO::FETCH_ASSOC);

        if (is_array($row)) {
            $row = admin_chat_normalize_row($row);
        }

        admin_json_response([
            'success' => true,
            'data' => $row ?: [
                'id' => $id,
                'user_id' => (int)$auth['user_id'],
                'username' => (string)$auth['username'],
                'message' => $message,
                'created_at' => date('Y-m-d H:i:s'),
            ],
        ], 201);
    } catch (PDOException $e) {
        $msg = $e->getMessage();
        if (stripos($msg, 'Unknown column') !== false || stripos($msg, 'doesn\'t exist') !== false) {
            admin_json_error('Brak wymaganych kolumn/tabel. Uruchom /administration/install_notes_chat.php', 500);
        }
        if (stripos($msg, 'max_allowed_packet') !== false || stripos($msg, 'packet') !== false || stripos($msg, 'server has gone away') !== false) {
            admin_json_error('Błąd zapisu wiadomości: prawdopodobnie limit max_allowed_packet w MySQL. Zmniejsz plik lub zwiększ max_allowed_packet na serwerze.', 500);
        }
        $compact = preg_replace('/\s+/', ' ', (string)$msg);
        $compact = mb_substr($compact, 0, 240);
        if ($fileData !== null) {
            admin_json_error('Błąd zapisu wiadomości (DB): ' . $compact, 500);
        }
        admin_json_error('Błąd zapisu wiadomości', 500);
    } catch (Throwable $e) {
        admin_json_error('Błąd zapisu wiadomości', 500);
    }
}

admin_json_error('Metoda niedozwolona', 405);
