import 'dart:async';

import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../../core/network/ws_client.dart';
import '../../domain/entities/game_entities.dart';
import '../../domain/repository/game_repository.dart';
import 'game_event.dart';
import 'game_state.dart';

/// بلوکِ realtime بازی: به استریمِ پیام‌ها/وضعیتِ repository گوش می‌دهد و
/// اقدام‌های بازیکن را به سرور می‌فرستد. متدهای کمکی برای موتور Flame هم دارد.
class GameBloc extends Bloc<GameEvent, GameUiState> {
  final GameRepository repository;
  late final StreamSubscription _msgSub;
  late final StreamSubscription _statusSub;

  Map<String, dynamic> _joinAction = const {};
  bool _joined = false;
  int _bubbleSeq = 0; // شناسه‌ی یکتا برای هر حبابِ پیام (انقضای دقیق)

  GameBloc(this.repository) : super(const GameUiState()) {
    _msgSub =
        repository.messages.listen((m) => add(GameMessageReceived(m)));
    _statusSub =
        repository.status.listen((s) => add(GameStatusChanged(s)));

    on<ConnectGameEvent>((event, emit) async {
      _joinAction = event.joinAction;
      _joined = false;
      await repository.connect();
    });

    on<GameStatusChanged>((event, emit) {
      emit(state.copyWith(connection: event.status));
      if (event.status == WsStatus.connected && !_joined) {
        _joined = true;
        repository.send(_joinAction);
      }
    });

    on<GameMessageReceived>((event, emit) => _onMessage(event.message, emit));

    on<ChooseTrumpEvent>(
        (event, emit) => repository.send({'type': 'choose_trump', 'suit': event.suit}));
    on<PlayCardEvent>(
        (event, emit) => repository.send({'type': 'play_card', 'card': event.card}));
    on<ReshuffleEvent>((event, emit) => repository.send({'type': 'reshuffle'}));
    on<SendChatEvent>((event, emit) => repository.send({
          'type': 'chat',
          if (event.text != null) 'text': event.text,
          if (event.emoji != null) 'emoji': event.emoji,
        }));
    on<ExpireChatEvent>((event, emit) {
      final cur = state.chatBubbles[event.seat];
      if (cur == null || cur.id != event.id) return; // حبابِ جدیدتر است ⇒ نگه‌دار
      final next = Map<int, ChatBubble>.from(state.chatBubbles)
        ..remove(event.seat);
      emit(state.copyWith(chatBubbles: next));
    });
    on<LeaveGameEvent>((event, emit) => repository.send({'type': 'leave'}));
    on<StartTableEvent>((event, emit) => repository.send(
        {'type': 'start_table', 'hands': event.hands, 'stake': event.stake}));
    on<SetStakeEvent>((event, emit) =>
        repository.send({'type': 'set_stake', 'stake': event.stake}));
    on<RotateTableEvent>((event, emit) =>
        repository.send({'type': 'rotate_table', 'dir': event.dir}));
    on<LeaveTableEvent>((event, emit) => repository.send({'type': 'leave_table'}));
    on<ClearNoticeEvent>((event, emit) => emit(state.copyWith(clearNotice: true)));
  }

  void _onMessage(Map<String, dynamic> msg, Emitter<GameUiState> emit) {
    switch (msg['type']) {
      case 'state':
        final gs = GameState.fromJson(msg);
        final clear = gs.phase == 'choose_trump' || gs.phase == 'playing';
        emit(state.copyWith(state: gs, clearHandResult: clear));
      case 'hand_over':
        emit(state.copyWith(handResult: HandResult.fromJson(msg)));
      case 'game_over':
        emit(state.copyWith(gameOver: GameOver.fromJson(msg)));
      case 'table_lobby':
        emit(state.copyWith(lobby: TableLobby.fromJson(msg)));
      case 'countdown':
        emit(state.copyWith(countdown: (msg['seconds'] ?? 3) as int));
      case 'table_closed':
        emit(state.copyWith(
            tableClosed: true, notice: 'میز توسط میزبان بسته شد'));
      case 'player_disconnected':
        emit(state.copyWith(notice: 'یک بازیکن قطع شد'));
      case 'player_reconnected':
        emit(state.copyWith(notice: 'بازیکن بازگشت'));
      case 'chat':
        final seat = (msg['seat'] ?? 0) as int;
        final id = ++_bubbleSeq;
        final bubble = ChatBubble(
          text: msg['text'] as String?,
          emoji: msg['emoji'] as String?,
          id: id,
        );
        final next = Map<int, ChatBubble>.from(state.chatBubbles)
          ..[seat] = bubble;
        emit(state.copyWith(chatBubbles: next));
        // انقضای خودکار پس از ۴ ثانیه (مگر حبابِ جدیدتری روی همان جایگاه بنشیند).
        Timer(const Duration(seconds: 4), () => add(ExpireChatEvent(seat, id)));
      case 'player_left':
        emit(state.copyWith(notice: 'یک بازیکن میز را ترک کرد'));
      case 'error':
        emit(state.copyWith(notice: (msg['message'] ?? 'خطا').toString()));
    }
  }

  // --- متدهای کمکی برای موتور Flame و صفحه‌ها ---
  void chooseTrump(String suit) => add(ChooseTrumpEvent(suit));
  void reshuffle() => add(ReshuffleEvent());
  void sendChatText(String text) => add(SendChatEvent(text: text));
  void sendChatEmoji(String emoji) => add(SendChatEvent(emoji: emoji));
  void playCard(String card) => add(PlayCardEvent(card));
  void leave() => add(LeaveGameEvent());
  void startTable(int hands, {int stake = 0}) =>
      add(StartTableEvent(hands, stake: stake));
  void setStake(int stake) => add(SetStakeEvent(stake));
  void rotateTable(int dir) => add(RotateTableEvent(dir));
  void leaveTable() => add(LeaveTableEvent());
  void clearNotice() => add(ClearNoticeEvent());

  @override
  Future<void> close() {
    _msgSub.cancel();
    _statusSub.cancel();
    repository.disconnect();
    return super.close();
  }
}
