import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class BaishunDebugLogEntry { const BaishunDebugLogEntry({ required this.timestamp, required this.tag, required this.message, }); final DateTime timestamp; final String tag; final String message; String get formattedTime { final hh = timestamp.hour.toString().padLeft(2, '0'); final mm = timestamp.minute.toString().padLeft(2, '0'); final ss = timestamp.second.toString().padLeft(2, '0'); return '$hh:$mm:$ss'; } } class BaishunDebugSnapshot { const BaishunDebugSnapshot({ required this.roomId, required this.gameName, required this.gameId, required this.gameSessionId, required this.entryUrl, required this.launchMode, required this.loading, required this.pageFinished, required this.receivedBridgeMessage, required this.injectCount, required this.configSendCount, required this.lastBridgeSource, required this.lastBridgeAction, required this.lastBridgePayload, required this.lastJsCallback, required this.lastConfigSummary, required this.errorMessage, required this.logs, }); final String roomId; final String gameName; final String gameId; final String gameSessionId; final String entryUrl; final String launchMode; final bool loading; final bool pageFinished; final bool receivedBridgeMessage; final int injectCount; final int configSendCount; final String lastBridgeSource; final String lastBridgeAction; final String lastBridgePayload; final String lastJsCallback; final String lastConfigSummary; final String errorMessage; final List logs; } class BaishunDebugPanel extends StatefulWidget { const BaishunDebugPanel({ super.key, required this.snapshot, required this.onReload, required this.onInjectBridge, required this.onReplayConfig, required this.onWalletUpdate, required this.onClearLogs, }); final BaishunDebugSnapshot snapshot; final VoidCallback onReload; final VoidCallback onInjectBridge; final VoidCallback onReplayConfig; final VoidCallback onWalletUpdate; final VoidCallback onClearLogs; @override State createState() => _BaishunDebugPanelState(); } class _BaishunDebugPanelState extends State { bool _expanded = false; @override Widget build(BuildContext context) { final maxWidth = (ScreenUtil().screenWidth - 24.w).clamp(220.w, 360.w); return AnimatedSwitcher( duration: const Duration(milliseconds: 180), child: _expanded ? Container( key: const ValueKey('baishun_debug_panel_expanded'), width: maxWidth, padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: const Color(0xE616302B), borderRadius: BorderRadius.circular(16.w), border: Border.all( color: Colors.white.withValues(alpha: 0.14), width: 1.w, ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.22), blurRadius: 18.w, offset: Offset(0, 8.w), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( 'BAISHUN DEBUG', style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w800, letterSpacing: 0.4, ), ), const Spacer(), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { setState(() { _expanded = false; }); }, child: Padding( padding: EdgeInsets.all(2.w), child: Icon( Icons.close, size: 16.w, color: Colors.white70, ), ), ), ], ), SizedBox(height: 8.w), _InfoLine(label: 'Room', value: widget.snapshot.roomId), _InfoLine( label: 'Game', value: '${widget.snapshot.gameName} (${widget.snapshot.gameId})', ), _InfoLine( label: 'Session', value: widget.snapshot.gameSessionId, ), _InfoLine( label: 'Launch', value: widget.snapshot.launchMode, ), _InfoLine( label: 'Loading', value: widget.snapshot.loading ? 'YES' : 'NO', ), _InfoLine( label: 'PageFinished', value: widget.snapshot.pageFinished ? 'YES' : 'NO', ), _InfoLine( label: 'BridgeMsg', value: widget.snapshot.receivedBridgeMessage ? 'YES' : 'NO', ), _InfoLine( label: 'InjectCount', value: '${widget.snapshot.injectCount}', ), _InfoLine( label: 'ConfigSend', value: '${widget.snapshot.configSendCount}', ), _InfoLine( label: 'LastSource', value: widget.snapshot.lastBridgeSource.isEmpty ? '--' : widget.snapshot.lastBridgeSource, ), _InfoLine( label: 'LastAction', value: widget.snapshot.lastBridgeAction.isEmpty ? '--' : widget.snapshot.lastBridgeAction, ), _InfoLine( label: 'LastCallback', value: widget.snapshot.lastJsCallback.isEmpty ? '--' : widget.snapshot.lastJsCallback, ), if (widget.snapshot.lastConfigSummary.isNotEmpty) _MultilineLine( label: 'Config', value: widget.snapshot.lastConfigSummary, ), _MultilineLine( label: 'EntryUrl', value: widget.snapshot.entryUrl, ), if (widget.snapshot.lastBridgePayload.isNotEmpty) _MultilineLine( label: 'Payload', value: widget.snapshot.lastBridgePayload, ), if (widget.snapshot.errorMessage.isNotEmpty) _MultilineLine( label: 'Error', value: widget.snapshot.errorMessage, valueColor: const Color(0xFFFFB4B4), ), SizedBox(height: 10.w), Wrap( spacing: 8.w, runSpacing: 8.w, children: [ _DebugActionChip( label: 'Reload', onTap: widget.onReload, ), _DebugActionChip( label: 'Inject', onTap: widget.onInjectBridge, ), _DebugActionChip( label: 'ReplayConfig', onTap: widget.onReplayConfig, ), _DebugActionChip( label: 'Wallet', onTap: widget.onWalletUpdate, ), _DebugActionChip( label: 'ClearLogs', onTap: widget.onClearLogs, ), ], ), SizedBox(height: 10.w), Text( 'Recent Logs', style: TextStyle( color: Colors.white, fontSize: 11.sp, fontWeight: FontWeight.w700, ), ), SizedBox(height: 6.w), Container( height: 180.w, padding: EdgeInsets.all(8.w), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.22), borderRadius: BorderRadius.circular(12.w), border: Border.all( color: Colors.white.withValues(alpha: 0.08), ), ), child: widget.snapshot.logs.isEmpty ? Center( child: Text( 'No bridge logs yet', style: TextStyle( color: Colors.white54, fontSize: 11.sp, ), ), ) : ListView.separated( itemCount: widget.snapshot.logs.length, separatorBuilder: (_, __) => SizedBox(height: 6.w), itemBuilder: (context, index) { final entry = widget.snapshot.logs[index]; return Text( '[${entry.formattedTime}] ${entry.tag} ${entry.message}', style: TextStyle( color: Colors.white70, fontSize: 10.sp, height: 1.35, ), ); }, ), ), ], ), ) : GestureDetector( key: const ValueKey('baishun_debug_panel_collapsed'), behavior: HitTestBehavior.opaque, onTap: () { setState(() { _expanded = true; }); }, child: Container( padding: EdgeInsets.symmetric( horizontal: 12.w, vertical: 8.w, ), decoration: BoxDecoration( color: const Color(0xD916302B), borderRadius: BorderRadius.circular(999.w), border: Border.all( color: Colors.white.withValues(alpha: 0.12), ), ), child: Text( 'BS DEBUG', style: TextStyle( color: Colors.white, fontSize: 11.sp, fontWeight: FontWeight.w700, ), ), ), ), ); } } class _InfoLine extends StatelessWidget { const _InfoLine({required this.label, required this.value}); final String label; final String value; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: 4.w), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 82.w, child: Text( '$label:', style: TextStyle( color: Colors.white60, fontSize: 10.sp, fontWeight: FontWeight.w600, ), ), ), Expanded( child: Text( value, style: TextStyle( color: Colors.white, fontSize: 10.sp, height: 1.3, ), ), ), ], ), ); } } class _MultilineLine extends StatelessWidget { const _MultilineLine({ required this.label, required this.value, this.valueColor = Colors.white, }); final String label; final String value; final Color valueColor; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(top: 4.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '$label:', style: TextStyle( color: Colors.white60, fontSize: 10.sp, fontWeight: FontWeight.w600, ), ), SizedBox(height: 2.w), SelectableText( value, style: TextStyle(color: valueColor, fontSize: 10.sp, height: 1.35), ), ], ), ); } } class _DebugActionChip extends StatelessWidget { const _DebugActionChip({required this.label, required this.onTap}); final String label; final VoidCallback onTap; @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: onTap, child: Container( padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.w), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(999.w), border: Border.all(color: Colors.white.withValues(alpha: 0.08)), ), child: Text( label, style: TextStyle( color: Colors.white, fontSize: 10.sp, fontWeight: FontWeight.w600, ), ), ), ); } }