import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../data/services/bridge_service.dart';
import '../../../data/services/coordination_service.dart';
import 'ptz_event.dart';
import 'ptz_state.dart';

class PtzBloc extends Bloc<PtzEvent, PtzState> {
  final BridgeService _bridgeService;
  final CoordinationService _coordinationService;

  Timer? _lockResetTimer;

  PtzBloc({
    required BridgeService bridgeService,
    required CoordinationService coordinationService,
  })  : _bridgeService = bridgeService,
        _coordinationService = coordinationService,
        super(const PtzState()) {
    on<PtzPanStart>(_onPanStart);
    on<PtzTiltStart>(_onTiltStart);
    on<PtzZoomStart>(_onZoomStart);
    on<PtzStop>(_onStop);
    on<PtzGoToPreset>(_onGoToPreset);
    on<PtzSetCamera>(_onSetCamera);
  }

  /// Ensure we have a lock on the camera before PTZ movement.
  /// Returns true if lock was acquired or already held.
  Future<bool> _ensureLock(int cameraId, Emitter<PtzState> emit) async {
    // Already locked by us
    if (_coordinationService.isCameraLockedByMe(cameraId)) {
      emit(state.copyWith(lockStatus: PtzLockStatus.locked));
      return true;
    }

    emit(state.copyWith(lockStatus: PtzLockStatus.acquiring));

    final result = await _coordinationService.tryLock(cameraId);
    if (result.acquired) {
      emit(state.copyWith(lockStatus: PtzLockStatus.locked));
      _startLockResetTimer(cameraId);
      return true;
    }

    // Lock denied — someone else has it
    emit(state.copyWith(
      lockStatus: PtzLockStatus.denied,
      lockedBy: result.lock?.ownerName,
      error: 'Camera locked by ${result.lock?.ownerName ?? "another keyboard"}',
    ));
    return false;
  }

  void _startLockResetTimer(int cameraId) {
    _lockResetTimer?.cancel();
    _lockResetTimer = Timer.periodic(const Duration(minutes: 2), (_) {
      if (_coordinationService.isCameraLockedByMe(cameraId)) {
        _coordinationService.resetExpiration(cameraId);
      } else {
        _lockResetTimer?.cancel();
      }
    });
  }

  Future<void> _onPanStart(
    PtzPanStart event,
    Emitter<PtzState> emit,
  ) async {
    emit(state.copyWith(activeCameraId: event.cameraId));

    if (!await _ensureLock(event.cameraId, emit)) return;

    final direction =
        event.direction == 'left' ? PtzDirection.left : PtzDirection.right;
    emit(state.copyWith(
      currentDirection: direction,
      isMoving: true,
      error: null,
    ));

    try {
      await _bridgeService.ptzPan(event.cameraId, event.direction, event.speed);
    } catch (e) {
      emit(state.copyWith(error: e.toString(), isMoving: false));
    }
  }

  Future<void> _onTiltStart(
    PtzTiltStart event,
    Emitter<PtzState> emit,
  ) async {
    emit(state.copyWith(activeCameraId: event.cameraId));

    if (!await _ensureLock(event.cameraId, emit)) return;

    final direction =
        event.direction == 'up' ? PtzDirection.up : PtzDirection.down;
    emit(state.copyWith(
      currentDirection: direction,
      isMoving: true,
      error: null,
    ));

    try {
      await _bridgeService.ptzTilt(
          event.cameraId, event.direction, event.speed);
    } catch (e) {
      emit(state.copyWith(error: e.toString(), isMoving: false));
    }
  }

  Future<void> _onZoomStart(
    PtzZoomStart event,
    Emitter<PtzState> emit,
  ) async {
    emit(state.copyWith(activeCameraId: event.cameraId));

    if (!await _ensureLock(event.cameraId, emit)) return;

    final direction =
        event.direction == 'in' ? PtzDirection.zoomIn : PtzDirection.zoomOut;
    emit(state.copyWith(
      currentDirection: direction,
      isMoving: true,
      error: null,
    ));

    try {
      await _bridgeService.ptzZoom(
          event.cameraId, event.direction, event.speed);
    } catch (e) {
      emit(state.copyWith(error: e.toString(), isMoving: false));
    }
  }

  Future<void> _onStop(
    PtzStop event,
    Emitter<PtzState> emit,
  ) async {
    emit(state.copyWith(
      currentDirection: PtzDirection.none,
      isMoving: false,
      error: null,
    ));

    try {
      await _bridgeService.ptzStop(event.cameraId);
    } catch (e) {
      emit(state.copyWith(error: e.toString()));
    }
  }

  Future<void> _onGoToPreset(
    PtzGoToPreset event,
    Emitter<PtzState> emit,
  ) async {
    if (!await _ensureLock(event.cameraId, emit)) return;

    emit(state.copyWith(error: null));

    try {
      await _bridgeService.ptzPreset(event.cameraId, event.preset);
    } catch (e) {
      emit(state.copyWith(error: e.toString()));
    }
  }

  void _onSetCamera(
    PtzSetCamera event,
    Emitter<PtzState> emit,
  ) {
    if (event.cameraId == null) {
      _lockResetTimer?.cancel();
      emit(state.copyWith(
          clearCamera: true, lockStatus: PtzLockStatus.none));
    } else {
      emit(state.copyWith(activeCameraId: event.cameraId));
    }
  }

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