<?php
declare(strict_types=1);
require dirname(__DIR__) . '/src/bootstrap.php';

$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
$path = rawurldecode(parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/');
$path = '/' . ltrim(preg_replace('#^/index\.php#', '', $path), '/');

if ($method === 'OPTIONS') jsonResponse([], 204);
if ($path === '/health') jsonResponse(['status'=>'ok','version'=>'3.0.0-php','database'=>'mysql']);
if ($path === '/' || $path === '/admin') { header('Location: /admin/'); exit; }
if ($path === '/admin/' || $path === '/admin/index.html') {
    header('Content-Type: text/html; charset=utf-8');
    readfile(__DIR__ . '/admin/index.html'); exit;
}

// 微信登录
if ($method === 'POST' && $path === '/api/auth/login') {
    $code = trim((string)(body()['code'] ?? ''));
    if ($code === '') apiError('登录 code 不能为空');
    $appid=env('WECHAT_APPID','') ?: ''; $secret=env('WECHAT_SECRET','') ?: '';
    if ($appid==='' || $secret==='') apiError('服务器未配置微信小程序 AppID/Secret',500);
    $url='https://api.weixin.qq.com/sns/jscode2session?appid='.rawurlencode($appid).'&secret='.rawurlencode($secret).'&js_code='.rawurlencode($code).'&grant_type=authorization_code';
    $result=httpJson($url);
    if (empty($result['openid'])) apiError((string)($result['errmsg'] ?? '微信登录失败'));
    $existing=queryOne('SELECT * FROM users WHERE openid=?',[$result['openid']]); $isNew=!$existing;
    $user=$existing ?: ensureUser($result['openid']);
    jsonResponse(['success'=>true,'openid'=>$user['openid'],'nickname'=>$user['nickname'] ?? '','avatar_url'=>$user['avatar_url'] ?? '','is_new_user'=>$isNew]);
}

// 用户
if ($path === '/api/users/login' && $method === 'POST') jsonResponse(ensureUser(userId()));
if ($path === '/api/users/me' && $method === 'GET') {
    $user=queryOne('SELECT * FROM users WHERE openid=?',[userId()]); if(!$user) apiError('用户不存在',404); jsonResponse($user);
}

// 便签列表、搜索、回收站、创建
if ($path === '/api/notes' && $method === 'POST') {
    $uid=userId(); ensureUser($uid); $data=body(); $content=trim((string)($data['content'] ?? ''));
    if($content==='') apiError('便签内容不能为空');
    db()->beginTransaction();
    try {
        executeSql('INSERT INTO notes(user_id,content,title,is_voice) VALUES(?,?,?,?)',[$uid,$content,(string)($data['title'] ?? ''),!empty($data['is_voice'])?1:0]);
        $id=(int)db()->lastInsertId();
        foreach((array)($data['tag_ids'] ?? []) as $tagId) executeSql('INSERT IGNORE INTO note_tags(note_id,tag_id) SELECT ?,id FROM tags WHERE id=? AND user_id=?',[$id,(int)$tagId,$uid]);
        audit($uid,'create','note',$id); db()->commit();
    } catch(Throwable $e){ db()->rollBack(); throw $e; }
    jsonResponse(noteData(queryOne('SELECT * FROM notes WHERE id=?',[$id]) ?? [],$uid),201);
}
if (($path === '/api/notes' || $path === '/api/notes/search') && $method === 'GET') {
    $uid=userId(); $isSearch=$path==='/api/notes/search'; $limit=max(1,min(100,(int)($_GET[$isSearch?'page_size':'limit'] ?? ($isSearch?20:100))));
    $page=max(1,(int)($_GET['page'] ?? 1)); $skip=$isSearch?(($page-1)*$limit):max(0,(int)($_GET['skip'] ?? 0));
    $where='user_id=? AND is_deleted=0'; $params=[$uid];
    if($isSearch){ $kw=trim((string)($_GET['keyword'] ?? '')); if($kw==='') apiError('搜索关键词不能为空'); $where.=' AND (title LIKE ? OR content LIKE ?)'; $params[]="%$kw%"; $params[]="%$kw%"; }
    if(!$isSearch && isset($_GET['tag_id'])){ $where.=' AND id IN (SELECT note_id FROM note_tags WHERE tag_id=?)'; $params[]=(int)$_GET['tag_id']; }
    $total=(int)(queryOne("SELECT COUNT(*) c FROM notes WHERE $where",$params)['c'] ?? 0);
    $rows=queryAll("SELECT * FROM notes WHERE $where ORDER BY is_pinned DESC,created_at DESC LIMIT $limit OFFSET $skip",$params);
    jsonResponse(['notes'=>array_map(fn($n)=>noteData($n,$uid),$rows),'total'=>$total,'page'=>$isSearch?$page:intdiv($skip,$limit)+1,'page_size'=>$limit]);
}
if ($path === '/api/notes/trash' && $method === 'GET') {
    $uid=userId(); $rows=queryAll('SELECT * FROM notes WHERE user_id=? AND is_deleted=1 ORDER BY deleted_at DESC',[$uid]); jsonResponse(array_map(fn($n)=>noteData($n,$uid),$rows));
}
if (preg_match('#^/api/notes/(\d+)(?:/(pin|restore|permanent))?$#',$path,$m)) {
    $uid=userId(); $id=(int)$m[1]; $action=$m[2] ?? '';
    $note=queryOne('SELECT * FROM notes WHERE id=?',[$id]); if(!$note) apiError('便签不存在',404);
    $isOwner=$note['user_id']===$uid;
    if($method==='GET' && $action==='') {
        $canView=$isOwner || queryOne('SELECT id FROM note_shares WHERE note_id=? AND to_user_id=?',[$id,$uid]) || queryOne('SELECT gn.id FROM group_notes gn JOIN group_members gm ON gm.group_id=gn.group_id WHERE gn.note_id=? AND gm.user_id=?',[$id,$uid]);
        if(!$canView || (bool)$note['is_deleted']) apiError('便签不存在或无权查看',404);
        jsonResponse(noteData($note,$uid));
    }
    if(!$isOwner) apiError('便签不存在或无权操作',404);
    if($method==='PUT' && $action==='') {
        $d=body(); $fields=[]; $params=[];
        foreach(['content','title'] as $f) if(array_key_exists($f,$d)){ $fields[]="$f=?"; $params[]=(string)$d[$f]; }
        if(array_key_exists('is_pinned',$d)){ $fields[]='is_pinned=?'; $params[]=!empty($d['is_pinned'])?1:0; }
        db()->beginTransaction(); try {
            if($fields){ $params[]=$id; executeSql('UPDATE notes SET '.implode(',',$fields).' WHERE id=?',$params); }
            if(array_key_exists('tag_ids',$d)){ executeSql('DELETE FROM note_tags WHERE note_id=?',[$id]); foreach((array)$d['tag_ids'] as $tagId) executeSql('INSERT IGNORE INTO note_tags(note_id,tag_id) SELECT ?,id FROM tags WHERE id=? AND user_id=?',[$id,(int)$tagId,$uid]); }
            audit($uid,'update','note',$id); db()->commit();
        } catch(Throwable $e){ db()->rollBack(); throw $e; }
        jsonResponse(noteData(queryOne('SELECT * FROM notes WHERE id=?',[$id]) ?? [],$uid));
    }
    if($method==='POST' && $action==='pin'){ executeSql('UPDATE notes SET is_pinned=1-is_pinned WHERE id=?',[$id]); $v=queryOne('SELECT is_pinned FROM notes WHERE id=?',[$id]); jsonResponse(['success'=>true,'is_pinned'=>(bool)$v['is_pinned']]); }
    if($method==='DELETE' && $action===''){ executeSql('UPDATE notes SET is_deleted=1,deleted_at=NOW() WHERE id=?',[$id]); audit($uid,'delete','note',$id); jsonResponse(['success'=>true,'message'=>'已移入回收站']); }
    if($method==='POST' && $action==='restore'){ executeSql('UPDATE notes SET is_deleted=0,deleted_at=NULL WHERE id=? AND is_deleted=1',[$id]); jsonResponse(noteData(queryOne('SELECT * FROM notes WHERE id=?',[$id]) ?? [],$uid)); }
    if($method==='DELETE' && $action==='permanent'){ if(!(bool)$note['is_deleted']) apiError('便签不在回收站中',404); executeSql('DELETE FROM notes WHERE id=?',[$id]); jsonResponse(['success'=>true,'message'=>'永久删除成功']); }
}

// 标签
if ($path === '/api/tags') {
    $uid=userId();
    if($method==='GET') jsonResponse(queryAll('SELECT * FROM tags WHERE user_id=? ORDER BY created_at DESC',[$uid]));
    if($method==='POST'){ $d=body(); $name=trim((string)($d['name'] ?? '')); if($name==='') apiError('标签名不能为空'); try{ executeSql('INSERT INTO tags(user_id,name,color) VALUES(?,?,?)',[$uid,$name,(string)($d['color'] ?? '#1989fa')]); }catch(PDOException){ apiError('标签名已存在',409); } jsonResponse(queryOne('SELECT * FROM tags WHERE id=?',[(int)db()->lastInsertId()]),201); }
}
if (preg_match('#^/api/tags/(\d+)$#',$path,$m)) {
    $uid=userId(); $id=(int)$m[1]; if(!queryOne('SELECT id FROM tags WHERE id=? AND user_id=?',[$id,$uid])) apiError('标签不存在',404);
    if($method==='PUT'){ $d=body(); executeSql('UPDATE tags SET name=COALESCE(?,name),color=COALESCE(?,color) WHERE id=?',[$d['name'] ?? null,$d['color'] ?? null,$id]); jsonResponse(queryOne('SELECT * FROM tags WHERE id=?',[$id])); }
    if($method==='DELETE'){ executeSql('DELETE FROM tags WHERE id=?',[$id]); jsonResponse(['success'=>true,'message'=>'删除成功']); }
}

// 评论与点赞
if ($path === '/api/comments' && $method === 'POST') {
    $uid=userId(); $d=body(); $noteId=(int)($d['note_id'] ?? $_GET['note_id'] ?? 0); $content=trim((string)($d['content'] ?? ''));
    if(!queryOne('SELECT id FROM notes WHERE id=?',[$noteId])) apiError('便签不存在',404); if($content==='') apiError('评论不能为空'); ensureUser($uid);
    executeSql('INSERT INTO comments(note_id,user_id,content) VALUES(?,?,?)',[$noteId,$uid,$content]); $id=(int)db()->lastInsertId(); audit($uid,'comment','note',$noteId); jsonResponse(queryOne('SELECT * FROM comments WHERE id=?',[$id]),201);
}
if (preg_match('#^/api/comments/note/(\d+)$#',$path,$m) && $method==='GET') {
    userId(); $rows=queryAll('SELECT c.*,u.openid,u.nickname,u.avatar_url FROM comments c LEFT JOIN users u ON u.openid=c.user_id WHERE c.note_id=? ORDER BY c.created_at',[(int)$m[1]]);
    jsonResponse(array_map(function($r){ $r['id']=(int)$r['id']; $r['note_id']=(int)$r['note_id']; $r['user']=['openid'=>$r['openid'] ?? $r['user_id'],'nickname'=>$r['nickname'] ?? '','avatar_url'=>$r['avatar_url'] ?? '']; unset($r['openid'],$r['nickname'],$r['avatar_url']); return $r; },$rows));
}
if (preg_match('#^/api/comments/(\d+)$#',$path,$m)) {
    $uid=userId(); $id=(int)$m[1];
    if($method==='PUT'){ $content=trim((string)(body()['content'] ?? '')); $s=executeSql('UPDATE comments SET content=? WHERE id=? AND user_id=?',[$content,$id,$uid]); if(!$s->rowCount()) apiError('评论不存在或无权修改',404); jsonResponse(queryOne('SELECT * FROM comments WHERE id=?',[$id])); }
    if($method==='DELETE'){ $s=executeSql('DELETE FROM comments WHERE id=? AND user_id=?',[$id,$uid]); if(!$s->rowCount()) apiError('评论不存在或无权删除',404); jsonResponse(['success'=>true,'message'=>'删除成功']); }
}
if (preg_match('#^/api/likes/(\d+)(?:/status)?$#',$path,$m)) {
    $uid=userId(); $id=(int)$m[1];
    if($method==='GET') jsonResponse(['is_liked'=>(bool)queryOne('SELECT id FROM likes WHERE note_id=? AND user_id=?',[$id,$uid])]);
    if($method==='POST'){ if(!queryOne('SELECT id FROM notes WHERE id=?',[$id])) apiError('便签不存在',404); executeSql('INSERT IGNORE INTO likes(note_id,user_id) VALUES(?,?)',[$id,$uid]); jsonResponse(['success'=>true,'message'=>'点赞成功']); }
    if($method==='DELETE'){ executeSql('DELETE FROM likes WHERE note_id=? AND user_id=?',[$id,$uid]); jsonResponse(['success'=>true,'message'=>'取消点赞']); }
}

// 关注与分享
if ($path === '/api/social/following' && $method==='GET') {
    $uid=userId(); jsonResponse(queryAll('SELECT f.*,u.nickname,u.avatar_url FROM friendships f LEFT JOIN users u ON u.openid=f.to_user_id WHERE f.from_user_id=? ORDER BY f.created_at DESC',[$uid]));
}
if ($path === '/api/social/followers' && $method==='GET') { $uid=userId(); jsonResponse(queryAll('SELECT * FROM friendships WHERE to_user_id=? ORDER BY created_at DESC',[$uid])); }
if ($path === '/api/social/following/notes' && $method==='GET') {
    $uid=userId(); $rows=queryAll('SELECT n.* FROM notes n JOIN friendships f ON f.to_user_id=n.user_id WHERE f.from_user_id=? AND n.is_deleted=0 ORDER BY n.created_at DESC LIMIT 100',[$uid]); jsonResponse(array_map(fn($n)=>noteData($n,$uid),$rows));
}
if (preg_match('#^/api/social/(follow|check)/(.+)$#',$path,$m)) {
    $uid=userId(); $target=$m[2];
    if($m[1]==='check' && $method==='GET') jsonResponse(['is_following'=>(bool)queryOne('SELECT id FROM friendships WHERE from_user_id=? AND to_user_id=?',[$uid,$target])]);
    if($m[1]==='follow' && $method==='POST'){ if($uid===$target) apiError('不能关注自己'); ensureUser($target); executeSql('INSERT IGNORE INTO friendships(from_user_id,to_user_id) VALUES(?,?)',[$uid,$target]); jsonResponse(['success'=>true,'message'=>'关注成功']); }
    if($m[1]==='follow' && $method==='DELETE'){ executeSql('DELETE FROM friendships WHERE from_user_id=? AND to_user_id=?',[$uid,$target]); jsonResponse(['success'=>true,'message'=>'取消关注']); }
}
if ($path === '/api/shares/received' && $method==='GET') {
    $uid=userId(); $rows=queryAll('SELECT s.*,n.* ,s.created_at shared_at FROM note_shares s JOIN notes n ON n.id=s.note_id WHERE s.to_user_id=? ORDER BY s.created_at DESC',[$uid]);
    jsonResponse(array_map(function($r)use($uid){ $from=queryOne('SELECT openid,nickname,avatar_url FROM users WHERE openid=?',[$r['from_user_id']]); return ['note'=>noteData($r,$uid),'from_user'=>$from,'shared_at'=>$r['shared_at']]; },$rows));
}
if (preg_match('#^/api/shares/(\d+)/to/(.+)$#',$path,$m)) {
    $uid=userId(); $id=(int)$m[1]; $target=$m[2]; if(!queryOne('SELECT id FROM notes WHERE id=? AND user_id=?',[$id,$uid])) apiError('便签不存在或无权分享',404);
    if($method==='POST'){ ensureUser($target); executeSql('INSERT IGNORE INTO note_shares(note_id,from_user_id,to_user_id) VALUES(?,?,?)',[$id,$uid,$target]); jsonResponse(['success'=>true,'message'=>'分享成功']); }
    if($method==='DELETE'){ executeSql('DELETE FROM note_shares WHERE note_id=? AND from_user_id=? AND to_user_id=?',[$id,$uid,$target]); jsonResponse(['success'=>true,'message'=>'取消分享']); }
}

// 群组
if ($path === '/api/groups') {
    $uid=userId();
    if($method==='GET'){ $rows=queryAll('SELECT g.*,(SELECT COUNT(*) FROM group_members gm2 WHERE gm2.group_id=g.id) member_count FROM `groups` g JOIN group_members gm ON gm.group_id=g.id WHERE gm.user_id=? ORDER BY g.created_at DESC',[$uid]); jsonResponse(array_map(fn($g)=>['group'=>array_diff_key($g,['member_count'=>1]),'member_count'=>(int)$g['member_count']],$rows)); }
    if($method==='POST'){ $name=trim((string)(body()['name'] ?? '')); if($name==='') apiError('群组名不能为空'); ensureUser($uid); db()->beginTransaction(); try{ executeSql('INSERT INTO `groups`(name,owner_id) VALUES(?,?)',[$name,$uid]); $id=(int)db()->lastInsertId(); executeSql('INSERT INTO group_members(group_id,user_id) VALUES(?,?)',[$id,$uid]); db()->commit(); }catch(Throwable $e){db()->rollBack();throw $e;} $g=queryOne('SELECT * FROM `groups` WHERE id=?',[$id]); $g['member_count']=1; jsonResponse($g,201); }
}
if (preg_match('#^/api/groups/(\d+)(?:/(members|notes)(?:/(.+))?)?$#',$path,$m)) {
    $uid=userId(); $gid=(int)$m[1]; $section=$m[2] ?? ''; $target=$m[3] ?? null; $g=queryOne('SELECT * FROM `groups` WHERE id=?',[$gid]); if(!$g) apiError('群组不存在',404);
    if(!queryOne('SELECT id FROM group_members WHERE group_id=? AND user_id=?',[$gid,$uid])) apiError('不是群组成员',403);
    if($section==='' && $method==='GET'){ $g['member_count']=(int)(queryOne('SELECT COUNT(*) c FROM group_members WHERE group_id=?',[$gid])['c']??0); jsonResponse($g); }
    if($section==='' && $method==='PUT'){ if($g['owner_id']!==$uid) apiError('无权修改',403); $name=trim((string)(body()['name']??'')); executeSql('UPDATE `groups` SET name=? WHERE id=?',[$name,$gid]); jsonResponse(queryOne('SELECT * FROM `groups` WHERE id=?',[$gid])); }
    if($section==='' && $method==='DELETE'){ if($g['owner_id']!==$uid) apiError('无权删除',403); executeSql('DELETE FROM `groups` WHERE id=?',[$gid]); jsonResponse(['success'=>true,'message'=>'删除成功']); }
    if($section==='members' && $target===null && $method==='GET'){ $rows=queryAll('SELECT gm.user_id,gm.joined_at,u.openid,u.nickname,u.avatar_url FROM group_members gm LEFT JOIN users u ON u.openid=gm.user_id WHERE gm.group_id=?',[$gid]); jsonResponse(array_map(fn($r)=>['member'=>['user_id'=>$r['user_id'],'joined_at'=>$r['joined_at']],'user'=>['openid'=>$r['openid']??$r['user_id'],'nickname'=>$r['nickname']??'','avatar_url'=>$r['avatar_url']??'']],$rows)); }
    if($section==='members' && $target!==null && $method==='POST'){ ensureUser($target); executeSql('INSERT IGNORE INTO group_members(group_id,user_id) VALUES(?,?)',[$gid,$target]); jsonResponse(['success'=>true,'message'=>'添加成功']); }
    if($section==='members' && $target!==null && $method==='DELETE'){ if($g['owner_id']!==$uid || $target===$uid) apiError('无权移除',403); executeSql('DELETE FROM group_members WHERE group_id=? AND user_id=?',[$gid,$target]); jsonResponse(['success'=>true,'message'=>'移除成功']); }
    if($section==='notes' && $target===null && $method==='GET'){ $rows=queryAll('SELECT n.*,gn.shared_by,gn.created_at shared_at FROM group_notes gn JOIN notes n ON n.id=gn.note_id WHERE gn.group_id=? ORDER BY gn.created_at DESC',[$gid]); jsonResponse(array_map(fn($r)=>['note'=>noteData($r,$uid),'shared_by'=>$r['shared_by'],'created_at'=>$r['shared_at']],$rows)); }
    if($section==='notes' && $target!==null && $method==='POST'){ $nid=(int)$target; if(!queryOne('SELECT id FROM notes WHERE id=? AND user_id=?',[$nid,$uid])) apiError('便签不存在或无权分享',404); executeSql('INSERT IGNORE INTO group_notes(group_id,note_id,shared_by) VALUES(?,?,?)',[$gid,$nid,$uid]); jsonResponse(['success'=>true,'message'=>'添加成功']); }
    if($section==='notes' && $target!==null && $method==='DELETE'){ $nid=(int)$target; executeSql('DELETE FROM group_notes WHERE group_id=? AND note_id=? AND (shared_by=? OR ?=?)',[$gid,$nid,$uid,$g['owner_id'],$uid]); jsonResponse(['success'=>true,'message'=>'移除成功']); }
}

// 提醒
if ($path === '/api/reminders' && $method==='POST') { $uid=userId(); $d=body(); $nid=(int)($d['note_id']??0); if(!queryOne('SELECT id FROM notes WHERE id=? AND user_id=?',[$nid,$uid])) apiError('便签不存在',404); executeSql('INSERT INTO reminders(note_id,user_id,remind_at) VALUES(?,?,?)',[$nid,$uid,(string)($d['remind_at']??'')]); jsonResponse(queryOne('SELECT * FROM reminders WHERE id=?',[(int)db()->lastInsertId()]),201); }
if ($path === '/api/reminders/pending' && $method==='GET') { $uid=userId(); jsonResponse(queryAll('SELECT * FROM reminders WHERE user_id=? AND is_triggered=0 AND remind_at>=NOW() ORDER BY remind_at',[$uid])); }
if (preg_match('#^/api/reminders/note/(\d+)$#',$path,$m) && $method==='GET') { $uid=userId(); jsonResponse(queryAll('SELECT * FROM reminders WHERE note_id=? AND user_id=? ORDER BY remind_at',[(int)$m[1],$uid])); }
if (preg_match('#^/api/reminders/(\d+)$#',$path,$m) && $method==='DELETE') { $uid=userId(); executeSql('DELETE FROM reminders WHERE id=? AND user_id=?',[(int)$m[1],$uid]); jsonResponse(['success'=>true,'message'=>'删除成功']); }

// 腾讯云一句话识别
if ($path === '/api/asr/recognize' && $method==='POST') {
    if(empty($_FILES['file']['tmp_name'])) apiError('请上传音频文件'); $audio=file_get_contents($_FILES['file']['tmp_name']); if($audio===false) apiError('读取音频失败');
    $ext=strtolower(pathinfo((string)($_FILES['file']['name']??'audio.mp3'),PATHINFO_EXTENSION)); if($ext==='') $ext='mp3'; $result=tencentAsr($audio,$ext);
    if(isset($result['Response']['Error'])) apiError((string)$result['Response']['Error']['Message'],502); jsonResponse(['success'=>true,'text'=>(string)($result['Response']['Result']??'')]);
}

// 管理员认证
if ($path === '/api/admin/auth/login' && $method==='POST') {
    createDefaultAdmin(); $d=body(); $admin=queryOne('SELECT * FROM admins WHERE username=?',[trim((string)($d['username']??''))]);
    if(!$admin || !password_verify((string)($d['password']??''),$admin['password_hash'])) apiError('用户名或密码错误',401);
    $token=bin2hex(random_bytes(32)); executeSql('DELETE FROM admin_sessions WHERE expires_at<=NOW()'); executeSql('INSERT INTO admin_sessions(admin_id,token_hash,expires_at) VALUES(?,?,DATE_ADD(NOW(),INTERVAL 7 DAY))',[$admin['id'],hash('sha256',$token)]); executeSql('UPDATE admins SET last_login=NOW(),login_count=login_count+1 WHERE id=?',[$admin['id']]);
    jsonResponse(['success'=>true,'token'=>$token,'username'=>$admin['username'],'nickname'=>$admin['nickname'],'is_super'=>(bool)$admin['is_super']]);
}
if ($path === '/api/admin/auth/logout' && $method==='POST') { requireAdmin(); executeSql('DELETE FROM admin_sessions WHERE token_hash=?',[hash('sha256',bearerToken())]); jsonResponse(['success'=>true]); }
if ($path === '/api/admin/auth/profile' && $method==='POST') { $a=requireAdmin(); $nickname=trim((string)(body()['nickname']??'')); if($nickname==='') apiError('昵称不能为空'); executeSql('UPDATE admins SET nickname=? WHERE id=?',[$nickname,$a['id']]); jsonResponse(['success'=>true,'nickname'=>$nickname]); }
if (preg_match('#^/api/admin/auth/change-password(?:/[^/]+)?$#',$path) && $method==='POST') { $a=requireAdmin(); $d=body(); $old=(string)($d['old_password']??($_SERVER['HTTP_OLD_PASSWORD']??'')); $new=(string)($d['new_password']??($_SERVER['HTTP_NEW_PASSWORD']??'')); if(!password_verify($old,$a['password_hash'])) apiError('原密码错误',400); if(strlen($new)<6) apiError('新密码至少6位'); executeSql('UPDATE admins SET password_hash=? WHERE id=?',[password_hash($new,PASSWORD_DEFAULT),$a['id']]); executeSql('DELETE FROM admin_sessions WHERE admin_id=? AND token_hash<>?',[$a['id'],hash('sha256',bearerToken())]); jsonResponse(['success'=>true]); }

if (str_starts_with($path,'/api/admin/')) {
    requireAdmin();
    if($path==='/api/admin/stats' && $method==='GET') jsonResponse(['total_users'=>(int)(queryOne('SELECT COUNT(*) c FROM users')['c']??0),'total_notes'=>(int)(queryOne('SELECT COUNT(*) c FROM notes WHERE is_deleted=0')['c']??0),'total_tags'=>(int)(queryOne('SELECT COUNT(*) c FROM tags')['c']??0),'total_comments'=>(int)(queryOne('SELECT COUNT(*) c FROM comments')['c']??0),'notes_today'=>(int)(queryOne('SELECT COUNT(*) c FROM notes WHERE is_deleted=0 AND DATE(created_at)=CURDATE()')['c']??0)]);
    if($path==='/api/admin/users' && $method==='GET'){ $kw=trim((string)($_GET['search']??'')); $params=[]; $where=''; if($kw!==''){$where='WHERE openid LIKE ? OR nickname LIKE ?';$params=["%$kw%","%$kw%"];} jsonResponse(queryAll("SELECT u.*,(SELECT COUNT(*) FROM notes n WHERE n.user_id=u.openid AND n.is_deleted=0) note_count FROM users u $where ORDER BY u.created_at DESC LIMIT 100",$params)); }
    if(preg_match('#^/api/admin/users/(.+)$#',$path,$m) && $method==='DELETE'){ $oid=$m[1]; db()->beginTransaction(); try{executeSql('DELETE FROM note_shares WHERE from_user_id=? OR to_user_id=?',[$oid,$oid]);executeSql('DELETE FROM friendships WHERE from_user_id=? OR to_user_id=?',[$oid,$oid]);executeSql('DELETE FROM group_members WHERE user_id=?',[$oid]);executeSql('DELETE FROM reminders WHERE user_id=?',[$oid]);executeSql('DELETE FROM comments WHERE user_id=?',[$oid]);executeSql('DELETE FROM likes WHERE user_id=?',[$oid]);executeSql('DELETE FROM tags WHERE user_id=?',[$oid]);executeSql('DELETE FROM notes WHERE user_id=?',[$oid]);executeSql('DELETE FROM users WHERE openid=?',[$oid]);db()->commit();}catch(Throwable $e){db()->rollBack();throw $e;} jsonResponse(['success'=>true]);}
    if($path==='/api/admin/notes' && $method==='GET'){ $kw=trim((string)($_GET['search']??'')); $params=[];$where='is_deleted=0';if($kw!==''){$where.=' AND (title LIKE ? OR content LIKE ?)';$params=["%$kw%","%$kw%"];} $total=(int)(queryOne("SELECT COUNT(*) c FROM notes WHERE $where",$params)['c']??0);$rows=queryAll("SELECT * FROM notes WHERE $where ORDER BY created_at DESC LIMIT 100",$params);jsonResponse(['notes'=>array_map(fn($n)=>noteData($n),$rows),'total'=>$total]);}
    if(preg_match('#^/api/admin/notes/(\d+)$#',$path,$m) && $method==='DELETE'){executeSql('DELETE FROM notes WHERE id=?',[(int)$m[1]]);jsonResponse(['success'=>true]);}
    if($path==='/api/admin/tags' && $method==='GET') jsonResponse(queryAll('SELECT t.*,(SELECT COUNT(*) FROM note_tags nt WHERE nt.tag_id=t.id) use_count FROM tags t ORDER BY created_at DESC'));
    if($path==='/api/admin/tags' && $method==='POST'){ $d=body();executeSql('INSERT INTO tags(user_id,name,color) VALUES(?,?,?)',['admin',trim((string)($d['name']??'')),(string)($d['color']??'#1989fa')]);jsonResponse(queryOne('SELECT * FROM tags WHERE id=?',[(int)db()->lastInsertId()]),201);}
    if(preg_match('#^/api/admin/tags/(\d+)$#',$path,$m) && $method==='DELETE'){executeSql('DELETE FROM tags WHERE id=?',[(int)$m[1]]);jsonResponse(['success'=>true]);}
    if($path==='/api/admin/comments' && $method==='GET') jsonResponse(queryAll('SELECT * FROM comments ORDER BY created_at DESC LIMIT 100'));
    if(preg_match('#^/api/admin/comments/(\d+)$#',$path,$m) && $method==='DELETE'){executeSql('DELETE FROM comments WHERE id=?',[(int)$m[1]]);jsonResponse(['success'=>true]);}
    if($path==='/api/admin/groups' && $method==='GET'){ $rows=queryAll('SELECT g.*,(SELECT COUNT(*) FROM group_members gm WHERE gm.group_id=g.id) member_count FROM `groups` g ORDER BY created_at DESC'); jsonResponse(array_map(fn($g)=>['group'=>array_diff_key($g,['member_count'=>1]),'member_count'=>(int)$g['member_count']],$rows)); }
    if($path==='/api/admin/audit' && $method==='GET'){ $rows=queryAll('SELECT id,user_id,action,target_type resource_type,target_id resource_id,detail details,created_at FROM audit_logs ORDER BY created_at DESC LIMIT 100'); jsonResponse($rows); }
}

apiError('接口不存在',404);
