<?php
declare(strict_types=1);

/**
 * /messages/dialog.php
 * Відповідає за:
 * - показ діалогу між двома користувачами
 * - БЕЗ створення порожнього треду при простому відкритті ?u=username
 * - завершення / відновлення розмови
 * - AJAX polling для автопідтягування нових повідомлень без F5
 * - форма відправки тепер йде В ЄДИНУ ТОЧКУ: /messages/send.php
 */

require_once __DIR__ . '/../includes/start.php';

$pdo = db();
$me  = current_user();
$u   = $me;

require_login();

if (!function_exists('h')) {
    function h(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
}

function msg_flash_set(string $k, string $v): void {
    if (!isset($_SESSION)) return;
    if (!isset($_SESSION['_flash']) || !is_array($_SESSION['_flash'])) $_SESSION['_flash'] = [];
    $_SESSION['_flash'][$k] = $v;

    if (!isset($_SESSION['flash']) || !is_array($_SESSION['flash'])) $_SESSION['flash'] = [];
    $_SESSION['flash'][$k] = $v;
}

function msg_csrf_token(): string {
    if (function_exists('csrf_token')) {
        return (string)csrf_token();
    }
    if (!isset($_SESSION)) return '';
    if (empty($_SESSION['_msg_csrf'])) $_SESSION['_msg_csrf'] = bin2hex(random_bytes(16));
    return (string)$_SESSION['_msg_csrf'];
}

function msg_csrf_check(): bool {
    $sent = (string)($_POST['csrf'] ?? '');
    if ($sent === '') return false;

    if (function_exists('csrf_check')) {
        try {
            if (csrf_check($sent)) return true;
        } catch (Throwable $e) {}
    }

    $cur = msg_csrf_token();
    return $cur !== '' && hash_equals($cur, $sent);
}

function msg_json_out(array $data, int $code = 200): void {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Expires: 0');
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function msg_fmt_bytes(int $bytes): string {
    $u = ['B','KB','MB','GB','TB'];
    $i = 0;
    $n = (float)$bytes;
    while ($n >= 1024 && $i < count($u)-1) {
        $n /= 1024;
        $i++;
    }
    return ($i === 0 ? (string)$bytes : number_format($n, 2)) . ' ' . $u[$i];
}

/* ===== lang detect ===== */
$lang = 'uk';
if (isset($_SESSION['lang'])) {
    $lang = (string)$_SESSION['lang'];
} elseif (is_array($me) && isset($me['lang'])) {
    $lang = (string)$me['lang'];
}
$lang = strtolower(trim($lang));
if (!in_array($lang, ['uk','ru','en'], true)) $lang = 'uk';

function msg_t(string $key, string $lang): string {
    static $dict = [
        'uk' => [
            'title' => 'Діалог',
            'back'  => 'Назад',
            'send'  => 'Відправити',
            'type'  => 'Напишіть повідомлення…',
            'files' => 'Файли',
            'close' => 'Завершити розмову',
            'restore' => 'Відновити розмову',
            'closed_note' => 'Розмова завершена. Писати не можна, доки її не відновлять.',
            'sys_closed'  => 'завершив(ла) розмову.',
            'sys_restored'=> 'відновив(ла) розмову.',
            'err_csrf' => 'CSRF помилка. Оновіть сторінку і спробуйте ще раз.',
            'err_not_allowed' => 'Немає доступу до цієї розмови.',
            'err_user_not_found'=> 'Користувача не знайдено.',
            'err_self' => 'Не можна писати самому собі.',
            'attachments'=> 'Вкладення',
            'no_messages'=> 'Поки що повідомлень немає.',
        ],
        'ru' => [
            'title' => 'Диалог',
            'back'  => 'Назад',
            'send'  => 'Отправить',
            'type'  => 'Напишите сообщение…',
            'files' => 'Файлы',
            'close' => 'Завершить диалог',
            'restore' => 'Восстановить диалог',
            'closed_note' => 'Диалог завершён. Писать нельзя, пока его не восстановят.',
            'sys_closed'  => 'завершил(а) диалог.',
            'sys_restored'=> 'восстановил(а) диалог.',
            'err_csrf' => 'CSRF ошибка. Обновите страницу и попробуйте снова.',
            'err_not_allowed' => 'Нет доступа к этому диалогу.',
            'err_user_not_found'=> 'Пользователь не найден.',
            'err_self' => 'Нельзя писать самому себе.',
            'attachments'=> 'Вложения',
            'no_messages'=> 'Пока сообщений нет.',
        ],
        'en' => [
            'title' => 'Dialog',
            'back'  => 'Back',
            'send'  => 'Send',
            'type'  => 'Type a message…',
            'files' => 'Files',
            'close' => 'Close conversation',
            'restore' => 'Restore conversation',
            'closed_note' => 'Conversation is closed. You cannot send messages until it is restored.',
            'sys_closed'  => 'closed the conversation.',
            'sys_restored'=> 'restored the conversation.',
            'err_csrf' => 'CSRF error. Refresh the page and try again.',
            'err_not_allowed' => 'You have no access to this conversation.',
            'err_user_not_found'=> 'User not found.',
            'err_self' => 'You cannot message yourself.',
            'attachments'=> 'Attachments',
            'no_messages'=> 'No messages yet.',
        ],
    ];
    return $dict[$lang][$key] ?? ($dict['uk'][$key] ?? $key);
}

function msg_ensure_schema(PDO $pdo): void {
    $pdo->exec("
CREATE TABLE IF NOT EXISTS `message_threads` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user1_id` INT(10) UNSIGNED NOT NULL,
  `user2_id` INT(10) UNSIGNED NOT NULL,
  `created_at` DATETIME NOT NULL,
  `updated_at` DATETIME DEFAULT NULL,
  `last_message_at` DATETIME DEFAULT NULL,
  `last_message_id` BIGINT(20) UNSIGNED DEFAULT NULL,
  `is_closed` TINYINT(1) NOT NULL DEFAULT 0,
  `closed_by_user_id` INT(10) UNSIGNED DEFAULT NULL,
  `closed_at` DATETIME DEFAULT NULL,
  `user1_last_read_at` DATETIME DEFAULT NULL,
  `user2_last_read_at` DATETIME DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_pair` (`user1_id`,`user2_id`),
  KEY `idx_user1` (`user1_id`),
  KEY `idx_user2` (`user2_id`),
  KEY `idx_last_message_at` (`last_message_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");

    $pdo->exec("
CREATE TABLE IF NOT EXISTS `message_messages` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `thread_id` INT(10) UNSIGNED NOT NULL,
  `sender_id` INT(10) UNSIGNED NOT NULL,
  `msg_type` ENUM('user','system') NOT NULL DEFAULT 'user',
  `body` TEXT COLLATE utf8mb4_unicode_ci,
  `has_attachments` TINYINT(1) NOT NULL DEFAULT 0,
  `created_at` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_thread_id` (`thread_id`,`id`),
  KEY `idx_sender_id` (`sender_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");

    $pdo->exec("
CREATE TABLE IF NOT EXISTS `message_attachments` (
  `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `message_id` BIGINT(20) UNSIGNED NOT NULL,
  `rel_path` VARCHAR(512) COLLATE utf8mb4_unicode_ci NOT NULL,
  `stored_name` VARCHAR(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `original_name` VARCHAR(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `mime` VARCHAR(190) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `size_bytes` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
  `created_at` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_message_id` (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
");
}

function msg_thread_get_by_id(PDO $pdo, int $id): ?array {
    $st = $pdo->prepare("SELECT * FROM message_threads WHERE id=? LIMIT 1");
    $st->execute([$id]);
    $r = $st->fetch(PDO::FETCH_ASSOC);
    return $r ?: null;
}

function msg_thread_find_between(PDO $pdo, int $meId, int $peerId): ?array {
    $a = min($meId, $peerId);
    $b = max($meId, $peerId);
    $st = $pdo->prepare("SELECT * FROM message_threads WHERE user1_id=? AND user2_id=? LIMIT 1");
    $st->execute([$a, $b]);
    $r = $st->fetch(PDO::FETCH_ASSOC);
    return $r ?: null;
}

function msg_user_by_username(PDO $pdo, string $username): ?array {
    $st = $pdo->prepare("SELECT id, username, full_name, avatar_path FROM users WHERE username=? LIMIT 1");
    $st->execute([$username]);
    $r = $st->fetch(PDO::FETCH_ASSOC);
    return $r ?: null;
}

function msg_user_by_id(PDO $pdo, int $id): ?array {
    $st = $pdo->prepare("SELECT id, username, full_name, avatar_path FROM users WHERE id=? LIMIT 1");
    $st->execute([$id]);
    $r = $st->fetch(PDO::FETCH_ASSOC);
    return $r ?: null;
}

function msg_set_thread_read(PDO $pdo, array $thread, int $meId): void {
    $isU1 = (int)$thread['user1_id'] === $meId;
    $col  = $isU1 ? 'user1_last_read_at' : 'user2_last_read_at';
    $st = $pdo->prepare("UPDATE message_threads SET {$col}=NOW() WHERE id=?");
    $st->execute([(int)$thread['id']]);
}

function msg_initials(string $name): string {
    $name = trim($name);
    if ($name === '') return '?';
    $parts = preg_split('/\s+/u', $name) ?: [];
    $a = mb_substr($parts[0] ?? $name, 0, 1, 'UTF-8');
    $b = mb_substr($parts[1] ?? '', 0, 1, 'UTF-8');
    $res = mb_strtoupper($a . $b, 'UTF-8');
    return $res !== '' ? $res : '?';
}

function msg_avatar_src(array $user): string {
    $p = trim((string)($user['avatar_path'] ?? ''));
    if ($p !== '') return $p;

    $name = (string)($user['full_name'] ?? ($user['username'] ?? 'U'));
    $ini  = msg_initials($name);
    $id   = (int)($user['id'] ?? 0);
    $hue  = ($id * 47) % 360;
    $svg  = '<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96">'
          . '<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">'
          . '<stop offset="0" stop-color="hsl('.$hue.',85%,55%)"/>'
          . '<stop offset="1" stop-color="hsl('.(($hue+40)%360).',85%,50%)"/>'
          . '</linearGradient></defs>'
          . '<rect width="96" height="96" rx="20" fill="url(#g)"/>'
          . '<text x="50%" y="56%" dominant-baseline="middle" text-anchor="middle" '
          . 'font-family="Arial, sans-serif" font-size="34" fill="white" font-weight="700">'.$ini.'</text>'
          . '</svg>';
    return 'data:image/svg+xml;utf8,' . rawurlencode($svg);
}

function msg_load_messages(PDO $pdo, int $threadId, int $limit = 120, int $afterId = 0): array {
    if ($afterId > 0) {
        $st = $pdo->prepare("
            SELECT *
            FROM message_messages
            WHERE thread_id = ? AND id > ?
            ORDER BY id ASC
            LIMIT ?
        ");
        $st->bindValue(1, $threadId, PDO::PARAM_INT);
        $st->bindValue(2, $afterId, PDO::PARAM_INT);
        $st->bindValue(3, $limit, PDO::PARAM_INT);
        $st->execute();
        $msgs = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    } else {
        $st = $pdo->prepare("
            SELECT *
            FROM message_messages
            WHERE thread_id = ?
            ORDER BY id DESC
            LIMIT ?
        ");
        $st->bindValue(1, $threadId, PDO::PARAM_INT);
        $st->bindValue(2, $limit, PDO::PARAM_INT);
        $st->execute();
        $desc = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
        $msgs = array_reverse($desc);
    }

    $attMap = [];
    if ($msgs) {
        $ids = array_map(static fn($r) => (int)$r['id'], $msgs);
        $in  = implode(',', array_fill(0, count($ids), '?'));
        $stA = $pdo->prepare("SELECT * FROM message_attachments WHERE message_id IN ($in) ORDER BY id ASC");
        $stA->execute($ids);
        foreach ($stA->fetchAll(PDO::FETCH_ASSOC) ?: [] as $a) {
            $mid = (int)$a['message_id'];
            if (!isset($attMap[$mid])) $attMap[$mid] = [];
            $attMap[$mid][] = $a;
        }
    }

    return [$msgs, $attMap];
}

function msg_render_item_html(array $m, array $atts, int $meId): string {
    $mid = (int)$m['id'];
    $type = (string)$m['msg_type'];
    $isMine = (int)$m['sender_id'] === $meId;
    $dt = (string)($m['created_at'] ?? '');
    $timeOut = $dt ? date('d.m.Y H:i', strtotime($dt)) : '';

    ob_start();
    if ($type === 'system'): ?>
        <div class="msg-system" data-message-id="<?= $mid ?>">
            <span><?= h((string)$m['body']) ?></span>
            <?php if ($timeOut): ?><em><?= h($timeOut) ?></em><?php endif; ?>
        </div>
    <?php else: ?>
        <div class="msg-bubble-row <?= $isMine ? 'mine' : 'theirs' ?>" data-message-id="<?= $mid ?>">
            <div class="msg-bubble">
                <?php if (trim((string)$m['body']) !== ''): ?>
                    <div class="msg-text"><?= nl2br(h((string)$m['body'])) ?></div>
                <?php endif; ?>

                <?php if ($atts): ?>
                    <div class="msg-atts">
                        <?php foreach ($atts as $a): ?>
                            <?php
                            $aid  = (int)$a['id'];
                            $mime = strtolower((string)($a['mime'] ?? ''));
                            $orig = (string)($a['original_name'] ?? 'file');
                            $size = (int)($a['size_bytes'] ?? 0);
                            $url  = '/messages/file.php?id=' . $aid;
                            $dl   = '/messages/file.php?id=' . $aid . '&dl=1';
                            ?>

                            <?php if (str_starts_with($mime, 'image/')): ?>
                                <a class="msg-att img" href="<?= h($url) ?>" target="_blank" rel="noopener">
                                    <img src="<?= h($url) ?>" alt="<?= h($orig) ?>">
                                    <div class="cap"><?= h($orig) ?> · <?= h(msg_fmt_bytes($size)) ?></div>
                                </a>
                            <?php elseif (str_starts_with($mime, 'audio/')): ?>
                                <div class="msg-att audio">
                                    <audio controls src="<?= h($url) ?>"></audio>
                                    <div class="cap"><a href="<?= h($dl) ?>"><?= h($orig) ?></a> · <?= h(msg_fmt_bytes($size)) ?></div>
                                </div>
                            <?php elseif (str_starts_with($mime, 'video/')): ?>
                                <div class="msg-att video">
                                    <video controls src="<?= h($url) ?>"></video>
                                    <div class="cap"><a href="<?= h($dl) ?>"><?= h($orig) ?></a> · <?= h(msg_fmt_bytes($size)) ?></div>
                                </div>
                            <?php else: ?>
                                <a class="msg-att file" href="<?= h($dl) ?>">
                                    <div class="file-ic">📎</div>
                                    <div class="file-meta">
                                        <div class="file-name"><?= h($orig) ?></div>
                                        <div class="file-sub"><?= h(msg_fmt_bytes($size)) ?></div>
                                    </div>
                                </a>
                            <?php endif; ?>
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>

                <?php if ($timeOut): ?>
                    <div class="msg-time"><?= h($timeOut) ?></div>
                <?php endif; ?>
            </div>
        </div>
    <?php endif;
    return (string)ob_get_clean();
}

try {
    msg_ensure_schema($pdo);
} catch (Throwable $e) {
    $title = msg_t('title', $lang);
    $seo_noindex = true;
    require __DIR__ . '/../includes/header.php';
    echo '<div class="container my-4"><div class="alert alert-danger">DB schema error: ' . h($e->getMessage()) . '</div></div>';
    require __DIR__ . '/../includes/footer.php';
    exit;
}

/* ===== identify context ===== */
$meId = (int)($me['id'] ?? 0);
$threadId = (int)($_GET['t'] ?? 0);
$peerU    = trim((string)($_GET['u'] ?? ''));

$thread = null;
$peer   = null;

if ($threadId > 0) {
    $thread = msg_thread_get_by_id($pdo, $threadId);
    if (!$thread) {
        http_response_code(404);
        echo 'Thread not found';
        exit;
    }

    if ((int)$thread['user1_id'] !== $meId && (int)$thread['user2_id'] !== $meId) {
        msg_flash_set('err', msg_t('err_not_allowed', $lang));
        header('Location: /messages/');
        exit;
    }

    $peerId = ((int)$thread['user1_id'] === $meId) ? (int)$thread['user2_id'] : (int)$thread['user1_id'];
    $peer = msg_user_by_id($pdo, $peerId);
} elseif ($peerU !== '') {
    $peer = msg_user_by_username($pdo, $peerU);
    if (!$peer) {
        msg_flash_set('err', msg_t('err_user_not_found', $lang));
        header('Location: /messages/');
        exit;
    }

    $peerId = (int)$peer['id'];
    if ($peerId === $meId) {
        msg_flash_set('err', msg_t('err_self', $lang));
        header('Location: /messages/');
        exit;
    }

    // ТІЛЬКИ шукаємо, але не створюємо пустий тред
    $thread = msg_thread_find_between($pdo, $meId, $peerId);
    if ($thread) {
        $threadId = (int)$thread['id'];
    }
} else {
    header('Location: /messages/');
    exit;
}

if (!$peer) {
    msg_flash_set('err', msg_t('err_user_not_found', $lang));
    header('Location: /messages/');
    exit;
}

/* ===== AJAX polling ===== */
if ((string)($_GET['ajax'] ?? '') === 'poll') {
    if (!$thread && $peer) {
        $thread = msg_thread_find_between($pdo, $meId, (int)$peer['id']);
        if ($thread) {
            $threadId = (int)$thread['id'];
        }
    }

    if (!$thread || $threadId <= 0) {
        msg_json_out([
            'ok' => true,
            'has_thread' => false,
            'thread_id' => 0,
            'is_closed' => false,
            'items' => [],
            'last_id' => 0,
        ]);
    }

    if ((int)$thread['user1_id'] !== $meId && (int)$thread['user2_id'] !== $meId) {
        msg_json_out(['ok' => false, 'err' => 'forbidden'], 403);
    }

    $afterId = max(0, (int)($_GET['after_id'] ?? 0));
    [$newMsgs, $attMapAjax] = msg_load_messages($pdo, $threadId, 100, $afterId);

    $hasIncoming = false;
    $items = [];
    foreach ($newMsgs as $m) {
        if ((int)($m['sender_id'] ?? 0) !== $meId) {
            $hasIncoming = true;
        }
        $mid = (int)$m['id'];
        $items[] = [
            'id' => $mid,
            'html' => msg_render_item_html($m, $attMapAjax[$mid] ?? [], $meId),
        ];
    }

    if ($hasIncoming) {
        try { msg_set_thread_read($pdo, $thread, $meId); } catch (Throwable $e) {}
    }

    $freshThread = msg_thread_get_by_id($pdo, $threadId) ?: $thread;

    msg_json_out([
        'ok' => true,
        'has_thread' => true,
        'thread_id' => (int)$freshThread['id'],
        'is_closed' => ((int)($freshThread['is_closed'] ?? 0) === 1),
        'items' => $items,
        'last_id' => $items ? (int)end($items)['id'] : $afterId,
    ]);
}

/* ===== close / restore ===== */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!msg_csrf_check()) {
        msg_flash_set('err', msg_t('err_csrf', $lang));
        if ($threadId > 0) {
            header('Location: /messages/dialog.php?t=' . $threadId);
        } else {
            header('Location: /messages/dialog.php?u=' . urlencode((string)$peer['username']));
        }
        exit;
    }

    $action = (string)($_POST['action'] ?? '');

    if ($threadId > 0) {
        $thread = msg_thread_get_by_id($pdo, $threadId) ?: $thread;
    }

    if ($action === 'close' || $action === 'restore') {
        if (!$thread || $threadId <= 0) {
            header('Location: /messages/');
            exit;
        }

        if ((int)$thread['user1_id'] !== $meId && (int)$thread['user2_id'] !== $meId) {
            msg_flash_set('err', msg_t('err_not_allowed', $lang));
            header('Location: /messages/');
            exit;
        }

        $isClosed = (int)($thread['is_closed'] ?? 0) === 1;

        if ($action === 'close' && !$isClosed) {
            $pdo->prepare("UPDATE message_threads SET is_closed=1, closed_by_user_id=?, closed_at=NOW(), updated_at=NOW() WHERE id=?")
                ->execute([$meId, $threadId]);

            $body = trim((string)($me['full_name'] ?? ($me['username'] ?? 'User'))) . ' ' . msg_t('sys_closed', $lang);
            $pdo->prepare("INSERT INTO message_messages (thread_id,sender_id,msg_type,body,has_attachments,created_at) VALUES (?,?,?,?,0,NOW())")
                ->execute([$threadId, $meId, 'system', $body]);
            $mid = (int)$pdo->lastInsertId();

            $pdo->prepare("UPDATE message_threads SET last_message_id=?, last_message_at=NOW(), updated_at=NOW() WHERE id=?")
                ->execute([$mid, $threadId]);

            header('Location: /messages/dialog.php?t=' . $threadId);
            exit;
        }

        if ($action === 'restore' && $isClosed) {
            $pdo->prepare("UPDATE message_threads SET is_closed=0, closed_by_user_id=NULL, closed_at=NULL, updated_at=NOW() WHERE id=?")
                ->execute([$threadId]);

            $body = trim((string)($me['full_name'] ?? ($me['username'] ?? 'User'))) . ' ' . msg_t('sys_restored', $lang);
            $pdo->prepare("INSERT INTO message_messages (thread_id,sender_id,msg_type,body,has_attachments,created_at) VALUES (?,?,?,?,0,NOW())")
                ->execute([$threadId, $meId, 'system', $body]);
            $mid = (int)$pdo->lastInsertId();

            $pdo->prepare("UPDATE message_threads SET last_message_id=?, last_message_at=NOW(), updated_at=NOW() WHERE id=?")
                ->execute([$mid, $threadId]);

            header('Location: /messages/dialog.php?t=' . $threadId);
            exit;
        }

        header('Location: /messages/dialog.php?t=' . $threadId);
        exit;
    }

    header('Location: /messages/');
    exit;
}

/* ===== mark as read ===== */
if ($thread) {
    try { msg_set_thread_read($pdo, $thread, $meId); } catch (Throwable $e) {}
}

/* ===== load messages ===== */
$msgs = [];
$attMap = [];
if ($thread && $threadId > 0) {
    [$msgs, $attMap] = msg_load_messages($pdo, $threadId, 120, 0);
}

$isClosed = $thread ? ((int)($thread['is_closed'] ?? 0) === 1) : false;
$msgRestrictionState = (is_array($me) && !empty($me['id']) && function_exists('ar_get_state')) ? ar_get_state($pdo, (int)$me['id']) : [];
$msgSendBlockedByRestriction = is_array($msgRestrictionState) && function_exists('ar_is_action_blocked') ? ar_is_action_blocked($msgRestrictionState, 'messages') : false;
$msgSendBlocked = $isClosed || $msgSendBlockedByRestriction;
$msgRestrictionText = $msgSendBlockedByRestriction && function_exists('ar_action_error') ? ar_action_error($msgRestrictionState, 'messages') : '';
$peerName = (string)($peer['full_name'] ?? $peer['username'] ?? '');
$peerAva  = msg_avatar_src($peer);

$lastMsgId = 0;
if ($msgs) {
    $last = end($msgs);
    $lastMsgId = (int)($last['id'] ?? 0);
}

$title = msg_t('title', $lang);
$seo_noindex = true;

require __DIR__ . '/../includes/header.php';
?>

<link rel="stylesheet" href="/css/messages.css?v=4">

<div class="feed-layout">
  <aside>
    <div class="sidebar-sticky">
      <?php require __DIR__ . '/../blocks/profile.php'; ?>
    </div>
  </aside>

  <section>
    <div class="container my-4">

      <div class="msg-chat-head">
        <div class="msg-chat-left">
          <a class="btn btn-sm btn-outline-secondary" href="/messages/"><?= h(msg_t('back', $lang)) ?></a>

          <div class="msg-peer">
            <div class="msg-peer-ava"><img src="<?= h($peerAva) ?>" alt=""></div>
            <div class="msg-peer-meta">
              <div class="msg-peer-name"><?= h($peerName) ?></div>
              <div class="msg-peer-sub">@<?= h((string)$peer['username']) ?></div>
            </div>
          </div>
        </div>

        <div class="msg-chat-actions">
          <?php if ($thread && $threadId > 0): ?>
            <?php if (!$isClosed): ?>
              <form method="post" class="d-inline">
                <input type="hidden" name="csrf" value="<?= h(msg_csrf_token()) ?>">
                <input type="hidden" name="action" value="close">
                <button class="btn btn-sm btn-outline-danger" type="submit"><?= h(msg_t('close', $lang)) ?></button>
              </form>
            <?php else: ?>
              <form method="post" class="d-inline">
                <input type="hidden" name="csrf" value="<?= h(msg_csrf_token()) ?>">
                <input type="hidden" name="action" value="restore">
                <button class="btn btn-sm btn-success" type="submit"><?= h(msg_t('restore', $lang)) ?></button>
              </form>
            <?php endif; ?>
          <?php endif; ?>
        </div>
      </div>

      <?php if ($isClosed): ?>
        <div class="msg-closed-note" id="msgClosedNote"><?= h(msg_t('closed_note', $lang)) ?></div>
      <?php else: ?>
        <div class="msg-closed-note" id="msgClosedNote" style="display:none;"><?= h(msg_t('closed_note', $lang)) ?></div>
      <?php endif; ?>

      <?php if ($msgSendBlockedByRestriction): ?>
        <div class="alert alert-warning mt-3 mb-0"><?= h($msgRestrictionText) ?></div>
      <?php endif; ?>

      <div class="msg-chat-card">
        <div class="msg-chat-body" id="chatBody" data-last-id="<?= (int)$lastMsgId ?>">
          <?php if (!$msgs): ?>
            <div class="msg-system" id="msgEmptyState">
              <span><?= h(msg_t('no_messages', $lang)) ?></span>
            </div>
          <?php else: ?>
            <?php foreach ($msgs as $m): ?>
              <?= msg_render_item_html($m, $attMap[(int)$m['id']] ?? [], $meId) ?>
            <?php endforeach; ?>
          <?php endif; ?>
        </div>

        <div class="msg-chat-foot">
          <form method="post" action="/messages/send.php" enctype="multipart/form-data" class="msg-send-form" id="msgSendForm">
            <input type="hidden" name="csrf" value="<?= h(msg_csrf_token()) ?>">
            <input type="hidden" name="back" value="<?= h('/messages/dialog.php' . ($threadId > 0 ? '?t=' . $threadId : '?u=' . urlencode((string)$peer['username']))) ?>">
            <input type="hidden" name="to_user_id" value="<?= (int)$peer['id'] ?>">
            <input type="hidden" name="to_username" value="<?= h((string)$peer['username']) ?>">
            <?php if ($threadId > 0): ?>
              <input type="hidden" name="thread_id" value="<?= (int)$threadId ?>">
            <?php endif; ?>

            <textarea name="body" class="form-control msg-input" rows="2" placeholder="<?= h(msg_t('type', $lang)) ?>" <?= $msgSendBlocked ? 'disabled' : '' ?>></textarea>

            <div class="msg-send-row">
              <label class="btn btn-sm btn-outline-secondary mb-0">
                <?= h(msg_t('files', $lang)) ?>
                <input type="file" name="files[]" multiple hidden accept=".jpg,.jpeg,.png,.gif,.webp,.mp3,.ogg,.wav,.m4a,.aac,.flac,.mp4,.webm,.mov,.m4v,.pdf,.txt,.rtf,.csv,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.odt,.ods,.odp,.zip,.rar,.7z" <?= $msgSendBlocked ? 'disabled' : '' ?>>
              </label>

              <button class="btn btn-sm btn-primary" type="submit" id="msgSendBtn" <?= $msgSendBlocked ? 'disabled' : '' ?>>
                <?= h(msg_t('send', $lang)) ?>
              </button>
            </div>

            <div class="msg-send-hint">
              <?= h(msg_t('attachments', $lang)) ?>: max 10 файлів · 100 MB / файл · 200 MB / повідомлення
            </div>
          </form>
        </div>
      </div>

    </div>
  </section>

  <aside>
    <div class="sidebar-sticky">
      <?php require __DIR__ . '/../blocks/groups.php'; ?>
    </div>
  </aside>
</div>

<script>
(function(){
  var chatBody = document.getElementById('chatBody');
  var sendForm = document.getElementById('msgSendForm');
  var sendBtn  = document.getElementById('msgSendBtn');
  var closedNote = document.getElementById('msgClosedNote');

  var threadId = <?= (int)$threadId ?>;
  var peerUsername = <?= json_encode((string)$peer['username'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
  var lastMsgId = parseInt(chatBody ? (chatBody.getAttribute('data-last-id') || '0') : '0', 10) || 0;

  function scrollToBottom() {
    if (!chatBody) return;
    chatBody.scrollTop = chatBody.scrollHeight;
  }

  function isNearBottom() {
    if (!chatBody) return true;
    return (chatBody.scrollHeight - chatBody.scrollTop - chatBody.clientHeight) < 140;
  }

  function removeEmptyState() {
    var empty = document.getElementById('msgEmptyState');
    if (empty) empty.remove();
  }

  function setClosedState(isClosed) {
    if (closedNote) {
      closedNote.style.display = isClosed ? '' : 'none';
    }
    if (!sendForm) return;

    var textarea = sendForm.querySelector('textarea[name="body"]');
    var fileInput = sendForm.querySelector('input[type="file"][name="files[]"]');

    if (textarea) textarea.disabled = !!isClosed;
    if (fileInput) fileInput.disabled = !!isClosed;
    if (sendBtn) sendBtn.disabled = !!isClosed;
  }

  function buildPollUrl() {
    var url = new URL(window.location.href);
    url.searchParams.set('ajax', 'poll');
    url.searchParams.set('after_id', String(lastMsgId));
    url.searchParams.set('_ts', String(Date.now()));

    if (threadId > 0) {
      url.searchParams.set('t', String(threadId));
      url.searchParams.delete('u');
    } else {
      url.searchParams.set('u', peerUsername);
      url.searchParams.delete('t');
    }

    return url.toString();
  }

  var polling = false;

  async function pollMessages() {
    if (polling) return;
    polling = true;

    try {
      var nearBottom = isNearBottom();
      var res = await fetch(buildPollUrl(), {
        method: 'GET',
        credentials: 'same-origin',
        headers: {
          'X-Requested-With': 'XMLHttpRequest',
          'Accept': 'application/json'
        },
        cache: 'no-store'
      });

      if (!res.ok) return;

      var data = await res.json();
      if (!data || !data.ok) return;

      if (data.has_thread && data.thread_id && threadId !== data.thread_id) {
        threadId = parseInt(data.thread_id, 10) || 0;
        history.replaceState({}, '', '/messages/dialog.php?t=' + threadId);

        var hiddenThread = sendForm ? sendForm.querySelector('input[name="thread_id"]') : null;
        if (!hiddenThread && sendForm) {
          hiddenThread = document.createElement('input');
          hiddenThread.type = 'hidden';
          hiddenThread.name = 'thread_id';
          sendForm.appendChild(hiddenThread);
        }
        if (hiddenThread) hiddenThread.value = String(threadId);

        var back = sendForm ? sendForm.querySelector('input[name="back"]') : null;
        if (back) back.value = '/messages/dialog.php?t=' + threadId;
      }

      if (typeof data.is_closed !== 'undefined') {
        setClosedState(!!data.is_closed);
      }

      if (Array.isArray(data.items) && data.items.length) {
        removeEmptyState();

        data.items.forEach(function(item){
          if (!item || !item.html) return;
          chatBody.insertAdjacentHTML('beforeend', item.html);
          var id = parseInt(item.id || '0', 10) || 0;
          if (id > lastMsgId) lastMsgId = id;
        });

        chatBody.setAttribute('data-last-id', String(lastMsgId));

        if (nearBottom) {
          scrollToBottom();
        }
      }
    } catch (e) {
      // ignore
    } finally {
      polling = false;
    }
  }

  scrollToBottom();
  setClosedState(<?= $isClosed ? 'true' : 'false' ?>);

  setInterval(pollMessages, 2500);

  document.addEventListener('visibilitychange', function() {
    if (!document.hidden) pollMessages();
  });

  window.addEventListener('focus', function() {
    pollMessages();
  });
})();
</script>

<?php require __DIR__ . '/../includes/footer.php'; ?>