import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../../data/models/analysis_models.dart'; import '../../../../services/analysis_providers.dart'; import '../../../shared/presentation/widgets/async_value_view.dart'; import '../../../shared/presentation/widgets/lab_section_scaffold.dart'; class ObservationPage extends ConsumerWidget { const ObservationPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final snapshot = ref.watch(selectedSessionSnapshotProvider); final observation = ref.watch(selectedObservationProvider); return LabSectionScaffold( eyebrow: 'Observation', title: 'Inspect sample metadata and probe evidence.', description: 'This page will show the captured audio summary, waveform slices, probe output, and sample tags before deeper analysis begins.', children: [ AsyncValueView( value: snapshot, loadingMessage: 'Loading observation snapshot...', data: (session) { if (session != null) { return _ObservationSessionView(session: session); } return AsyncValueView( value: observation, loadingMessage: 'Loading observation details...', data: (selectedObservation) { if (selectedObservation == null) { return const _EmptySelectionCard(); } return _ObservationOnlyView(observation: selectedObservation); }, ); }, ), ], ); } } class _ObservationSessionView extends StatelessWidget { const _ObservationSessionView({required this.session}); final AnalysisSessionSnapshot session; @override Widget build(BuildContext context) { return Column( children: [ _ObservationDetailView( observation: session.observation, headerLine: 'Mode: ${session.mode} | Status: ${session.status}', ), const SizedBox(height: 16), Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Probe Evidence', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 12), ...session.probeEvidence.map( (evidence) => ListTile( contentPadding: EdgeInsets.zero, title: Text( '${evidence.category} | ${evidence.producerModuleId ?? 'unknown'}', ), subtitle: Text( evidence.values.entries .map((entry) => '${entry.key}: ${entry.value}') .join(' | '), ), trailing: Text(evidence.confidence.toStringAsFixed(2)), ), ), ], ), ), ), const SizedBox(height: 16), FilledButton( onPressed: () => context.goNamed('experiment'), child: const Text('Open Experiment Timeline'), ), ], ); } } class _ObservationDetailView extends StatelessWidget { const _ObservationDetailView({ required this.observation, this.headerLine, }); final ObservationSummary observation; final String? headerLine; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Observation ${observation.id}', style: theme.textTheme.titleLarge, ), if (headerLine != null) ...[ const SizedBox(height: 10), Text(headerLine!, style: theme.textTheme.bodyMedium), ], const SizedBox(height: 14), Text( 'Duration ${observation.durationMs} ms | ${observation.sampleRate} Hz | ${observation.channels} channel', style: theme.textTheme.bodyLarge, ), const SizedBox(height: 12), if (observation.tags.isEmpty) Text('No tags attached yet.', style: theme.textTheme.bodyMedium) else Wrap( spacing: 8, runSpacing: 8, children: observation.tags .map((tag) => Chip(label: Text(tag))) .toList(), ), if (observation.captureMetadata.isNotEmpty) ...[ const SizedBox(height: 14), ...observation.captureMetadata.entries.map( (entry) => Padding( padding: const EdgeInsets.only(bottom: 4), child: Text('${entry.key}: ${entry.value}'), ), ), ], ], ), ), ); } } class _ObservationOnlyView extends ConsumerStatefulWidget { const _ObservationOnlyView({required this.observation}); final ObservationSummary observation; @override ConsumerState<_ObservationOnlyView> createState() => _ObservationOnlyViewState(); } class _ObservationOnlyViewState extends ConsumerState<_ObservationOnlyView> { bool _creatingSession = false; String? _errorMessage; @override Widget build(BuildContext context) { return Column( children: [ _ObservationDetailView(observation: widget.observation), const SizedBox(height: 16), if (_errorMessage != null) Card( child: Padding( padding: const EdgeInsets.all(20), child: Text(_errorMessage!), ), ), if (_errorMessage != null) const SizedBox(height: 16), FilledButton( onPressed: _creatingSession ? null : _createSession, child: Text( _creatingSession ? 'Creating Analysis Session...' : 'Analyze This Observation', ), ), ], ); } Future _createSession() async { setState(() { _creatingSession = true; _errorMessage = null; }); try { await ref .read(sessionActionsProvider) .createSessionForObservation(widget.observation.id); if (!mounted) return; context.goNamed('experiment'); } catch (error) { if (!mounted) return; setState(() { _errorMessage = 'Failed to create analysis session: $error'; }); } finally { if (mounted) { setState(() { _creatingSession = false; }); } } } } class _EmptySelectionCard extends StatelessWidget { const _EmptySelectionCard(); @override Widget build(BuildContext context) { return const Card( child: Padding( padding: EdgeInsets.all(20), child: Text( 'No observation selected yet. Open one from History or create a new capture.', ), ), ); } }