import 'dart:convert';
import 'dart:io';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/providers/auth.dart';
import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_models/room.dart';

part 'web_socket.g.dart';

final _logger = Logger('WebSocketNotifier');

@riverpod
class WebSocketNotifier extends _$WebSocketNotifier {
  @override
  Future<WebSocket?> build() async {
    return null;
  }

  Future<void> connect() async {
    state = AsyncValue.loading();
    final jwt = ref.read(jwtNotifierProvider).valueOrNull;
    final jwtBody = ref.read(jwtBodyProvider);
    if (jwt == null || jwtBody == null) {
      _logger.warning('Tried to connect to ws without jwt token');
      return;
    }

    final wsUrl = Uri.parse('ws://localhost:8080/room/${jwtBody.roomCode}/ws');
    _logger.finest('Attempting to connect to $wsUrl');

    try {
      final connection = await WebSocket.connect(wsUrl.toString(), headers: {'Authorization': 'Bearer: $jwt'});
      _logger.fine('Client ws connection established to room ${jwtBody.roomUuid}');
      state = AsyncValue.data(connection);
    } catch (e) {
      if (e is WebSocketException) {
        if (e.httpStatusCode == 401) {
          ref.read(jwtNotifierProvider.notifier).eraseJwt();
        }
      }
      _logger.warning('Error occurred creating web socket: $e');
    }
  }

  void sendMessage(GameRoomMessage message) {
    final msgStr = jsonEncode(message.toJson());
    final socket = state.valueOrNull;
    if (socket == null) {
      // TODO add queue
      _logger.info('Socket unavailable... adding to queue');
      throw UnimplementedError('No queue available');
    }
    _logger.finest('Sending message $message on websocket');
    socket.add(msgStr);
  }
}

@riverpod
Raw<Stream<dynamic>> webSocketStream(Ref ref) {
  final connection = ref.watch(webSocketNotifierProvider).valueOrNull;
  if (connection == null) return Stream.empty();
  _logger.finest('Created broadcast stream from ws connection');
  return connection.asBroadcastStream();
}