import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../shared/models/device_state.dart'; import '../shared/models/match.dart'; import '../shared/models/stream_session.dart'; enum DeviceRole { camera, controller } class SessionState { const SessionState({ this.session, this.match, this.connected = false, this.deviceRole = DeviceRole.controller, this.cameraDevice, this.elapsed = Duration.zero, this.viewerCount = 0, this.loading = false, this.error, }); final StreamSession? session; final MatchModel? match; final bool connected; final DeviceRole deviceRole; final DeviceStateModel? cameraDevice; final Duration elapsed; final int viewerCount; final bool loading; final String? error; SessionState copyWith({ StreamSession? session, MatchModel? match, bool? connected, DeviceRole? deviceRole, DeviceStateModel? cameraDevice, Duration? elapsed, int? viewerCount, bool? loading, String? error, bool clearError = false, }) { return SessionState( session: session ?? this.session, match: match ?? this.match, connected: connected ?? this.connected, deviceRole: deviceRole ?? this.deviceRole, cameraDevice: cameraDevice ?? this.cameraDevice, elapsed: elapsed ?? this.elapsed, viewerCount: viewerCount ?? this.viewerCount, loading: loading ?? this.loading, error: clearError ? null : (error ?? this.error), ); } } class SessionNotifier extends StateNotifier { SessionNotifier() : super(const SessionState()); void setSession(StreamSession session, {MatchModel? match}) { state = state.copyWith( session: session, match: match, clearError: true, ); } void setMatch(MatchModel match) { state = state.copyWith(match: match); } void setConnected(bool connected) { state = state.copyWith(connected: connected); } void setDeviceRole(DeviceRole role) { state = state.copyWith(deviceRole: role); } void updateDeviceState(DeviceStateModel device) { if (device.deviceRole == 'camera') { state = state.copyWith(cameraDevice: device); } if (device.status != null && state.session != null) { state = state.copyWith( session: StreamSession( id: state.session!.id, matchId: state.session!.matchId, status: device.status!, platform: state.session!.platform, rtmpIngestUrl: state.session!.rtmpIngestUrl, youtubeBroadcastId: state.session!.youtubeBroadcastId, privacyStatus: state.session!.privacyStatus, qualityPreset: state.session!.qualityPreset, targetBitrate: state.session!.targetBitrate, startedAt: state.session!.startedAt, disconnectionCount: state.session!.disconnectionCount, score: state.session!.score, devices: state.session!.devices, ), ); } } void setElapsed(Duration elapsed) { state = state.copyWith(elapsed: elapsed); } void setViewerCount(int count) { state = state.copyWith(viewerCount: count); } void applyCommand(String action) { final session = state.session; if (session == null) return; String newStatus = session.status; switch (action) { case 'start_stream': newStatus = 'live'; break; case 'pause_stream': newStatus = 'paused'; break; case 'stop_stream': newStatus = 'ended'; break; } state = state.copyWith( session: StreamSession( id: session.id, matchId: session.matchId, status: newStatus, platform: session.platform, rtmpIngestUrl: session.rtmpIngestUrl, youtubeBroadcastId: session.youtubeBroadcastId, privacyStatus: session.privacyStatus, qualityPreset: session.qualityPreset, targetBitrate: session.targetBitrate, startedAt: session.startedAt, disconnectionCount: session.disconnectionCount, score: session.score, devices: session.devices, ), ); } void setLoading(bool loading) { state = state.copyWith(loading: loading); } void setError(String message) { state = state.copyWith(loading: false, error: message); } void reset() { state = const SessionState(); } } final sessionProvider = StateNotifierProvider( (ref) => SessionNotifier(), );