<?php
declare(strict_types=1);

/**
 * /functional/group/invite.php
 * Запросити користувача в групу:
 * - лише manager (owner/admin) групи
 * - POST + CSRF
 * - створює або переводить запис у group_members в status='pending'
 * - якщо вже є pending -> залишає pending і може тихо повторно спробувати email з cooldown
 * - якщо вже є active -> не дублює
 * - якщо є notifications-модуль email -> надсилає group invite
 */

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

$__ntfLib = dirname(__DIR__) . '/notifications/_lib.php';
if (is_file($__ntfLib)) {
    require_once $__ntfLib;
}
unset($__ntfLib);

require_login();

$pdo = g_db();
$me  = g_me();
$meId = (int)($me['id'] ?? 0);

function json_out(array $data, int $code = 200): void {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    header('X-Content-Type-Options: nosniff');
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

function safe_back_url(string $raw): string {
    $raw = trim($raw);
    if ($raw === '') return '/';

    if (preg_match('~^https?://~i', $raw)) {
        $p = parse_url($raw);
        $path = (string)($p['path'] ?? '/');
        $q = isset($p['query']) && $p['query'] !== '' ? ('?' . $p['query']) : '';
        $h = isset($p['fragment']) && $p['fragment'] !== '' ? ('#' . $p['fragment']) : '';
        $raw = $path . $q . $h;
    }

    if ($raw === '' || $raw[0] !== '/') return '/';
    if (str_starts_with($raw, '//')) return '/';

    return $raw;
}

$isAjax = (strtolower((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '')) === 'xmlhttprequest')
    || str_contains(strtolower((string)($_SERVER['HTTP_ACCEPT'] ?? '')), 'application/json');

$back = safe_back_url((string)post_str('back'));
if ($back === '/' || $back === '') {
    $ref = (string)($_SERVER['HTTP_REFERER'] ?? '');
    $back = safe_back_url($ref);
}

if (!is_post()) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'method'], 405);
    http_response_code(405);
    echo "Method Not Allowed";
    exit;
}

if (!csrf_check(post_str('csrf'))) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'csrf'], 400);
    flash_set('err', t('bad_csrf'));
    redirect($back);
}

$groupId = (int)post_str('group_id');
$userId  = (int)post_str('user_id');

if ($groupId <= 0 || $userId <= 0) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'bad'], 400);
    redirect($back);
}

$g = g_group_get($pdo, $groupId);
if (!$g) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'group_not_found'], 404);
    redirect($back);
}

if (!g_can_manage_group($me, $g)) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'forbidden'], 403);
    http_response_code(403);
    echo "Forbidden";
    exit;
}

if (!g_column_exists($pdo, 'group_members', 'status')) {
    $msg = g_t(
        'Для запрошень у групи потрібна колонка status у group_members.',
        'Для приглашений в группы нужна колонка status в group_members.',
        'Group invites require the status column in group_members.'
    );
    if ($isAjax) json_out(['ok' => false, 'error' => 'schema_status_missing', 'message' => $msg], 500);
    flash_set('err', $msg);
    redirect($back);
}

// user exists?
$stU = $pdo->prepare("SELECT id FROM users WHERE id=? LIMIT 1");
$stU->execute([$userId]);
if (!$stU->fetchColumn()) {
    if ($isAjax) json_out(['ok' => false, 'error' => 'user_not_found'], 404);
    redirect($back);
}

$gmHasRole = g_column_exists($pdo, 'group_members', 'role');
$gmHasCreatedAt = g_column_exists($pdo, 'group_members', 'created_at');

try {
    $selectCols = ['user_id', 'status'];
    if ($gmHasRole) $selectCols[] = 'role';

    $st = $pdo->prepare("
        SELECT " . implode(', ', $selectCols) . "
        FROM group_members
        WHERE group_id=? AND user_id=?
        LIMIT 1
    ");
    $st->execute([$groupId, $userId]);
    $row = $st->fetch(PDO::FETCH_ASSOC);

    $status = trim((string)($row['status'] ?? ''));

    if ($row) {
        if ($status === 'banned') {
            if ($isAjax) json_out(['ok' => false, 'error' => 'banned'], 400);
            flash_set('err', g_t('Користувач забанений у групі.', 'Пользователь забанен в группе.', 'User is banned in the group.'));
            redirect($back);
        }

        if ($status === 'active') {
            if ($isAjax) {
                json_out([
                    'ok' => true,
                    'status' => 'active',
                    'already' => true,
                    'message' => g_t('Користувач уже є учасником групи.', 'Пользователь уже является участником группы.', 'User is already a group member.')
                ], 200);
            }

            flash_set('ok', g_t('Користувач уже є учасником групи.', 'Пользователь уже является участником группы.', 'User is already a group member.'));
            redirect($back);
        }

        $updateParts = ["status='pending'"];
        if ($gmHasRole) {
            $updateParts[] = "role='member'";
        }

        $sql = "UPDATE group_members SET " . implode(', ', $updateParts) . " WHERE group_id=? AND user_id=? LIMIT 1";
        $st = $pdo->prepare($sql);
        $st->execute([$groupId, $userId]);

        $emailSent = false;
        if (function_exists('ntf_dispatch_group_invite')) {
            try {
                $emailSent = ntf_dispatch_group_invite($pdo, $meId, $userId, $groupId);
            } catch (Throwable $e) {
                error_log('group/invite.php notification error (existing pending): ' . $e->getMessage());
            }
        }

        if ($isAjax) {
            json_out([
                'ok' => true,
                'status' => 'pending',
                'already' => true,
                'email_sent' => $emailSent,
            ], 200);
        }

        flash_set(
            'ok',
            g_t(
                'Запрошення вже активне. Якщо потрібно, email буде надіслано з урахуванням антиспаму.',
                'Приглашение уже активно. Если нужно, email будет отправлен с учётом антиспама.',
                'The invite is already active. If needed, the email will be sent with anti-spam limits.'
            )
        );
        redirect($back);
    }

    $fields = ['group_id', 'user_id', 'status'];
    $placeholders = ['?', '?', '?'];
    $values = [$groupId, $userId, 'pending'];

    if ($gmHasRole) {
        $fields[] = 'role';
        $placeholders[] = '?';
        $values[] = 'member';
    }

    if ($gmHasCreatedAt) {
        $fields[] = 'created_at';
        $placeholders[] = 'NOW()';
    }

    $sql = "INSERT INTO group_members (" . implode(', ', $fields) . ") VALUES (" . implode(', ', $placeholders) . ")";
    $st = $pdo->prepare($sql);
    $st->execute($values);

    $emailSent = false;
    if (function_exists('ntf_dispatch_group_invite')) {
        try {
            $emailSent = ntf_dispatch_group_invite($pdo, $meId, $userId, $groupId);
        } catch (Throwable $e) {
            error_log('group/invite.php notification error (new pending): ' . $e->getMessage());
        }
    }

    if ($isAjax) {
        json_out([
            'ok' => true,
            'status' => 'pending',
            'already' => false,
            'email_sent' => $emailSent,
        ], 200);
    }

    flash_set(
        'ok',
        g_t(
            'Запрошення надіслано.',
            'Приглашение отправлено.',
            'Invite sent.'
        )
    );
    redirect($back);
} catch (Throwable $e) {
    error_log('group/invite.php error: ' . $e->getMessage());
    if ($isAjax) json_out(['ok' => false, 'error' => 'server'], 500);
    flash_set('err', t('error') ?: 'Error');
    redirect($back);
}