<?php
declare(strict_types=1);

/**
 * /functional/group/join.php
 * Вступ у групу:
 * - самостійний вступ у відкриту групу
 * - подача заявки у закриту групу
 * - legacy/pending invite => active
 * - pending request не активується користувачем самостійно
 * - внутрішнє notification + email для owner/admin при заявці у закриту групу
 *
 * ОНОВЛЕНО:
 * - admin сайту більше не обходить правила закритої групи
 * - для всіх користувачів логіка однакова:
 *   відкрита група => joined
 *   закрита група => request_sent
 */

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

$notificationsEvents = __DIR__ . '/../notifications/events.php';
if (is_file($notificationsEvents)) {
    require_once $notificationsEvents;
}

if (!function_exists('g_join_notify_manager_ids')) {
    /**
     * Повертає список user_id менеджерів групи, яким треба слати заявку на вступ:
     * - owner_user_id із groups
     * - active owner/admin із group_members
     * - без дублів
     * - без $excludeUserId
     */
    function g_join_notify_manager_ids(PDO $pdo, array $g, int $excludeUserId = 0): array
    {
        $out = [];

        $groupId = (int)($g['id'] ?? 0);
        if ($groupId <= 0) {
            return [];
        }

        $ownerId = (int)($g['owner_user_id'] ?? 0);
        if ($ownerId > 0 && $ownerId !== $excludeUserId) {
            $out[$ownerId] = $ownerId;
        }

        if (!g_table_exists($pdo, 'group_members') || !g_column_exists($pdo, 'group_members', 'role')) {
            return array_values($out);
        }

        try {
            $where = [
                "group_id = ?",
                "role IN ('owner','admin')",
            ];
            $args = [$groupId];

            if (g_column_exists($pdo, 'group_members', 'status')) {
                $where[] = "status = 'active'";
            }

            if ($excludeUserId > 0) {
                $where[] = "user_id <> ?";
                $args[] = $excludeUserId;
            }

            $sql = "
                SELECT user_id
                FROM group_members
                WHERE " . implode(' AND ', $where) . "
            ";

            $st = $pdo->prepare($sql);
            $st->execute($args);

            while ($uid = $st->fetchColumn()) {
                $uid = (int)$uid;
                if ($uid > 0 && $uid !== $excludeUserId) {
                    $out[$uid] = $uid;
                }
            }
        } catch (Throwable $e) {
            error_log('[group join notify managers] ' . $e->getMessage());
        }

        return array_values($out);
    }
}

/* SEO: endpoint не індексуємо */
if (!headers_sent()) {
    header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
    header('Cache-Control: no-store, max-age=0', true);
}

if (function_exists('require_login')) {
    require_login();
} else {
    $me0 = g_me();
    if (!$me0) {
        header('Location: /login.php');
        exit;
    }
}

$pdo = g_db();
$me  = g_me();

if (!$me || (int)($me['id'] ?? 0) <= 0) {
    header('Location: /login.php');
    exit;
}

/* group resolve: by id or slug */
$id   = (int)($_GET['id'] ?? 0);
$slug = trim((string)($_GET['g'] ?? ''));

$g = null;
if ($id > 0) {
    $g = g_group_get($pdo, $id);
} elseif ($slug !== '') {
    $g = g_group_get_by_slug($pdo, $slug);
}

if (!$g) {
    http_response_code(404);
    echo "Group not found";
    exit;
}

/* group_members table must exist */
if (!g_table_exists($pdo, 'group_members')) {
    http_response_code(500);
    echo "DB schema missing: group_members. Run migration.";
    exit;
}

/* owner already in group */
if ((int)($g['owner_user_id'] ?? 0) === (int)$me['id']) {
    header('Location: ' . g_group_url($g));
    exit;
}

/* schema flags for group_members */
$gmHasStatus      = g_column_exists($pdo, 'group_members', 'status');
$gmHasRole        = g_column_exists($pdo, 'group_members', 'role');
$gmHasCreatedAt   = g_column_exists($pdo, 'group_members', 'created_at');
$gmHasPendingKind = g_column_exists($pdo, 'group_members', 'pending_kind');

/* membership first */
$mem = g_membership($pdo, (int)$g['id'], (int)$me['id']);
$curStatus = $gmHasStatus ? (string)($mem['status'] ?? '') : ($mem ? 'active' : '');
$curPendingKind = $gmHasPendingKind ? trim((string)($mem['pending_kind'] ?? '')) : '';

/* banned => no join */
if ($gmHasStatus && $curStatus === 'banned') {
    $url = g_group_url($g);
    $sep = (strpos($url, '?') !== false) ? '&' : '?';
    header('Location: ' . $url . $sep . 'm=banned');
    exit;
}

$isClosed = false;
if (g_column_exists($pdo, 'groups', 'is_closed')) {
    $isClosed = ((int)($g['is_closed'] ?? 0) === 1);
}

$sendJoinRequestEmail = false;
$sendJoinRequestNotif = false;
$redirectMark = '';

try {
    $pdo->beginTransaction();

    if ($mem) {
        if ($gmHasStatus) {
            if ($curStatus === 'active') {
                $redirectMark = 'joined';
            } elseif ($curStatus === 'pending') {
                /*
                 * Тепер pending розділяємо:
                 * - invite  => користувач може прийняти (legacy / старі записи без kind теж вважаємо invite)
                 * - request => користувач НЕ може сам себе активувати
                 */
                $isInvite = ($curPendingKind === '' || $curPendingKind === 'invite');
                $isRequest = ($curPendingKind === 'request');

                if ($isInvite) {
                    $sql = "UPDATE group_members SET status='active'";
                    $args = [];

                    if ($gmHasPendingKind) {
                        $sql .= ", pending_kind=NULL";
                    }

                    $sql .= " WHERE group_id=? AND user_id=?";
                    $args[] = (int)$g['id'];
                    $args[] = (int)$me['id'];

                    $st = $pdo->prepare($sql);
                    $st->execute($args);

                    $redirectMark = 'joined';
                } elseif ($isRequest) {
                    $redirectMark = 'request_sent';
                } else {
                    $redirectMark = 'request_sent';
                }
            }
        } else {
            $redirectMark = 'joined';
        }
    } else {
        $statusToInsert = 'active';
        $pendingKindToInsert = null;

        if ($isClosed) {
            if ($gmHasStatus) {
                $statusToInsert = 'pending';
                $pendingKindToInsert = 'request';
                $sendJoinRequestEmail = true;
                $sendJoinRequestNotif = true;
                $redirectMark = 'request_sent';
            } else {
                $pdo->rollBack();
                $url = g_group_url($g);
                $sep = (strpos($url, '?') !== false) ? '&' : '?';
                header('Location: ' . $url . $sep . 'm=closed');
                exit;
            }
        } else {
            $redirectMark = 'joined';
        }

        $cols = ['group_id', 'user_id'];
        $vals = [(int)$g['id'], (int)$me['id']];

        if ($gmHasRole) {
            $cols[] = 'role';
            $vals[] = 'member';
        }
        if ($gmHasStatus) {
            $cols[] = 'status';
            $vals[] = $statusToInsert;
        }
        if ($gmHasPendingKind) {
            $cols[] = 'pending_kind';
            $vals[] = $pendingKindToInsert;
        }
        if ($gmHasCreatedAt) {
            $cols[] = 'created_at';
            $vals[] = g_now();
        }

        $ph = implode(',', array_fill(0, count($cols), '?'));
        $sql = "INSERT INTO group_members(" . implode(',', $cols) . ") VALUES($ph)";
        $ins = $pdo->prepare($sql);
        $ins->execute($vals);
    }

    $pdo->commit();
} catch (Throwable $e) {
    try {
        if ($pdo->inTransaction()) {
            $pdo->rollBack();
        }
    } catch (Throwable $e2) {
    }

    error_log('[group join] ' . $e->getMessage());
    $url = g_group_url($g);
    $sep = (strpos($url, '?') !== false) ? '&' : '?';
    header('Location: ' . $url . $sep . 'm=join_error');
    exit;
}

if ($sendJoinRequestNotif || $sendJoinRequestEmail) {
    try {
        $targets = g_join_notify_manager_ids($pdo, $g, (int)$me['id']);

        foreach ($targets as $targetUserId) {
            $targetUserId = (int)$targetUserId;
            if ($targetUserId <= 0 || $targetUserId === (int)$me['id']) {
                continue;
            }

            if ($sendJoinRequestNotif) {
                g_notification_upsert_group_join_request($pdo, $targetUserId, (int)$me['id'], $g);
            }

            if ($sendJoinRequestEmail && function_exists('ntf_dispatch_group_join_request')) {
                $dedupeKey = 'gjr:g' . (int)$g['id'] . ':from' . (int)$me['id'] . ':to' . $targetUserId;

                ntf_dispatch_group_join_request(
                    $pdo,
                    (int)$me['id'],
                    $targetUserId,
                    (int)$g['id'],
                    $dedupeKey
                );
            }
        }
    } catch (Throwable $e) {
        error_log('[group join notify] ' . $e->getMessage());
    }
}

$url = g_group_url($g);
if ($redirectMark !== '') {
    $sep = (strpos($url, '?') !== false) ? '&' : '?';
    $url .= $sep . 'm=' . rawurlencode($redirectMark);
}

header('Location: ' . $url);
exit;