<?php

require_once dirname(__DIR__, 2) . '/core/lib/audit_log.php';

$page = max(1, intval($_GET['page'] ?? 1));
$pageSize = 20;
$keyword = trim((string) ($_GET['keyword'] ?? ''));
$actionType = trim((string) ($_GET['action_type'] ?? ''));
$module = trim((string) ($_GET['module'] ?? ''));
$category = trim((string) ($_GET['category'] ?? ''));
$risk = trim((string) ($_GET['risk'] ?? ''));
$dateFrom = trim((string) ($_GET['date_from'] ?? ''));
$dateTo = trim((string) ($_GET['date_to'] ?? ''));
$category = in_array($category, ['security','system','workflow','configuration','content'], true) ? $category : '';
$risk = in_array($risk, ['low','medium','high'], true) ? $risk : '';

$where = '1=1'; $params = [];
if ($keyword !== '') { $where .= ' AND (l.action LIKE ? OR l.module LIKE ? OR l.content LIKE ? OR l.username LIKE ? OR l.ip LIKE ?)'; for ($i=0;$i<5;$i++) { $params[]='%'.$keyword.'%'; } }
if ($actionType !== '') { $where .= ' AND l.action=?'; $params[]=$actionType; }
if ($module !== '') { $where .= ' AND l.module=?'; $params[]=$module; }
if ($dateFrom !== '' && strtotime($dateFrom) !== false) { $where .= ' AND l.create_time>=?'; $params[]=strtotime($dateFrom); }
if ($dateTo !== '' && strtotime($dateTo) !== false) { $where .= ' AND l.create_time<=?'; $params[]=strtotime($dateTo.' 23:59:59'); }

$logTable = $db->table('log');
$baseSql = "SELECT l.* FROM {$logTable} l WHERE {$where} ORDER BY l.id DESC";
$needsDerivedFilter = $category !== '' || $risk !== '';
if ($needsDerivedFilter) {
    $candidateLimit = 10000;
    $candidateRows = $db->fetchAll($baseSql . ' LIMIT ' . $candidateLimit, $params);
    $filteredRows = cmsAuditFilterRows($candidateRows, $category, $risk);
    $total = count($filteredRows);
    $logs = array_slice($filteredRows, ($page-1)*$pageSize, $pageSize);
} else {
    $total = intval($db->fetch("SELECT COUNT(*) cnt FROM {$logTable} l WHERE {$where}", $params)['cnt'] ?? 0);
    $logs = $db->fetchAll($baseSql . ' LIMIT '.(($page-1)*$pageSize).','.$pageSize, $params);
}
$totalPages = max(1, intval(ceil($total/$pageSize)));
$derivedFilterTruncated = $needsDerivedFilter && count($candidateRows) >= $candidateLimit;
$actionTypes = $db->fetchAll("SELECT DISTINCT action FROM {$logTable} WHERE action<>'' ORDER BY action");
$modules = $db->fetchAll("SELECT DISTINCT module FROM {$logTable} WHERE module<>'' ORDER BY module");

if (($_GET['export'] ?? '') === 'csv') {
    $rows = cmsAuditFilterRows($db->fetchAll($baseSql . ' LIMIT 5000', $params), $category, $risk);
    while (ob_get_level() > 0) { ob_end_clean(); }
    header('Content-Type: text/csv; charset=UTF-8');
    header('Content-Disposition: attachment; filename="audit-log-'.date('Ymd-His').'.csv"');
    echo "\xEF\xBB\xBF";
    $out=fopen('php://output','w'); fputcsv($out,['ID','风险','分类','操作者','动作','模块','内容','IP','设备','时间']);
    foreach ($rows as $row) { $meta=cmsAuditClassify($row['action'],$row['module'],$row['content']); fputcsv($out,array_map('cmsAuditCsvCell',[$row['id'],cmsAuditRiskLabel($meta['risk']),cmsAuditCategoryLabel($meta['category']),$row['username'],$row['action'],$row['module'],$row['content'],$row['ip'],$row['user_agent'],date('Y-m-d H:i:s',$row['create_time'])])); }
    fclose($out); exit;
}

$todayStart=strtotime(date('Y-m-d'));
$todayCount=intval($db->fetch("SELECT COUNT(*) cnt FROM {$logTable} WHERE create_time>=?",[$todayStart])['cnt'] ?? 0);
$recentRows=$db->fetchAll("SELECT action,module,content FROM {$logTable} ORDER BY id DESC LIMIT 1000");
$riskCounts=['high'=>0,'medium'=>0,'low'=>0]; foreach($recentRows as $row){$riskCounts[cmsAuditClassify($row['action'],$row['module'],$row['content'])['risk']]++;}
$query=$_GET; unset($query['page'],$query['export']); $queryString=http_build_query($query);
?>
<div class="page-container fade-in audit-page">
<div class="page-header"><div class="page-title"><div class="page-icon"><i class="fa fa-shield"></i></div><div><h1>审计日志</h1><p>追踪后台操作、安全事件与关键配置变更</p></div></div><div class="page-actions"><a class="btn btn-secondary" href="?<?php echo htmlspecialchars($queryString.'&export=csv'); ?>"><i class="fa fa-download"></i> 导出当前结果</a></div></div>
<?php if ($derivedFilterTruncated): ?><div class="audit-limit-note">风险与分类筛选当前基于最近 10,000 条日志，请同时缩小日期范围以获得精确结果。</div><?php endif; ?>
<div class="audit-summary"><div><small>筛选结果</small><strong><?php echo $total; ?></strong><span>条记录</span></div><div><small>今日事件</small><strong><?php echo $todayCount; ?></strong><span>条记录</span></div><div class="is-danger"><small>最近高风险</small><strong><?php echo $riskCounts['high']; ?></strong><span>最近 1000 条</span></div><div class="is-warning"><small>最近需关注</small><strong><?php echo $riskCounts['medium']; ?></strong><span>最近 1000 条</span></div></div>
<form class="audit-filter" method="get"><input type="hidden" name="act" value="log"><input name="keyword" value="<?php echo htmlspecialchars($keyword); ?>" placeholder="动作、内容、用户、IP"><select name="risk"><option value="">全部风险</option><?php foreach(['high'=>'高风险','medium'=>'需关注','low'=>'常规'] as $k=>$v): ?><option value="<?php echo $k; ?>" <?php echo $risk===$k?'selected':''; ?>><?php echo $v; ?></option><?php endforeach; ?></select><select name="category"><option value="">全部分类</option><?php foreach(['security','system','workflow','configuration','content'] as $v): ?><option value="<?php echo $v; ?>" <?php echo $category===$v?'selected':''; ?>><?php echo cmsAuditCategoryLabel($v); ?></option><?php endforeach; ?></select><select name="module"><option value="">全部模块</option><?php foreach($modules as $v): ?><option <?php echo $module===$v['module']?'selected':''; ?>><?php echo htmlspecialchars($v['module']); ?></option><?php endforeach; ?></select><select name="action_type"><option value="">全部动作</option><?php foreach($actionTypes as $v): ?><option <?php echo $actionType===$v['action']?'selected':''; ?>><?php echo htmlspecialchars($v['action']); ?></option><?php endforeach; ?></select><input type="date" name="date_from" value="<?php echo htmlspecialchars($dateFrom); ?>"><input type="date" name="date_to" value="<?php echo htmlspecialchars($dateTo); ?>"><button class="btn btn-primary">筛选</button><a class="btn btn-secondary" href="?act=log">重置</a></form>
<div class="content-card audit-table-wrap"><table class="data-table audit-table"><thead><tr><th>风险</th><th>事件</th><th>操作者</th><th>IP</th><th>时间</th><th>详情</th></tr></thead><tbody><?php if(!$logs): ?><tr><td colspan="6" class="audit-empty">没有符合条件的日志</td></tr><?php endif; ?><?php foreach($logs as $item): $meta=cmsAuditClassify($item['action'],$item['module'],$item['content']); ?><tr><td><span class="audit-risk is-<?php echo $meta['risk']; ?>"><?php echo cmsAuditRiskLabel($meta['risk']); ?></span></td><td><strong><?php echo htmlspecialchars($item['action']); ?></strong><small><?php echo cmsAuditCategoryLabel($meta['category']); ?> · <?php echo htmlspecialchars($item['module'] ?: '未分类'); ?></small><p><?php echo htmlspecialchars($item['content']); ?></p></td><td><?php echo htmlspecialchars($item['username'] ?: '系统任务'); ?></td><td class="audit-mono"><?php echo htmlspecialchars($item['ip'] ?: '-'); ?></td><td><?php echo date('Y-m-d H:i:s',$item['create_time']); ?></td><td><button type="button" class="btn btn-secondary audit-detail" data-log='<?php echo htmlspecialchars(json_encode(['id'=>$item['id'],'risk'=>cmsAuditRiskLabel($meta['risk']),'category'=>cmsAuditCategoryLabel($meta['category']),'username'=>$item['username'] ?: '系统任务','action'=>$item['action'],'module'=>$item['module'],'content'=>$item['content'],'ip'=>$item['ip'] ?: '-','user_agent'=>$item['user_agent'] ?: '-','time'=>date('Y-m-d H:i:s',$item['create_time'])],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),ENT_QUOTES,'UTF-8'); ?>'>查看</button></td></tr><?php endforeach; ?></tbody></table><?php if($totalPages>1): ?><div class="pagination"><?php for($i=max(1,$page-2);$i<=min($totalPages,$page+2);$i++): ?><a class="<?php echo $i===$page?'active':''; ?>" href="?<?php echo htmlspecialchars($queryString.'&page='.$i); ?>"><?php echo $i; ?></a><?php endfor; ?></div><?php endif; ?></div>
</div>
<div class="audit-modal" id="auditModal" hidden><div class="audit-dialog"><div class="audit-dialog-head"><div><small>AUDIT EVENT</small><h2>日志详情</h2></div><button type="button" id="auditClose">×</button></div><dl id="auditDetail"></dl></div></div>
<style>
.audit-limit-note{margin-bottom:14px;padding:10px 14px;border:1px solid #fde68a;border-radius:10px;background:#fffbeb;color:#92400e;font-size:12px}
.audit-page{max-width:1500px}.audit-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}.audit-summary>div{padding:16px 18px;border:1px solid #e2e8f0;border-radius:14px;background:#fff}.audit-summary small,.audit-summary span{display:block;color:#94a3b8;font-size:11px}.audit-summary strong{display:inline-block;margin:5px 7px 2px 0;color:#1e293b;font-size:24px}.audit-summary .is-danger strong{color:#dc2626}.audit-summary .is-warning strong{color:#d97706}.audit-filter{display:flex;gap:10px;flex-wrap:wrap;padding:14px;margin-bottom:16px;border:1px solid #e2e8f0;border-radius:14px;background:#fff}.audit-filter input,.audit-filter select{min-width:125px;height:38px;padding:0 11px;border:1px solid #dbe3ee;border-radius:9px;background:#fff}.audit-filter input[name=keyword]{flex:1;min-width:210px}.audit-table-wrap{overflow-x:auto}.audit-table td{vertical-align:middle}.audit-table td:nth-child(2){min-width:300px}.audit-table td:nth-child(5){white-space:nowrap;color:#64748b;font-size:12px}.audit-table strong,.audit-table small,.audit-table p{display:block}.audit-table small{margin-top:3px;color:#94a3b8;font-size:10px}.audit-table p{max-width:520px;margin:5px 0 0;color:#64748b;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.audit-risk{display:inline-flex;padding:5px 9px;border-radius:999px;font-size:11px;font-weight:800}.audit-risk.is-low{color:#047857;background:#ecfdf5}.audit-risk.is-medium{color:#b45309;background:#fffbeb}.audit-risk.is-high{color:#b91c1c;background:#fef2f2}.audit-mono{font-family:Consolas,monospace;font-size:11px}.audit-empty{text-align:center!important;padding:54px!important;color:#94a3b8}.audit-modal{position:fixed;inset:0;z-index:9999;padding:24px;background:rgba(15,23,42,.42);backdrop-filter:blur(3px)}.audit-modal:not([hidden]){display:flex;align-items:center;justify-content:center}.audit-dialog{width:min(620px,100%);max-height:85vh;overflow:auto;padding:22px;border-radius:18px;background:#fff;box-shadow:0 30px 70px rgba(15,23,42,.25)}.audit-dialog-head{display:flex;justify-content:space-between;align-items:flex-start;border-bottom:1px solid #edf2f7;padding-bottom:14px}.audit-dialog-head small{color:#94a3b8;letter-spacing:.12em}.audit-dialog-head h2{margin:2px 0 0}.audit-dialog-head button{border:0;background:transparent;color:#64748b;font-size:28px;cursor:pointer}.audit-dialog dl{display:grid;grid-template-columns:100px 1fr;gap:0;margin:14px 0 0}.audit-dialog dt,.audit-dialog dd{margin:0;padding:11px 4px;border-bottom:1px solid #f1f5f9}.audit-dialog dt{color:#94a3b8;font-size:12px}.audit-dialog dd{color:#334155;word-break:break-word}@media(max-width:900px){.audit-summary{grid-template-columns:repeat(2,1fr)}}@media(max-width:600px){.audit-summary{grid-template-columns:1fr}.audit-dialog dl{grid-template-columns:1fr}.audit-dialog dt{padding-bottom:0;border-bottom:0}}
</style>
<script>(function(){var modal=document.getElementById('auditModal'),detail=document.getElementById('auditDetail'),labels={id:'日志 ID',risk:'风险等级',category:'事件分类',username:'操作者',action:'动作',module:'模块',content:'操作内容',ip:'IP 地址',user_agent:'设备信息',time:'发生时间'};document.querySelectorAll('.audit-detail').forEach(function(btn){btn.addEventListener('click',function(){var data=JSON.parse(this.getAttribute('data-log'));detail.innerHTML='';Object.keys(labels).forEach(function(key){var dt=document.createElement('dt'),dd=document.createElement('dd');dt.textContent=labels[key];dd.textContent=data[key]||'-';detail.appendChild(dt);detail.appendChild(dd);});modal.hidden=false;});});function close(){modal.hidden=true;}document.getElementById('auditClose').onclick=close;modal.addEventListener('click',function(e){if(e.target===modal)close();});document.addEventListener('keydown',function(e){if(e.key==='Escape')close();});})();</script>
