<?php

declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/wizwizxui-timebot/services/PlanInboundIds.php';
require_once dirname(__DIR__, 2) . '/wizwizxui-timebot/services/XuiV3Config.php';
require_once dirname(__DIR__, 2) . '/wizwizxui-timebot/services/XuiV3ApiClient.php';
require_once dirname(__DIR__, 2) . '/wizwizxui-timebot/services/XuiV3Service.php';

// Load Pasarguard services
$_pasarguardRoot = dirname(__DIR__, 2) . '/wizwizxui-timebot/services';
if (file_exists($_pasarguardRoot . '/PasarguardService.php')) {
    require_once $_pasarguardRoot . '/PasarguardApiClient.php';
    require_once $_pasarguardRoot . '/PasarguardConfig.php';
    require_once $_pasarguardRoot . '/PasarguardService.php';
}



class PlanInboundHelper

{

    public static function ensureSchema(mysqli $conn): void

    {

        PlanInboundIds::ensureSchema($conn);

    }



    /** @return array<string, mixed>|null */

    public static function serverConfigRow(mysqli $conn, int $serverId): ?array

    {

        if ($serverId <= 0) {

            return null;

        }

        $stmt = $conn->prepare('SELECT * FROM server_config WHERE id = ? LIMIT 1');

        $stmt->bind_param('i', $serverId);

        $stmt->execute();

        $row = $stmt->get_result()->fetch_assoc();

        $stmt->close();

        return $row ?: null;

    }



    /** @return array<int, array{id:int,label:string}> */
    public static function listInboundsFromRow(mysqli $conn, array $row): array
    {
        if (!$row || in_array($row['type'] ?? '', ['marzban', 'pasarguard'], true)) {
            return [];
        }
        if (!XuiV3Config::isPanelServer($row)) {
            return [];
        }
        try {
            $svc = new XuiV3Service($row);
            $resp = $svc->listInbounds();
            if (!$resp->success || !is_array($resp->obj)) {
                return [];
            }
            $out = [];
            foreach ($resp->obj as $inbound) {
                $id = (int) ($inbound->id ?? 0);
                if ($id <= 0) continue;
                $remark   = trim((string) ($inbound->remark ?? ''));
                $protocol = trim((string) ($inbound->protocol ?? ''));
                $port     = (int) ($inbound->port ?? 0);
                $label    = '#' . $id;
                if ($remark !== '') $label .= ' — ' . $remark;
                if ($protocol !== '') $label .= ' (' . $protocol . ($port > 0 ? ':' . $port : '') . ')';
                $out[] = ['id' => $id, 'label' => $label];
            }
            return $out;
        } catch (\Throwable $e) {
            return [];
        }
    }

    /** @return array<int, array{id:int,label:string}> */

    public static function listInbounds(mysqli $conn, int $serverId): array

    {

        $row = self::serverConfigRow($conn, $serverId);

        if (!$row || ($row['type'] ?? '') === 'marzban') {

            return [];

        }

        if (!XuiV3Config::isPanelServer($row)) {

            return [];

        }

        try {

            $svc = new XuiV3Service($row);

            $resp = $svc->listInbounds();

            if (!$resp->success || !is_array($resp->obj)) {

                return [];

            }

            $out = [];

            foreach ($resp->obj as $inbound) {

                $id = (int) ($inbound->id ?? 0);

                if ($id <= 0) {

                    continue;

                }

                $remark = trim((string) ($inbound->remark ?? ''));

                $protocol = trim((string) ($inbound->protocol ?? ''));

                $port = (int) ($inbound->port ?? 0);

                $label = '#' . $id;

                if ($remark !== '') {

                    $label .= ' — ' . $remark;

                }

                if ($protocol !== '') {

                    $label .= ' (' . $protocol;

                    if ($port > 0) {

                        $label .= ':' . $port;

                    }

                    $label .= ')';

                }

                $out[] = ['id' => $id, 'label' => $label];

            }

            return $out;

        } catch (Throwable $e) {

            return [];

        }

    }



    /** @return array{ok:bool, ids:int[]} */

    public static function fetchInboundIds(mysqli $conn, int $serverId): array

    {

        $row = self::serverConfigRow($conn, $serverId);

        if (!$row || ($row['type'] ?? '') === 'marzban' || !XuiV3Config::isPanelServer($row)) {

            return ['ok' => false, 'ids' => []];

        }

        try {

            $svc = new XuiV3Service($row);

            $resp = $svc->listInbounds();

            if (!$resp->success || !is_array($resp->obj)) {

                return ['ok' => false, 'ids' => []];

            }

            $ids = [];

            foreach ($resp->obj as $inbound) {

                $id = (int) ($inbound->id ?? 0);

                if ($id > 0) {

                    $ids[] = $id;

                }

            }

            return ['ok' => true, 'ids' => $ids];

        } catch (Throwable $e) {

            return ['ok' => false, 'ids' => []];

        }

    }



    /** @return int[] */

    public static function inboundIdList(mysqli $conn, int $serverId): array

    {

        return self::fetchInboundIds($conn, $serverId)['ids'];

    }



    /** @param int[] $rawIds @return int[] */

    public static function validateInboundIds(mysqli $conn, int $serverId, array $rawIds): array

    {

        $rawIds = array_values(array_unique(array_filter(array_map('intval', $rawIds))));

        if (empty($rawIds)) {

            return [];

        }

        $live = self::inboundIdList($conn, $serverId);

        if (empty($live)) {

            return $rawIds;

        }

        return PlanInboundIds::intersectWithLive($rawIds, $live);

    }



    public static function syncOrphanPlans(mysqli $conn, ?int $serverId = null): int

    {

        self::ensureSchema($conn);

        $fixed = 0;

        if ($serverId !== null && $serverId > 0) {

            return self::syncOrphanPlansForServer($conn, $serverId);

        }

        $res = $conn->query(

            'SELECT DISTINCT server_id FROM server_plans WHERE ' . PlanInboundIds::sqlHasInboundsCondition()

        );

        if (!$res) {

            return 0;

        }

        while ($row = $res->fetch_assoc()) {

            $fixed += self::syncOrphanPlansForServer($conn, (int) $row['server_id']);

        }

        return $fixed;

    }



    private static function syncOrphanPlansForServer(mysqli $conn, int $serverId): int

    {

        $fetch = self::fetchInboundIds($conn, $serverId);

        if (!$fetch['ok']) {

            return 0;

        }

        $valid = $fetch['ids'];

        $stmt = $conn->prepare(

            'SELECT id, inbound_id, inbound_ids FROM server_plans WHERE server_id = ? AND '

            . PlanInboundIds::sqlHasInboundsCondition()

        );

        $stmt->bind_param('i', $serverId);

        $stmt->execute();

        $rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

        $stmt->close();



        $fixed = 0;

        foreach ($rows as $plan) {

            $planId = (int) $plan['id'];

            $current = PlanInboundIds::parseFromRow($plan);

            $synced = PlanInboundIds::intersectWithLive($current, $valid);

            if ($synced !== $current) {

                PlanInboundIds::updatePlanRow($conn, $planId, $synced);

                $fixed++;

            }

        }

        return $fixed;

    }



    /** @param int[] $selectedIds */
    public static function renderInboundField(
        mysqli $conn,
        int $serverId,
        array $selectedIds,
        string $inputName,
        bool $required = false,
        ?string $currentCustomSni = null
    ): string {
        $row = self::serverConfigRow($conn, $serverId);
        $serverType = (string) ($row['type'] ?? '');

        // Pasarguard: show template picker instead of inbound select
        if ($serverType === 'pasarguard' && class_exists('PasarguardService') && $row !== null) {
            return self::renderPasarguardTemplateField($row, $currentCustomSni);
        }

        // Marzban: no inbound selection
        if ($serverType === 'marzban') {
            return '<div class="m-2"><p class="text-xs text-blue-500">ℹ سرور Marzban از انتخاب inbound در پلن پشتیبانی نمی‌کند.</p></div>';
        }

        $inbounds = self::listInbounds($conn, $serverId);
        $selectedIds = array_values(array_unique(array_filter(array_map('intval', $selectedIds))));
        $selectedSet = array_flip($selectedIds);
        $req  = $required ? ' required' : '';
        $name = htmlspecialchars($inputName, ENT_QUOTES);
        $html = '<div class="m-2"><span class="text-sm text-gray-600 font-medium">Inboundها <span class="font-normal text-gray-400">(Ctrl+کلیک برای چند انتخاب)</span></span>';

        if (empty($inbounds)) {
            $html .= '<p class="text-xs text-orange-500 mt-1">⚠ لیست inbound از پنل دریافت نشد. اتصال را بررسی کنید.</p>';
            foreach ($selectedIds as $sid) {
                $html .= '<input type="hidden" name="' . $name . '[]" value="' . (int) $sid . '"/>';
            }
            $html .= '<input name="' . $name . '[]" type="number" placeholder="ID inbound را وارد کنید" class="mt-1 w-full border rounded px-3 py-2 text-sm"' . $req . '/></div>';
            return $html;
        }

        $size = min(max(count($inbounds), 3), 8);
        $html .= '<select name="' . $name . '[]" multiple size="' . $size . '" class="mt-1 w-full border border-gray-300 rounded px-3 py-2 text-sm"' . $req . '>';
        foreach ($inbounds as $ib) {
            $id  = (int) $ib['id'];
            $sel = isset($selectedSet[$id]) ? ' selected' : '';
            $html .= '<option value="' . $id . '"' . $sel . '>' . htmlspecialchars($ib['label'], ENT_QUOTES) . '</option>';
        }
        foreach ($selectedIds as $sid) {
            $onPanel = false;
            foreach ($inbounds as $ib) {
                if ((int) $ib['id'] === $sid) { $onPanel = true; break; }
            }
            if (!$onPanel) {
                $html .= '<option value="' . $sid . '" selected>⚠ #' . $sid . ' (حذف شده از پنل)</option>';
            }
        }
        $html .= '</select>';
        $liveCount = count(array_intersect($selectedIds, array_column($inbounds, 'id')));
        if (!empty($selectedIds)) {
            $html .= '<p class="text-xs text-gray-500 mt-1">انتخاب‌شده: ' . count($selectedIds) . ' — فعال روی پنل: ' . $liveCount . '</p>';
        }
        $html .= '</div>';
        return $html;
    }

    /** @param array<string,mixed> $serverRow */
    private static function renderPasarguardTemplateField(array $serverRow, ?string $currentCustomSni): string
    {
        $currentTemplateId = 0;
        if ($currentCustomSni !== null && $currentCustomSni !== '') {
            $decoded = json_decode($currentCustomSni, true);
            if (is_array($decoded)) {
                $currentTemplateId = (int) ($decoded['template_id'] ?? $decoded['user_template_id'] ?? 0);
            }
        }
        try {
            $svc = new PasarguardService($serverRow);
            $templates = $svc->listUserTemplates();
        } catch (\Throwable $e) {
            $templates = [];
        }
        $html = '<div class="m-2">';
        $html .= '<span class="text-sm text-gray-600 font-medium">User Template پاسارگارد</span>';
        if (empty($templates)) {
            $html .= '<p class="text-xs text-orange-500 mt-1">⚠ هیچ User Template‌ای در پنل Pasarguard یافت نشد.</p>';
            $html .= '<input type="hidden" name="pasarguard_template_id" value="' . $currentTemplateId . '"/>';
            $html .= '</div>';
            return $html;
        }
        $html .= '<select name="pasarguard_template_id" class="mt-1 w-full border border-gray-300 rounded px-3 py-2 text-sm">';
        $html .= '<option value="">— انتخاب template —</option>';
        foreach ($templates as $tpl) {
            $tid  = (int) ($tpl->id ?? 0);
            $name = trim((string) ($tpl->name ?? ''));
            $lbl  = $name !== '' ? $name : 'Template #' . $tid;
            $sel  = ($currentTemplateId === $tid) ? ' selected' : '';
            $html .= '<option value="' . $tid . '"' . $sel . '>' . htmlspecialchars($lbl, ENT_QUOTES) . '</option>';
        }
        $html .= '</select>';
        $html .= '<p class="text-xs text-gray-400 mt-1">تنظیمات پروتکل و حجم از User Template دریافت می‌شود.</p>';
        $html .= '</div>';
        return $html;
    }



    /** @return int[] */

    public static function parsePostedInboundIds(): array

    {

        $raw = $_POST['inbound_ids_plan'] ?? $_POST['inbound_id_plan'] ?? $_POST['inbound_plan'] ?? [];

        if (!is_array($raw)) {

            $raw = [$raw];

        }

        return array_values(array_unique(array_filter(array_map('intval', $raw))));

    }

}



class WizPanelPostHandler

{

    public static function process(mysqli $conn): void

    {

        PlanInboundHelper::ensureSchema($conn);

        if (isset($_POST['btn_edit']) && !empty($_POST['server_id'])) {

            self::handleServerEdit($conn);

        }

        if (isset($_POST['btn_edit']) && !empty($_POST['plan_id'])) {

            self::handlePlanEdit($conn);

        }

    }



    private static function handleServerEdit(mysqli $conn): void

    {

        $serverId = (int) $_POST['server_id'];

        if ($serverId <= 0) {

            return;

        }

        $ip = (string) ($_POST['ip_server'] ?? '');

        $panelUrl = rtrim((string) ($_POST['panel_url_server'] ?? ''), '/');

        $sni = (string) ($_POST['sni_server'] ?? '');

        $headerType = (string) ($_POST['header_type_server'] ?? '');

        $requestHeader = (string) ($_POST['request_header_server'] ?? '');

        $responseHeader = (string) ($_POST['response_header_server'] ?? '');

        $security = (string) ($_POST['security_server'] ?? '');

        $tlsSettings = (string) ($_POST['tlsSettings_server'] ?? '');

        $username = (string) ($_POST['username_server'] ?? '');

        $password = (string) ($_POST['password_server'] ?? '');

        $apiToken = (string) ($_POST['api_token_server'] ?? '');

        $subBase = (string) ($_POST['subscription_base_url_server'] ?? '');

        $title = (string) ($_POST['title_server'] ?? '');

        $ucount = (int) ($_POST['ucount_server'] ?? 0);

        $remark = (string) ($_POST['remark_server'] ?? '');

        $flag = (string) ($_POST['flag_server'] ?? '');



        $stmt = $conn->prepare('UPDATE server_config SET ip=?, panel_url=?, sni=?, header_type=?, request_header=?, response_header=?, security=?, tlsSettings=?, username=?, password=?, api_token=?, subscription_base_url=? WHERE id = ?');

        $stmt->bind_param('ssssssssssssi', $ip, $panelUrl, $sni, $headerType, $requestHeader, $responseHeader, $security, $tlsSettings, $username, $password, $apiToken, $subBase, $serverId);

        if (!$stmt->execute()) {

            $_SESSION['flash_message'] = 'خطا: ' . $stmt->error;

            $stmt->close();

            header('Location: servers.php');

            exit;

        }

        $stmt->close();

        $stmt = $conn->prepare('UPDATE server_info SET title=?, ucount=?, remark=?, flag=? WHERE id = ?');

        $stmt->bind_param('sissi', $title, $ucount, $remark, $flag, $serverId);

        if (!$stmt->execute()) {

            $_SESSION['flash_message'] = 'خطا: ' . $stmt->error;

            $stmt->close();

            header('Location: servers.php');

            exit;

        }

        $stmt->close();

        PlanInboundHelper::syncOrphanPlans($conn, $serverId);

        editwizwiz();

        header('Location: servers.php');

        exit;

    }



    private static function handlePlanEdit(mysqli $conn): void

    {

        $planId = (int) $_POST['plan_id'];

        $page = (string) ($_POST['plan_edit_page'] ?? 'multiple');

        if ($planId <= 0) {

            return;

        }

        $redirect = $page === 'single' ? 'singleplans.php' : ($page === 'rahgozar' ? 'rahgozar.php' : 'multipleplans.php');



        $title = (string) ($_POST['title_plan'] ?? '');

        $protocol = (string) ($_POST['protocol_plan'] ?? '');

        $type = (string) ($_POST['type_plan'] ?? '');

        $price = (int) ($_POST['price_plan'] ?? 0);

        $days = (int) ($_POST['days_plan'] ?? 0);

        $volume = (int) ($_POST['volume_plan'] ?? 0);

        $limitip = (int) ($_POST['limitip_plan'] ?? 1);

        $catId = (int) ($_POST['name_category_wizwiz'] ?? 0);

        $serverId = (int) ($_POST['name_server_wizwiz'] ?? 0);

        $description = (string) ($_POST['description_plan'] ?? '');



        if ($page === 'single') {

            $inboundIds = [];

        } else {
            $inboundIds = PlanInboundHelper::validateInboundIds($conn, $serverId, PlanInboundHelper::parsePostedInboundIds());
        }

        $inboundIdsJson = PlanInboundIds::encode($inboundIds);
        $inboundIdLegacy = PlanInboundIds::legacyInboundId($inboundIds);

        $acount = array_key_exists('acount_plan', $_POST) ? (int) $_POST['acount_plan']
            : (array_key_exists('count_plan', $_POST) ? (int) $_POST['count_plan'] : null);
        if ($acount === null) {
            $stmtA = $conn->prepare('SELECT acount FROM server_plans WHERE id = ? LIMIT 1');
            $stmtA->bind_param('i', $planId);
            $stmtA->execute();
            $rowA = $stmtA->get_result()->fetch_assoc();
            $stmtA->close();
            $acount = (int) ($rowA['acount'] ?? 0);
        }

        // Detect Pasarguard server to save group_id as JSON in custom_sni
        $serverRow = PlanInboundHelper::serverConfigRow($conn, $serverId);
        $isPasarguard = ($serverRow !== null) && (($serverRow['type'] ?? '') === 'pasarguard');

        if ($page === 'rahgozar') {
            $customPath = (string) ($_POST['custom_path_plan'] ?? '');
            $customPort = (string) ($_POST['custom_port_plan'] ?? '');
            $customSni  = (string) ($_POST['custom_sni_plan'] ?? '');

            $sql  = 'UPDATE server_plans SET title=?, protocol=?, type=?, price=?, days=?, volume=?, limitip=?, inbound_id=?, inbound_ids=?, descr=?, catid=?, server_id=?, acount=?, custom_path=?, custom_port=?, custom_sni=? WHERE id = ?';
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('sssiiiiissiiisssi', $title, $protocol, $type, $price, $days, $volume, $limitip, $inboundIdLegacy, $inboundIdsJson, $description, $catId, $serverId, $acount, $customPath, $customPort, $customSni, $planId);
        } elseif ($isPasarguard) {
            // Pasarguard: save group_id (and optional legacy template_id) as JSON in custom_sni
            // protocol/type are not relevant for Pasarguard — use safe defaults
            $pgProtocol = $protocol !== '' ? $protocol : 'vless';
            $pgType     = $type     !== '' ? $type     : 'tcp';
            $groupId    = (int) ($_POST['pasarguard_group_id'] ?? 0);
            $templateId = (int) ($_POST['pasarguard_template_id'] ?? 0);
            $customSni  = json_encode([
                'pasarguard'  => true,
                'group_id'    => $groupId,
                'template_id' => $templateId,
            ], JSON_UNESCAPED_UNICODE);

            // SQL: 15 placeholders → type string must be exactly 15 chars: sssiiiiissiiisi
            $sql  = 'UPDATE server_plans SET title=?, protocol=?, type=?, price=?, days=?, volume=?, limitip=?, inbound_id=?, inbound_ids=?, descr=?, catid=?, server_id=?, acount=?, custom_sni=? WHERE id = ?';
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('sssiiiiissiiisi', $title, $pgProtocol, $pgType, $price, $days, $volume, $limitip, $inboundIdLegacy, $inboundIdsJson, $description, $catId, $serverId, $acount, $customSni, $planId);
        } else {
            $dest        = trim((string) ($_POST['dest_reality'] ?? '')) ?: null;
            $serverNames = trim((string) ($_POST['serverNames_reality'] ?? '')) ?: null;
            $spiderX     = trim((string) ($_POST['spiderX_reality'] ?? '')) ?: null;
            $flow        = trim((string) ($_POST['flow_reality'] ?? '')) ?: null;

            $sql  = 'UPDATE server_plans SET title=?, protocol=?, type=?, price=?, days=?, volume=?, limitip=?, inbound_id=?, inbound_ids=?, descr=?, catid=?, server_id=?, acount=?, dest=?, serverNames=?, spiderX=?, flow=? WHERE id = ?';
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('sssiiiiissiiissssi', $title, $protocol, $type, $price, $days, $volume, $limitip, $inboundIdLegacy, $inboundIdsJson, $description, $catId, $serverId, $acount, $dest, $serverNames, $spiderX, $flow, $planId);
        }

        if (!$stmt->execute()) {
            $_SESSION['flash_message'] = 'خطا: ' . $stmt->error;
            $stmt->close();
            header('Location: ' . $redirect);
            exit;
        }
        $stmt->close();
        editwizwiz();
        header('Location: ' . $redirect);
        exit;
    }

}


