<?php
if (!defined('AYCORE_SKIP_BOTAPI_ROUTER')) define('AYCORE_SKIP_BOTAPI_ROUTER', true);
session_start();
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/../rebecca.php';
require_once __DIR__ . '/../lib/panel_capabilities.php';
require_once __DIR__ . '/lib/icons.php';

$q = $pdo->prepare('SELECT * FROM admin WHERE username=:u');
$q->execute([':u' => $_SESSION['user'] ?? '']);
if (!isset($_SESSION['user']) || !$q->fetch(PDO::FETCH_ASSOC)) { header('Location: login.php'); exit; }
if (empty($_SESSION['csrf_token'])) $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
$csrf = $_SESSION['csrf_token'];
$flash = ['ok'=>'','err'=>''];
$apiResult = null;
$apiOperation = null;

function itz_rb_panel(PDO $pdo, string $code): ?array {
    $s=$pdo->prepare("SELECT * FROM marzban_panel WHERE code_panel=:c AND type='rebecca' LIMIT 1");
    $s->execute([':c'=>$code]); $r=$s->fetch(PDO::FETCH_ASSOC); return $r ?: null;
}
function itz_rb_csrf(string $csrf): void {
    if (!hash_equals($csrf, (string)($_POST['_csrf'] ?? ''))) { http_response_code(403); exit('درخواست معتبر نیست.'); }
}
function itz_rb_mask(string $v): string {
    if ($v==='') return 'تنظیم نشده';
    if (strlen($v)<=8) return str_repeat('•', max(4,strlen($v)));
    return substr($v,0,4).'••••••'.substr($v,-4);
}
function itz_rb_redact($value) {
    if (!is_array($value)) return $value;
    $out=[];
    foreach ($value as $k=>$v) {
        $key=strtolower((string)$k);
        if (preg_match('/(^|_)(password|passwd|secret|token|api[_-]?key|credential)(_|$)/i',$key)) {
            $out[$k]=is_scalar($v) && (string)$v!=='' ? '••••••REDACTED••••••' : $v;
        } else $out[$k]=is_array($v)?itz_rb_redact($v):$v;
    }
    return $out;
}
function itz_rb_spec_from_panel(array $panel): array {
    $cached=json_decode((string)($panel['rb_openapi_cache']??''),true);
    if (is_array($cached) && !empty($cached['paths'])) return $cached;
    $live=rb_get_openapi($panel);
    return !empty($live['ok']) && is_array($live['spec']??null) ? $live['spec'] : [];
}
function itz_rb_service_id(array $row): int { return (int)($row['id'] ?? $row['service_id'] ?? 0); }
function itz_rb_service_name(array $row): string { $id=itz_rb_service_id($row); return trim((string)($row['name']??$row['title']??'')) ?: ('Service #'.$id); }

$panels=[];
try { $panels=$pdo->query("SELECT * FROM marzban_panel WHERE type='rebecca' ORDER BY id DESC")->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch(Throwable $e){$flash['err']=$e->getMessage();}
$selectedCode=trim((string)($_GET['panel'] ?? $_POST['code_panel'] ?? ($panels[0]['code_panel']??'')));
$selected=$selectedCode!==''?itz_rb_panel($pdo,$selectedCode):null;

if ($_SERVER['REQUEST_METHOD']==='POST') {
    itz_rb_csrf($csrf);
    $action=(string)($_POST['_action']??'');
    try {
        $panel=itz_rb_panel($pdo,(string)($_POST['code_panel']??''));
        if (!$panel) throw new RuntimeException('Backend Rebecca پیدا نشد.');
        $selected=$panel; $selectedCode=(string)$panel['code_panel'];
        if ($action==='test') {
            $r=rb_test_connection($panel);
            if (empty($r['ok'])) throw new RuntimeException($r['message']??'اتصال ناموفق بود.');
            $pdo->prepare("UPDATE marzban_panel SET rb_last_sync=:t WHERE code_panel=:c")->execute([':t'=>time(),':c'=>$selectedCode]);
            $flash['ok']='اتصال Rebecca سالم است و احراز هویت تأیید شد.';
        } elseif ($action==='sync_services') {
            $r=rb_list_services($panel); if(empty($r['ok'])) throw new RuntimeException($r['error']??'دریافت Serviceها ناموفق بود.');
            $items=array_values(array_filter((array)$r['items'],'is_array'));
            $default=(int)($panel['rb_default_service_id']??0);
            $ids=[]; foreach($items as $it){$id=itz_rb_service_id($it);if($id>0)$ids[]=$id;}
            if($default<=0 && count($ids)===1)$default=$ids[0];
            if($default>0 && $ids && !in_array($default,$ids,true))$default=0;
            $pdo->prepare("UPDATE marzban_panel SET rb_service_snapshot=:s,rb_default_service_id=:d,rb_last_sync=:t WHERE code_panel=:c")
                ->execute([':s'=>json_encode($items,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':d'=>$default?:null,':t'=>time(),':c'=>$selectedCode]);
            $flash['ok']='Serviceها Sync شدند: '.count($items).' مورد.';
        } elseif ($action==='set_default_service') {
            $service=max(0,(int)($_POST['service_id']??0));
            $r=rb_list_services($panel); if(empty($r['ok'])) throw new RuntimeException($r['error']??'اعتبارسنجی Service ناموفق بود.');
            $valid=false; foreach((array)$r['items'] as $it){if(is_array($it)&&itz_rb_service_id($it)===$service){$valid=true;break;}}
            if(!$valid) throw new RuntimeException('Service انتخاب‌شده دیگر در Rebecca وجود ندارد. ابتدا Sync کن.');
            $pdo->prepare("UPDATE marzban_panel SET rb_default_service_id=:d,rb_service_snapshot=:s,rb_last_sync=:t WHERE code_panel=:c")
                ->execute([':d'=>$service,':s'=>json_encode($r['items'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':t'=>time(),':c'=>$selectedCode]);
            $flash['ok']='Service پیش‌فرض ساخت کاربر ذخیره شد.';
        } elseif ($action==='sync_openapi') {
            $r=rb_get_openapi($panel); if(empty($r['ok'])||!is_array($r['spec']??null)) throw new RuntimeException($r['error']??'OpenAPI دریافت نشد.');
            $ops=rb_openapi_operations($r['spec']);
            $pdo->prepare("UPDATE marzban_panel SET rb_openapi_cache=:s,rb_openapi_last_sync=:t,rb_capabilities=:cap WHERE code_panel=:c")
                ->execute([':s'=>json_encode($r['spec'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':t'=>time(),':cap'=>json_encode($ops,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':c'=>$selectedCode]);
            $flash['ok']='OpenAPI خود Rebecca Sync شد: '.count($ops).' عملیات قابل کشف.';
        } elseif ($action==='api_call') {
            $method=strtoupper(trim((string)($_POST['api_method']??'GET')));
            $path=trim((string)($_POST['api_path']??''));
            if(strpos($path,'http://')===0||strpos($path,'https://')===0||strpos($path,'..')!==false) throw new RuntimeException('فقط مسیر API داخلی مثل /api/users مجاز است.');
            $spec=itz_rb_spec_from_panel($panel);
            if(!$spec) throw new RuntimeException('OpenAPI در دسترس نیست؛ ابتدا Sync OpenAPI را بزن.');
            $match=rb_openapi_match($spec,$method,$path);
            if(!$match) throw new RuntimeException('این Method/Path در OpenAPI همین نسخه Rebecca وجود ندارد.');
            if(in_array($method,['POST','PUT','PATCH','DELETE'],true) && (string)($_POST['confirm_mutation']??'')!=='1') throw new RuntimeException('برای عملیات تغییردهنده تیک تأیید را فعال کن.');
            $payload=null;
            if(in_array($method,['POST','PUT','PATCH'],true)) {
                $raw=trim((string)($_POST['api_payload']??''));
                if($raw!=='') { $payload=json_decode($raw,true); if(!is_array($payload)) throw new RuntimeException('Payload باید JSON معتبر باشد.'); }
                else $payload=[];
            }
            $r=rb_request($panel,$method,$path,$payload);
            $apiOperation=$match;
            $reveal=(string)($_POST['reveal_secret']??'')==='1';
            $body=is_array($r['json']??null)?$r['json']:(string)($r['raw']??'');
            $apiResult=['ok'=>!empty($r['ok']),'http_code'=>(int)($r['http_code']??0),'body'=>$reveal?$body:itz_rb_redact($body),'error'=>!empty($r['ok'])?'':rb_error_message($r)];
            if($apiResult['ok']) $flash['ok']='عملیات OpenAPI اجرا شد: '.$method.' '.$path;
            else $flash['err']='Rebecca API پاسخ خطا داد: '.$apiResult['error'];
        }
        $selected=itz_rb_panel($pdo,$selectedCode) ?: $selected;
    } catch(Throwable $e) { $flash['err']=$e->getMessage(); error_log('[Rebecca Hub] '.$e->getMessage()); }
}

$services=[];$users=[];$nodes=[];$hosts=[];$inbounds=[];$adminInfo=[];$openapi=[];$ops=[];
if($selected){
    $sv=rb_list_services($selected); if(!empty($sv['ok']))$services=(array)$sv['items'];
    $us=rb_list_users($selected,25,0); if(!empty($us['ok']))$users=(array)$us['items'];
    $nd=rb_list_nodes($selected); if(!empty($nd['ok']))$nodes=(array)$nd['items'];
    $hs=rb_list_hosts($selected); if(!empty($hs['ok']))$hosts=(array)$hs['items'];
    $ib=rb_list_inbounds($selected,true); if(!empty($ib['ok']))$inbounds=(array)$ib['items'];
    $tc=rb_test_connection($selected); if(!empty($tc['ok']))$adminInfo=(array)($tc['admin']??[]);
    $openapi=itz_rb_spec_from_panel($selected); if($openapi)$ops=rb_openapi_operations($openapi);
}
$groups=[];foreach($ops as $op){$tag=(string)($op['tags'][0]??'Other');$groups[$tag]=($groups[$tag]??0)+1;}arsort($groups);
?>
<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><title>iTz Rebecca Hub | iTzAYDiN-BOT</title><link rel="stylesheet" href="css/theme.css"><link rel="stylesheet" href="css/admin-extra.css"><link rel="stylesheet" href="css/ay-controls.css"><script src="js/theme.js" defer></script><style>
.rb-hero{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:22px;border:1px solid var(--border-soft);border-radius:23px;background:linear-gradient(135deg,var(--surface-2),rgba(33,195,155,.08));margin-bottom:16px}.rb-kicker{font:800 11px 'JetBrains Mono',monospace;letter-spacing:2px;color:var(--accent)}.rb-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:13px}.rb-card{padding:16px;border:1px solid var(--border-soft);border-radius:18px;background:var(--surface-2)}.rb-card h3{margin:0 0 8px}.rb-stats{display:flex;gap:7px;flex-wrap:wrap}.rb-chip{padding:6px 9px;border-radius:999px;background:var(--surface-3);font-size:11px}.rb-ops{display:flex;gap:6px;flex-wrap:wrap;max-height:190px;overflow:auto}.rb-op{font:700 10px 'JetBrains Mono',monospace;padding:5px 7px;border:1px solid var(--border-soft);border-radius:9px}.rb-op b{color:var(--accent)}.rb-json{direction:ltr;text-align:left;white-space:pre-wrap;overflow:auto;max-height:420px;background:#090b12;color:#d6daf0;padding:13px;border-radius:13px;font:12px/1.7 monospace}.rb-list{display:grid;gap:7px;max-height:300px;overflow:auto}.rb-row{display:flex;justify-content:space-between;gap:10px;padding:9px;border:1px solid var(--border-soft);border-radius:11px;background:var(--surface-3)}@media(max-width:680px){.rb-hero{flex-direction:column}}
</style></head><body><section id="container"><?php include 'header.php';?><section id="main-content"><div class="wrapper">
<div class="rb-hero"><div><div class="rb-kicker">iTz • REBECCA API FABRIC</div><h1 style="margin:6px 0">Rebecca Hub</h1><p style="margin:0;color:var(--text-muted)">Service-driven provisioning، مدیریت کاربران و Nodeها، و OpenAPI Explorer زنده؛ قابلیت‌های همین نسخه Rebecca را مستقیم از spec خودش کشف می‌کند.</p></div><div class="rb-stats"><span class="rb-chip"><?=count($panels)?> Backend</span><span class="rb-chip"><?=count($ops)?> API Operation</span><a class="btn btn-primary btn-sm" href="panels.php">+ افزودن Rebecca</a></div></div>
<?php if($flash['ok']):?><div class="alert alert-success"><?=htmlspecialchars($flash['ok'],ENT_QUOTES,'UTF-8')?></div><?php endif;?><?php if($flash['err']):?><div class="alert alert-danger"><?=htmlspecialchars($flash['err'],ENT_QUOTES,'UTF-8')?></div><?php endif;?>
<div class="card"><form method="get"><div class="form-row"><div class="form-group"><label class="form-label">Backend Rebecca</label><select name="panel" class="form-control" onchange="this.form.submit()"><?php if(!$panels):?><option>هیچ Rebecca ثبت نشده</option><?php endif;?><?php foreach($panels as $p):?><option value="<?=htmlspecialchars($p['code_panel'],ENT_QUOTES)?>" <?=$selectedCode===(string)$p['code_panel']?'selected':''?>><?=htmlspecialchars($p['name_panel'].' — '.$p['url_panel'],ENT_QUOTES,'UTF-8')?></option><?php endforeach;?></select></div></div></form></div>
<?php if($selected):?>
<div class="rb-grid" style="margin-top:14px">
<div class="rb-card"><h3>🔐 اتصال</h3><div class="rb-stats"><span class="rb-chip">Auth: <?=htmlspecialchars(($selected['api_key']??'')!==''?'rk_ API Key':'Legacy JWT',ENT_QUOTES)?></span><span class="rb-chip">Last sync: <?=!empty($selected['rb_last_sync'])?htmlspecialchars(date('Y-m-d H:i',(int)$selected['rb_last_sync'])):'—'?></span></div><div style="margin-top:10px;color:var(--text-muted);font-size:12px">Endpoint: <code><?=htmlspecialchars($selected['url_panel']??'',ENT_QUOTES)?></code><br>Key: <code><?=htmlspecialchars(itz_rb_mask((string)($selected['api_key']??$selected['rb_access_token']??'')),ENT_QUOTES)?></code></div><form method="post" style="margin-top:12px"><input type="hidden" name="_csrf" value="<?=htmlspecialchars($csrf,ENT_QUOTES)?>"><input type="hidden" name="code_panel" value="<?=htmlspecialchars($selectedCode,ENT_QUOTES)?>"><button class="btn btn-primary btn-sm" name="_action" value="test">🧪 تست اتصال</button></form></div>
<div class="rb-card"><h3>🧩 Service Provisioning</h3><div class="rb-stats"><span class="rb-chip"><?=count($services)?> Service</span><span class="rb-chip">Default #<?=((int)($selected['rb_default_service_id']??0))?:'—'?></span></div><form method="post" style="margin-top:10px"><input type="hidden" name="_csrf" value="<?=htmlspecialchars($csrf,ENT_QUOTES)?>"><input type="hidden" name="code_panel" value="<?=htmlspecialchars($selectedCode,ENT_QUOTES)?>"><button class="btn btn-outline btn-sm" name="_action" value="sync_services">↻ Sync Serviceها</button></form><?php if($services):?><form method="post" style="margin-top:10px"><input type="hidden" name="_csrf" value="<?=htmlspecialchars($csrf,ENT_QUOTES)?>"><input type="hidden" name="code_panel" value="<?=htmlspecialchars($selectedCode,ENT_QUOTES)?>"><select class="form-control" name="service_id"><?php foreach($services as $s):$sid=itz_rb_service_id((array)$s);if($sid<=0)continue;?><option value="<?=$sid?>" <?=$sid===(int)($selected['rb_default_service_id']??0)?'selected':''?>>#<?=$sid?> · <?=htmlspecialchars(itz_rb_service_name((array)$s),ENT_QUOTES,'UTF-8')?></option><?php endforeach;?></select><button class="btn btn-primary btn-sm" style="margin-top:8px" name="_action" value="set_default_service">ذخیره Service پیش‌فرض</button></form><?php endif;?></div>
<div class="rb-card"><h3>📊 Inventory</h3><div class="rb-stats"><span class="rb-chip">👥 <?=count($users)?> user sample</span><span class="rb-chip">🛰 <?=count($nodes)?> node</span><span class="rb-chip">🌐 <?=count($hosts)?> host</span><span class="rb-chip">🧬 <?=count($inbounds)?> inbound</span></div><div style="margin-top:10px;color:var(--text-muted);font-size:12px">کاربرهای Rebecca به Service متصل می‌شوند؛ Service تعیین می‌کند چه Host/Inboundهایی در Subscription قرار بگیرند.</div></div>
<div class="rb-card"><h3>📚 Live OpenAPI</h3><div class="rb-stats"><span class="rb-chip"><?=count($ops)?> operation</span><span class="rb-chip"><?=count($groups)?> tag</span><span class="rb-chip">Sync: <?=!empty($selected['rb_openapi_last_sync'])?htmlspecialchars(date('Y-m-d H:i',(int)$selected['rb_openapi_last_sync'])):'live/cache'?></span></div><form method="post" style="margin-top:10px"><input type="hidden" name="_csrf" value="<?=htmlspecialchars($csrf,ENT_QUOTES)?>"><input type="hidden" name="code_panel" value="<?=htmlspecialchars($selectedCode,ENT_QUOTES)?>"><button class="btn btn-outline btn-sm" name="_action" value="sync_openapi">↻ Sync OpenAPI همین نسخه</button></form></div>
</div>
<div class="card" style="margin-top:14px"><div class="card__head"><div class="card__title">🧭 Capability Map</div><span class="chip">Dynamic</span></div><div class="rb-ops"><?php foreach($groups as $tag=>$count):?><span class="rb-op"><b><?=htmlspecialchars($tag,ENT_QUOTES,'UTF-8')?></b> × <?=$count?></span><?php endforeach;?></div><details style="margin-top:12px"><summary>نمایش همه عملیات OpenAPI</summary><div class="rb-ops" style="margin-top:10px;max-height:360px"><?php foreach($ops as $op):?><span class="rb-op"><b><?=htmlspecialchars($op['method'],ENT_QUOTES)?></b> <?=htmlspecialchars($op['path'],ENT_QUOTES)?><?=($op['summary']??'')!==''?' · '.htmlspecialchars($op['summary'],ENT_QUOTES,'UTF-8'):''?></span><?php endforeach;?></div></details></div>
<div class="card" style="margin-top:14px"><div class="card__head"><div><div class="card__title">🧪 Rebecca OpenAPI Console</div><div style="font-size:12px;color:var(--text-muted)">حتی endpointهای کم‌استفاده یا قابلیت‌های جدید آینده را تا وقتی در OpenAPI خود پنل وجود داشته باشند، از اینجا اجرا کن.</div></div></div><form method="post"><input type="hidden" name="_csrf" value="<?=htmlspecialchars($csrf,ENT_QUOTES)?>"><input type="hidden" name="code_panel" value="<?=htmlspecialchars($selectedCode,ENT_QUOTES)?>"><div class="form-row"><div class="form-group"><label class="form-label">Method</label><select class="form-control" name="api_method"><?php foreach(['GET','POST','PUT','PATCH','DELETE'] as $m):?><option><?=$m?></option><?php endforeach;?></select></div><div class="form-group"><label class="form-label">Path</label><input class="form-control" name="api_path" placeholder="/api/users?limit=10" dir="ltr" required></div></div><div class="form-group"><label class="form-label">JSON Payload برای POST/PUT/PATCH</label><textarea class="form-control" name="api_payload" rows="5" dir="ltr" placeholder='{"username":"example"}'></textarea></div><label style="display:flex;gap:8px;align-items:center;margin:8px 0"><input type="checkbox" name="confirm_mutation" value="1"> اجرای عملیات تغییردهنده را آگاهانه تأیید می‌کنم</label><label style="display:flex;gap:8px;align-items:center;margin:8px 0"><input type="checkbox" name="reveal_secret" value="1"> Secret احتمالی پاسخ را فقط همین یک‌بار نمایش بده (در حالت عادی Mask می‌شود)</label><button class="btn btn-primary" name="_action" value="api_call">اجرای عملیات معتبر OpenAPI</button></form><?php if($apiResult!==null):?><div style="margin-top:14px"><div class="rb-stats"><span class="rb-chip"><?=htmlspecialchars($apiOperation['method']??'',ENT_QUOTES)?> <?=htmlspecialchars($apiOperation['path']??'',ENT_QUOTES)?></span><span class="rb-chip">HTTP <?=intval($apiResult['http_code']??0)?></span><span class="rb-chip"><?=!empty($apiResult['ok'])?'OK':'ERROR'?></span></div><pre class="rb-json"><?=htmlspecialchars(is_array($apiResult['body'])?json_encode($apiResult['body'],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT):(string)$apiResult['body'],ENT_QUOTES,'UTF-8')?></pre></div><?php endif;?></div>
<div class="rb-grid" style="margin-top:14px"><div class="rb-card"><h3>👥 آخرین کاربران</h3><div class="rb-list"><?php foreach(array_slice($users,0,12) as $u):?><div class="rb-row"><span><?=htmlspecialchars((string)($u['username']??'—'),ENT_QUOTES,'UTF-8')?></span><span class="badge badge-info"><?=htmlspecialchars((string)($u['status']??'—'),ENT_QUOTES)?></span></div><?php endforeach;?><?php if(!$users):?><span style="color:var(--text-muted)">داده‌ای دریافت نشد.</span><?php endif;?></div></div><div class="rb-card"><h3>🛰 Nodeها</h3><div class="rb-list"><?php foreach(array_slice($nodes,0,12) as $n):?><div class="rb-row"><span><?=htmlspecialchars((string)($n['name']??$n['id']??'Node'),ENT_QUOTES,'UTF-8')?></span><span><?=htmlspecialchars((string)($n['status']??$n['connection_status']??''),ENT_QUOTES)?></span></div><?php endforeach;?><?php if(!$nodes):?><span style="color:var(--text-muted)">داده‌ای دریافت نشد.</span><?php endif;?></div></div></div>
<?php else:?><div class="card" style="padding:28px;text-align:center">هنوز Backend از نوع Rebecca ثبت نشده. <a href="panels.php">از Backend Registry اضافه کن.</a></div><?php endif;?>
</div></section></section></body></html>
