<?php
declare(strict_types=1);

/**
 * /functional/comment/manage.php
 *
 * Відповідає за:
 * - action=edit
 * - action=delete
 * - action=delete_attachment
 * - один центр логіки для root/admin-wrapper'ів
 *
 * Підтримує:
 * - редагування body
 * - видалення вибраних вкладень
 * - додавання нових вкладень
 * - безпечне фізичне видалення файлів після commit
 */

require_once dirname(__DIR__, 2) . '/includes/start.php';
require_once __DIR__ . '/_lib.php';

if (!function_exists('cm_redirect')) {
    function cm_redirect(string $to): never
    {
        header('Location: ' . $to);
        exit;
    }
}

if (!function_exists('cm_flash_set')) {
    function cm_flash_set(string $key, string $msg): void
    {
        $_SESSION['flash'][$key] = $msg;
    }
}

if (!function_exists('cm_is_post')) {
    function cm_is_post(): bool
    {
        return strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')) === 'POST';
    }
}

if (!function_exists('cm_table_exists')) {
    function cm_table_exists(PDO $pdo, string $table): bool
    {
        return function_exists('comment_table_exists')
            ? comment_table_exists($pdo, $table)
            : false;
    }
}

if (!function_exists('cm_table_columns')) {
    function cm_table_columns(PDO $pdo, string $table): array
    {
        return function_exists('comment_table_columns')
            ? comment_table_columns($pdo, $table)
            : [];
    }
}

if (!function_exists('cm_path_column')) {
    function cm_path_column(PDO $pdo, string $table): ?string
    {
        $cols = cm_table_columns($pdo, $table);
        foreach (['path', 'rel_path'] as $c) {
            if (in_array($c, $cols, true)) return $c;
        }
        return null;
    }
}

if (!function_exists('cm_comment_row')) {
    function cm_comment_row(PDO $pdo, int $commentId): ?array
    {
        $sql = "
            SELECT c.*,
                   p.user_id AS post_owner_user_id,
                   p.group_id AS post_group_id
            FROM comments c
            INNER JOIN posts p ON p.id = c.post_id
            WHERE c.id = ?
            LIMIT 1
        ";
        $st = $pdo->prepare($sql);
        $st->execute([$commentId]);
        $row = $st->fetch(PDO::FETCH_ASSOC);

        return $row ?: null;
    }
}

if (!function_exists('cm_is_group_manager')) {
    function cm_is_group_manager(PDO $pdo, int $groupId, int $userId): bool
    {
        if ($groupId <= 0 || $userId <= 0 || !cm_table_exists($pdo, 'group_members')) {
            return false;
        }

        $st = $pdo->prepare("
            SELECT 1
            FROM group_members
            WHERE group_id = ?
              AND user_id = ?
              AND status = 'active'
              AND role IN ('owner','admin')
            LIMIT 1
        ");
        $st->execute([$groupId, $userId]);

        return (bool)$st->fetchColumn();
    }
}

if (!function_exists('cm_user_can_manage_comment')) {
    function cm_user_can_manage_comment(PDO $pdo, array $me, array $comment): bool
    {
        $uid = (int)($me['id'] ?? 0);
        $role = (string)($me['role'] ?? '');

        if ($uid <= 0) return false;
        if ($role === 'admin') return true;
        if ((int)($comment['user_id'] ?? 0) === $uid) return true;
        if ((int)($comment['post_owner_user_id'] ?? 0) === $uid) return true;

        $groupId = (int)($comment['post_group_id'] ?? 0);
        if ($groupId > 0 && cm_is_group_manager($pdo, $groupId, $uid)) {
            return true;
        }

        return false;
    }
}

if (!function_exists('cm_attachment_paths_for_comment_ids')) {
    function cm_attachment_paths_for_comment_ids(PDO $pdo, array $commentIds): array
    {
        if (!$commentIds || !cm_table_exists($pdo, 'comment_attachments')) return [];

        $commentIds = array_values(array_unique(array_map('intval', $commentIds)));
        $commentIds = array_values(array_filter($commentIds, static fn($v) => $v > 0));
        if (!$commentIds) return [];

        $pathCol = cm_path_column($pdo, 'comment_attachments');
        if ($pathCol === null) return [];

        $in = implode(',', array_fill(0, count($commentIds), '?'));
        $sql = "SELECT `{$pathCol}` AS path_value FROM comment_attachments WHERE comment_id IN ({$in})";
        $st = $pdo->prepare($sql);
        $st->execute($commentIds);

        $rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
        $out = [];

        foreach ($rows as $r) {
            $p = (string)($r['path_value'] ?? '');
            if ($p !== '') $out[] = $p;
        }

        return array_values(array_unique($out));
    }
}

if (!function_exists('cm_collect_comment_tree_ids')) {
    function cm_collect_comment_tree_ids(PDO $pdo, int $commentId): array
    {
        $all = [];
        $queue = [$commentId];

        while ($queue) {
            $batch = array_values(array_unique(array_map('intval', $queue)));
            $batch = array_values(array_filter($batch, static fn($v) => $v > 0));
            $queue = [];

            if (!$batch) break;

            foreach ($batch as $id) {
                $all[$id] = true;
            }

            $in = implode(',', array_fill(0, count($batch), '?'));
            $st = $pdo->prepare("SELECT id FROM comments WHERE parent_id IN ({$in})");
            $st->execute($batch);
            $children = array_map('intval', $st->fetchAll(PDO::FETCH_COLUMN) ?: []);

            foreach ($children as $childId) {
                if (!isset($all[$childId])) {
                    $queue[] = $childId;
                }
            }
        }

        return array_map('intval', array_keys($all));
    }
}

if (!function_exists('cm_insert_attachment_dynamic')) {
    function cm_insert_attachment_dynamic(PDO $pdo, int $commentId, array $row): void
    {
        if (!function_exists('comment_insert_attachment_dynamic')) {
            throw new RuntimeException('comment_insert_attachment_dynamic() not found.');
        }
        comment_insert_attachment_dynamic($pdo, $commentId, $row);
    }
}

if (!function_exists('cm_delete_selected_attachment_rows')) {
    function cm_delete_selected_attachment_rows(PDO $pdo, int $commentId, array $ids): array
    {
        if (!$ids || !cm_table_exists($pdo, 'comment_attachments')) return [];

        $ids = array_values(array_unique(array_map('intval', $ids)));
        $ids = array_values(array_filter($ids, static fn($v) => $v > 0));
        if (!$ids) return [];

        $pathCol = cm_path_column($pdo, 'comment_attachments') ?? 'path';
        $in = implode(',', array_fill(0, count($ids), '?'));

        $sqlSel = "SELECT id, `{$pathCol}` AS path_value FROM comment_attachments WHERE comment_id = ? AND id IN ({$in})";
        $stSel = $pdo->prepare($sqlSel);
        $stSel->execute(array_merge([$commentId], $ids));
        $rows = $stSel->fetchAll(PDO::FETCH_ASSOC) ?: [];

        if ($rows) {
            $sqlDel = "DELETE FROM comment_attachments WHERE comment_id = ? AND id IN ({$in})";
            $stDel = $pdo->prepare($sqlDel);
            $stDel->execute(array_merge([$commentId], $ids));
        }

        $paths = [];
        foreach ($rows as $r) {
            $p = (string)($r['path_value'] ?? '');
            if ($p !== '') $paths[] = $p;
        }

        return array_values(array_unique($paths));
    }
}

if (!function_exists('cm_extract_delete_attachment_ids')) {
    function cm_extract_delete_attachment_ids(array $src): array
    {
        $out = [];

        foreach (['delete_attachment_ids', 'remove_attachment_ids', 'attachment_delete_ids'] as $key) {
            if (!isset($src[$key])) continue;

            $value = $src[$key];
            if (!is_array($value)) $value = [$value];

            foreach ($value as $v) {
                if ($v !== '' && is_numeric((string)$v)) {
                    $out[] = (int)$v;
                }
            }
        }

        return array_values(array_unique(array_filter($out, static fn($v) => $v > 0)));
    }
}

if (!function_exists('cm_edit_comment_core')) {
    function cm_edit_comment_core(PDO $pdo, array $me, int $commentId, string $body, array $deleteAttachmentIds, array $newUploads): void
    {
        $comment = cm_comment_row($pdo, $commentId);
        if (!$comment) {
            throw new RuntimeException('Comment not found.');
        }

        if (!cm_user_can_manage_comment($pdo, $me, $comment)) {
            throw new RuntimeException('Access denied.');
        }

        $body = comment_normalize_text($body);

        $existingAttachments = [];
        if (cm_table_exists($pdo, 'comment_attachments')) {
            $st = $pdo->prepare('SELECT * FROM comment_attachments WHERE comment_id = ? ORDER BY id ASC');
            $st->execute([$commentId]);
            $existingAttachments = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
        }

        $remainingAfterDelete = $existingAttachments;
        if ($deleteAttachmentIds) {
            $deleteMap = array_fill_keys(array_map('intval', $deleteAttachmentIds), true);
            $remainingAfterDelete = array_values(array_filter($existingAttachments, static function ($row) use ($deleteMap) {
                return !isset($deleteMap[(int)($row['id'] ?? 0)]);
            }));
        }

        if ($body === '' && !$remainingAfterDelete && !$newUploads) {
            throw new RuntimeException('Comment body or attachment required.');
        }

        $pathsToDeleteAfterCommit = [];

        try {
            if (!$pdo->inTransaction()) {
                $pdo->beginTransaction();
            }

            $st = $pdo->prepare('UPDATE comments SET body = ? WHERE id = ? LIMIT 1');
            $st->execute([$body, $commentId]);

            if ($deleteAttachmentIds) {
                $pathsToDeleteAfterCommit = array_merge(
                    $pathsToDeleteAfterCommit,
                    cm_delete_selected_attachment_rows($pdo, $commentId, $deleteAttachmentIds)
                );
            }

            foreach ($newUploads as $row) {
                cm_insert_attachment_dynamic($pdo, $commentId, $row);
            }

            $pdo->commit();

            foreach (array_values(array_unique($pathsToDeleteAfterCommit)) as $path) {
                comment_unlink_webpath($path);
            }
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            comment_cleanup_uploaded_files($newUploads);
            throw $e;
        }
    }
}

if (!function_exists('cm_delete_comment_core')) {
    function cm_delete_comment_core(PDO $pdo, array $me, int $commentId): void
    {
        $comment = cm_comment_row($pdo, $commentId);
        if (!$comment) {
            throw new RuntimeException('Comment not found.');
        }

        if (!cm_user_can_manage_comment($pdo, $me, $comment)) {
            throw new RuntimeException('Access denied.');
        }

        $treeIds = cm_collect_comment_tree_ids($pdo, $commentId);
        $paths = cm_attachment_paths_for_comment_ids($pdo, $treeIds);

        try {
            if (!$pdo->inTransaction()) {
                $pdo->beginTransaction();
            }

            $st = $pdo->prepare('DELETE FROM comments WHERE id = ? LIMIT 1');
            $st->execute([$commentId]);

            $pdo->commit();

            foreach ($paths as $path) {
                comment_unlink_webpath($path);
            }
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}

if (!function_exists('cm_delete_comment_attachment_core')) {
    function cm_delete_comment_attachment_core(PDO $pdo, array $me, int $attachmentId): void
    {
        if ($attachmentId <= 0 || !cm_table_exists($pdo, 'comment_attachments')) {
            throw new RuntimeException('Attachment not found.');
        }

        $pathCol = cm_path_column($pdo, 'comment_attachments') ?? 'path';

        $sql = "
            SELECT ca.id AS attachment_id,
                   ca.`{$pathCol}` AS path_value,
                   c.id AS comment_id,
                   c.user_id,
                   p.user_id AS post_owner_user_id,
                   p.group_id AS post_group_id
            FROM comment_attachments ca
            INNER JOIN comments c ON c.id = ca.comment_id
            INNER JOIN posts p ON p.id = c.post_id
            WHERE ca.id = ?
            LIMIT 1
        ";
        $st = $pdo->prepare($sql);
        $st->execute([$attachmentId]);
        $row = $st->fetch(PDO::FETCH_ASSOC);

        if (!$row) {
            throw new RuntimeException('Attachment not found.');
        }

        if (!cm_user_can_manage_comment($pdo, $me, $row)) {
            throw new RuntimeException('Access denied.');
        }

        $path = (string)($row['path_value'] ?? '');

        try {
            if (!$pdo->inTransaction()) {
                $pdo->beginTransaction();
            }

            $del = $pdo->prepare('DELETE FROM comment_attachments WHERE id = ? LIMIT 1');
            $del->execute([$attachmentId]);

            $pdo->commit();

            if ($path !== '') {
                comment_unlink_webpath($path);
            }
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }
}

if (!function_exists('cm_handle_request')) {
    function cm_handle_request(array $opts = []): void
    {
        header('X-Robots-Tag: noindex, nofollow, noarchive', true);
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

        require_login();

        $pdo = isset($GLOBALS['pdo']) && $GLOBALS['pdo'] instanceof PDO ? $GLOBALS['pdo'] : db();
        $me  = isset($GLOBALS['me']) && is_array($GLOBALS['me']) ? $GLOBALS['me'] : current_user();

        $back = comment_safe_back_url((string)(
            $_POST['back'] ??
            $_GET['back'] ??
            ($_SERVER['HTTP_REFERER'] ?? '/')
        ));

        if (!cm_is_post()) {
            http_response_code(405);
            echo 'Method Not Allowed';
            exit;
        }

        $csrf = (string)($_POST['csrf'] ?? $_POST['csrf_token'] ?? '');
        if (!function_exists('csrf_check') || !csrf_check($csrf)) {
            cm_flash_set('err', 'Bad CSRF token.');
            cm_redirect($back);
        }

        $defaultAction = (string)($opts['default_action'] ?? '');
        $action = (string)($_POST['action'] ?? $_GET['action'] ?? $defaultAction);
        $action = trim($action);

        $commentId = (int)($_POST['comment_id'] ?? $_POST['id'] ?? $_GET['comment_id'] ?? $_GET['id'] ?? 0);
        $attachmentId = (int)($_POST['attachment_id'] ?? $_POST['aid'] ?? $_GET['attachment_id'] ?? $_GET['aid'] ?? 0);

        try {
            if ($action === 'delete') {
                cm_delete_comment_core($pdo, $me, $commentId);
                cm_flash_set('ok', 'Comment deleted.');
                cm_redirect($back);
            }

            if ($action === 'delete_attachment') {
                cm_delete_comment_attachment_core($pdo, $me, $attachmentId);
                cm_flash_set('ok', 'Attachment deleted.');
                cm_redirect($back);
            }

            if ($action === 'edit') {
                $body = (string)($_POST['body'] ?? $_POST['comment'] ?? '');
                $deleteIds = cm_extract_delete_attachment_ids($_POST);

                $uploadField = null;
                foreach (['attachments', 'files', 'comment_files'] as $f) {
                    if (isset($_FILES[$f])) {
                        $uploadField = $f;
                        break;
                    }
                }

                $username = (string)($me['username'] ?? ('user_' . (int)($me['id'] ?? 0)));
                $newUploads = $uploadField !== null ? comment_handle_uploads($_FILES[$uploadField], $username) : [];

                cm_edit_comment_core($pdo, $me, $commentId, $body, $deleteIds, $newUploads);
                cm_flash_set('ok', 'Comment updated.');
                cm_redirect($back);
            }

            throw new RuntimeException('Unknown action.');
        } catch (Throwable $e) {
            cm_flash_set('err', $e->getMessage());
            cm_redirect($back);
        }
    }
}

$__cmDirect = realpath((string)($_SERVER['SCRIPT_FILENAME'] ?? ''));
if ($__cmDirect !== false && $__cmDirect === __FILE__) {
    cm_handle_request();
}