import 'dart:async';

import 'package:logger/logger.dart';
import 'package:rxdart/rxdart.dart';

import '../../domain/entities/monitor_state.dart';
import '../../domain/entities/alarm_state.dart';
import '../models/bridge_event.dart';
import 'bridge_service.dart';
import 'alarm_service.dart';

/// Service for tracking overall system state (monitors + alarms)
class StateService {
  final BridgeService _bridgeService;
  final AlarmService _alarmService;
  final Logger _logger = Logger();

  StreamSubscription? _eventSubscription;

  // Monitor state
  final _monitorStatesController =
      BehaviorSubject<Map<int, MonitorState>>.seeded({});

  // Combined state stream (monitors with alarm flags)
  final _combinedMonitorStatesController =
      BehaviorSubject<Map<int, MonitorState>>.seeded({});

  StateService({
    required BridgeService bridgeService,
    required AlarmService alarmService,
  })  : _bridgeService = bridgeService,
        _alarmService = alarmService;

  /// Stream of monitor states (without alarm info)
  Stream<Map<int, MonitorState>> get monitorStates =>
      _monitorStatesController.stream;

  /// Stream of monitor states with alarm flags
  Stream<Map<int, MonitorState>> get combinedMonitorStates =>
      _combinedMonitorStatesController.stream;

  /// Stream of active alarms (delegated to AlarmService)
  Stream<List<AlarmState>> get activeAlarms => _alarmService.alarms;

  /// Current monitor states
  Map<int, MonitorState> get currentMonitorStates =>
      _monitorStatesController.value;

  /// Get state for a specific monitor
  MonitorState? getMonitorState(int viewerId) {
    return _combinedMonitorStatesController.value[viewerId];
  }

  /// Initialize state tracking
  Future<void> initialize() async {
    // Subscribe to bridge events
    _eventSubscription = _bridgeService.eventStream.listen(_handleBridgeEvent);

    // Subscribe to alarm changes to update monitor flags
    _alarmService.alarms.listen((_) => _updateCombinedStates());

    _logger.i('StateService initialized');
  }

  /// Sync initial state from all bridges
  Future<void> syncFromBridges() async {
    _logger.i('Syncing state from bridges...');

    final connectionStatus = _bridgeService.currentConnectionStatus;
    final monitors = <int, MonitorState>{};

    for (final entry in connectionStatus.entries) {
      if (!entry.value) continue; // Skip disconnected servers

      try {
        final serverMonitors =
            await _bridgeService.getMonitorStates(entry.key);

        for (final monitorJson in serverMonitors) {
          final state = MonitorState.fromJson(monitorJson);
          monitors[state.viewerId] = state;
        }

        _logger.d(
            'Synced ${serverMonitors.length} monitors from ${entry.key}');
      } catch (e) {
        _logger.e('Failed to sync monitors from ${entry.key}: $e');
      }
    }

    _monitorStatesController.add(monitors);
    _updateCombinedStates();

    // Also sync alarms
    await _alarmService.queryAllAlarms();

    _logger.i('State sync complete: ${monitors.length} monitors');
  }

  /// Handle incoming bridge event
  void _handleBridgeEvent(BridgeEvent event) {
    if (event.isViewerConnected || event.isViewerSelectionChanged) {
      _handleViewerConnected(event);
    } else if (event.isViewerCleared) {
      _handleViewerCleared(event);
    } else if (event.isEventStarted ||
        event.isEventStopped ||
        event.isAlarmQueueNotification) {
      // Delegate alarm events to AlarmService
      _alarmService.handleAlarmEvent(event);
    }
  }

  /// Handle viewer connected event
  void _handleViewerConnected(BridgeEvent event) {
    final viewer = event.viewer;
    final channel = event.channel;
    final playMode = event.playMode;

    if (viewer == null) return;

    final monitors = Map<int, MonitorState>.from(_monitorStatesController.value);
    final existing = monitors[viewer];

    monitors[viewer] = MonitorState(
      viewerId: viewer,
      currentChannel: channel ?? existing?.currentChannel ?? 0,
      playMode: PlayMode.fromValue(playMode ?? existing?.playMode.value ?? 0),
      serverId: event.serverId,
      lastUpdated: event.timestamp,
    );

    _monitorStatesController.add(monitors);
    _updateCombinedStates();

    _logger.d('Monitor $viewer connected to channel $channel');
  }

  /// Handle viewer cleared event
  void _handleViewerCleared(BridgeEvent event) {
    final viewer = event.viewer;
    if (viewer == null) return;

    final monitors = Map<int, MonitorState>.from(_monitorStatesController.value);
    final existing = monitors[viewer];

    if (existing != null) {
      monitors[viewer] = existing.cleared();
    } else {
      monitors[viewer] = MonitorState(
        viewerId: viewer,
        currentChannel: 0,
        playMode: PlayMode.unknown,
        serverId: event.serverId,
        lastUpdated: event.timestamp,
      );
    }

    _monitorStatesController.add(monitors);
    _updateCombinedStates();

    _logger.d('Monitor $viewer cleared');
  }

  /// Update combined states with alarm flags
  void _updateCombinedStates() {
    final monitors = _monitorStatesController.value;
    final combined = <int, MonitorState>{};

    for (final entry in monitors.entries) {
      final hasAlarm = _alarmService.isMonitorBlocked(entry.key);
      combined[entry.key] = entry.value.withAlarm(hasAlarm);
    }

    _combinedMonitorStatesController.add(combined);
  }

  /// Check if a monitor is blocked by an alarm
  bool isMonitorBlocked(int viewerId) {
    return _alarmService.isMonitorBlocked(viewerId);
  }

  /// Dispose resources
  void dispose() {
    _eventSubscription?.cancel();
    _monitorStatesController.close();
    _combinedMonitorStatesController.close();
  }
}
