import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../data/services/bridge_service.dart';
import '../../../domain/entities/wall_config.dart';
import 'wall_event.dart';
import 'wall_state.dart';

class WallBloc extends Bloc<WallEvent, WallState> {
  final BridgeService _bridgeService;
  Timer? _editTimeoutTimer;

  /// Legacy cancel timeout: 5 seconds of inactivity cancels edit mode.
  static const _editTimeout = Duration(seconds: 5);

  WallBloc({required BridgeService bridgeService})
      : _bridgeService = bridgeService,
        super(const WallState()) {
    on<LoadWallConfig>(_onLoadWallConfig);
    on<SelectViewer>(_onSelectViewer);
    on<DeselectViewer>(_onDeselectViewer);
    on<SetCameraPrefix>(_onSetCameraPrefix);
    on<AddCameraDigit>(_onAddCameraDigit);
    on<BackspaceCameraDigit>(_onBackspaceCameraDigit);
    on<CancelCameraEdit>(_onCancelCameraEdit);
    on<CycleCameraPrefix>(_onCycleCameraPrefix);
    on<ExecuteCrossSwitch>(_onExecuteCrossSwitch);
    on<UpdateViewerCamera>(_onUpdateViewerCamera);
    on<SetViewerAlarm>(_onSetViewerAlarm);
    on<SetViewerLock>(_onSetViewerLock);
    on<ToggleSectionExpanded>(_onToggleSectionExpanded);
  }

  @override
  Future<void> close() {
    _editTimeoutTimer?.cancel();
    return super.close();
  }

  void _restartEditTimeout() {
    _editTimeoutTimer?.cancel();
    _editTimeoutTimer = Timer(_editTimeout, () {
      add(const CancelCameraEdit());
    });
  }

  void _cancelEditTimeout() {
    _editTimeoutTimer?.cancel();
  }

  void _onLoadWallConfig(LoadWallConfig event, Emitter<WallState> emit) {
    emit(state.copyWith(isLoading: true, clearError: true));

    try {
      // Use provided config or load sample
      final config = event.config ?? WallConfig.sample();

      // Initialize viewer states for all viewers
      final viewerStates = <int, ViewerState>{};
      for (final viewerId in config.allViewerIds) {
        viewerStates[viewerId] = ViewerState(viewerId: viewerId);
      }

      // Expand all sections by default
      final expandedSections = config.sections.map((s) => s.id).toSet();

      emit(state.copyWith(
        config: config,
        isLoading: false,
        viewerStates: viewerStates,
        expandedSections: expandedSections,
      ));
    } catch (e) {
      emit(state.copyWith(
        isLoading: false,
        error: 'Failed to load wall config: $e',
      ));
    }
  }

  void _onSelectViewer(SelectViewer event, Emitter<WallState> emit) {
    if (state.config == null) return;

    // Find the physical monitor containing this viewer
    final monitor = state.config!.findMonitorByViewerId(event.viewerId);

    _cancelEditTimeout();
    emit(state.copyWith(
      selectedViewerId: event.viewerId,
      selectedPhysicalMonitorId: monitor?.id,
      cameraNumberInput: '',
      isEditing: false,
    ));
  }

  void _onDeselectViewer(DeselectViewer event, Emitter<WallState> emit) {
    _cancelEditTimeout();
    emit(state.copyWith(
      clearSelection: true,
      cameraNumberInput: '',
      isEditing: false,
    ));
  }

  void _onSetCameraPrefix(SetCameraPrefix event, Emitter<WallState> emit) {
    if (event.prefix != 500 && event.prefix != 501 && event.prefix != 502) {
      return;
    }
    emit(state.copyWith(cameraPrefix: event.prefix));
  }

  void _onAddCameraDigit(AddCameraDigit event, Emitter<WallState> emit) {
    if (event.digit < 0 || event.digit > 9) return;
    if (state.selectedViewerId == null) return;
    if (state.cameraNumberInput.length >= 6) return; // Max 6 digits (legacy)

    _restartEditTimeout();
    emit(state.copyWith(
      cameraNumberInput: state.cameraNumberInput + event.digit.toString(),
      isEditing: true,
    ));
  }

  void _onBackspaceCameraDigit(
      BackspaceCameraDigit event, Emitter<WallState> emit) {
    if (state.cameraNumberInput.isEmpty) return;

    final newInput = state.cameraNumberInput
        .substring(0, state.cameraNumberInput.length - 1);
    if (newInput.isEmpty) {
      _cancelEditTimeout();
      emit(state.copyWith(cameraNumberInput: '', isEditing: false));
    } else {
      _restartEditTimeout();
      emit(state.copyWith(cameraNumberInput: newInput));
    }
  }

  void _onCancelCameraEdit(CancelCameraEdit event, Emitter<WallState> emit) {
    _cancelEditTimeout();
    emit(state.copyWith(cameraNumberInput: '', isEditing: false));
  }

  void _onCycleCameraPrefix(
      CycleCameraPrefix event, Emitter<WallState> emit) {
    const prefixes = [500, 501, 502];
    final idx = prefixes.indexOf(state.cameraPrefix);
    final next = prefixes[(idx + 1) % prefixes.length];
    emit(state.copyWith(cameraPrefix: next));
  }

  Future<void> _onExecuteCrossSwitch(
      ExecuteCrossSwitch event, Emitter<WallState> emit) async {
    print('CrossSwitch: canExecute=${state.canExecuteCrossSwitch}, selectedViewer=${state.selectedViewerId}, cameraInput=${state.cameraNumberInput}, fullCamera=${state.fullCameraNumber}');
    if (!state.canExecuteCrossSwitch) {
      print('CrossSwitch: Cannot execute - returning early');
      return;
    }

    final viewerId = state.selectedViewerId!;
    final cameraId = state.fullCameraNumber!;

    try {
      print('CrossSwitch: Calling viewerConnectLive(viewer=$viewerId, camera=$cameraId)');
      // Execute CrossSwitch via bridge service
      final result = await _bridgeService.viewerConnectLive(viewerId, cameraId);
      print('CrossSwitch: Result = $result');

      // Update local state
      final viewerStates = Map<int, ViewerState>.from(state.viewerStates);
      viewerStates[viewerId] = state.getViewerState(viewerId).copyWith(
            currentCameraId: cameraId,
            isLive: true,
          );

      _cancelEditTimeout();
      emit(state.copyWith(
        viewerStates: viewerStates,
        cameraNumberInput: '',
        isEditing: false,
      ));
    } catch (e) {
      emit(state.copyWith(error: 'CrossSwitch failed: $e'));
    }
  }

  void _onUpdateViewerCamera(
      UpdateViewerCamera event, Emitter<WallState> emit) {
    final viewerStates = Map<int, ViewerState>.from(state.viewerStates);

    viewerStates[event.viewerId] =
        (viewerStates[event.viewerId] ?? ViewerState(viewerId: event.viewerId))
            .copyWith(
      currentCameraId: event.cameraId,
      isLive: event.isLive,
    );

    emit(state.copyWith(viewerStates: viewerStates));
  }

  void _onSetViewerAlarm(SetViewerAlarm event, Emitter<WallState> emit) {
    final viewerStates = Map<int, ViewerState>.from(state.viewerStates);

    viewerStates[event.viewerId] =
        (viewerStates[event.viewerId] ?? ViewerState(viewerId: event.viewerId))
            .copyWith(hasAlarm: event.hasAlarm);

    emit(state.copyWith(viewerStates: viewerStates));
  }

  void _onSetViewerLock(SetViewerLock event, Emitter<WallState> emit) {
    final viewerStates = Map<int, ViewerState>.from(state.viewerStates);

    viewerStates[event.viewerId] =
        (viewerStates[event.viewerId] ?? ViewerState(viewerId: event.viewerId))
            .copyWith(
      isLocked: event.isLocked,
      lockedBy: event.lockedBy,
    );

    emit(state.copyWith(viewerStates: viewerStates));
  }

  void _onToggleSectionExpanded(
      ToggleSectionExpanded event, Emitter<WallState> emit) {
    final expandedSections = Set<String>.from(state.expandedSections);

    if (expandedSections.contains(event.sectionId)) {
      expandedSections.remove(event.sectionId);
    } else {
      expandedSections.add(event.sectionId);
    }

    emit(state.copyWith(expandedSections: expandedSections));
  }
}
