import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:bus_infotainment/backend/modules/info_module.dart';
import 'package:bus_infotainment/utils/delegates.dart';

class NetworkingModule extends InfoModule {
  // Host websocket server
  String host = "ws://0.0.0.0:8080";
  HttpServer? _server;
  WebSocketChannel? _channel;

  // Store connected WebSocket channels
  final List<WebSocketChannel> _connectedClients = [];

  EventDelegate<String>? onMessageReceived = EventDelegate();

  NetworkingModule() {
    _refresh();
    refreshTimer();
  }

  Future<bool> startWebSocketServer() async {
    try {
      var handler = webSocketHandler((WebSocketChannel webSocket) {
        _connectedClients.add(webSocket); // Add the client to the list
        print('Client connected: ${webSocket}'); // Log client connection

        webSocket.stream.listen((message) {
          // Handle messages from the client here
          print('Received message: $message');

          // Forward message to all clients except the sender
          for (var client in _connectedClients) {
            if (client != webSocket) {
              client.sink.add(message);
            }
          }

          _onMessageReceived(message);
        }, onDone: () {
          _connectedClients.remove(webSocket); // Remove client on disconnect
          print('Client disconnected: ${webSocket}'); // Log client disconnection
        });
      });

      _server = await io.serve(handler, InternetAddress.anyIPv4, 8080);
      print('WebSocket server started at ${_server?.address.address}:${_server?.port}');

      return true;
    } catch (e) {
      print('Failed to start WebSocket server: $e');
      return false;
    }
  }

  bool stopWebSocketServer() {
    if (_server == null) {
      throw Exception('WebSocket server is not running');
    }

    try {
      for (var client in _connectedClients) {
        client.sink.close();
      }
      _connectedClients.clear();
      _server?.close(force: true);
      _server = null;
      print('WebSocket server stopped');
      return true;
    } catch (e) {
      print('Failed to stop WebSocket server: $e');
      return false;
    }
  }

  Future<bool> connectToWebSocketServer(String url) async {
    try {
      _channel = await WebSocketChannel.connect(Uri.parse(url));
      _channel?.stream.listen((message) {
        // Handle messages from the server here
        print('Received message from server: $message');
        _onMessageReceived(message);
      });

      print('Connected to WebSocket server at $url');
      return true;
    } catch (e) {
      print('Failed to connect to WebSocket server: $e');
      return false;
    }
  }

  bool disconnectFromWebSocketServer() {
    if (_channel == null) {
      throw Exception('No active WebSocket connection');
    }

    try {
      _channel?.sink.close();
      _channel = null;
      print('Disconnected from WebSocket server');
      return true;
    } catch (e) {
      print('Failed to disconnect from WebSocket server: $e');
      return false;
    }
  }

  bool sendMessage(String message) {

    // If hosting a server, send message to all clients
    if (_server != null) {
      return sendMessageToClients(message);
    }

    if (_channel == null) {
      throw Exception('No active WebSocket connection');
    }

    try {
      _channel?.sink.add(message);
      print('Sent message: $message');
      return true;
    } catch (e) {
      print('Failed to send message: $e');
      return false;
    }
  }

  bool sendMessageToClients(String message) {
    if (_connectedClients.isEmpty) {
      print('No clients connected');
      return false;
    }

    try {
      for (var client in _connectedClients) {
        client.sink.add(message);
      }
      print('Sent message to all clients: $message');
      return true;
    } catch (e) {
      print('Failed to send message to clients: $e');
      return false;
    }
  }

  void _onMessageReceived(String message) {
    // Notify all listeners that a message has been received.
    onMessageReceived?.trigger(message);
  }

  // Useful boilerplate
  String _localIP = "";
  String get localIP => _localIP;

  Timer refreshTimer() => Timer.periodic(const Duration(seconds: 10), (timer) {
    if (kIsWeb) return;
    _refresh();
  });

  Future<void> _refresh() async {
    print("Refreshing network info...");
    {
      // Update the local IP address

      // First try NetworkInfo
      _localIP = (await NetworkInfo().getWifiIP()) ?? "";

      // If null, try NetworkInterface
      // Only look for ethernet. Wifi would have been found by NetworkInfo
      if (_localIP.isEmpty) {
        for (var interface in await NetworkInterface.list()) {
          if (!interface.name.toLowerCase().contains("eth") || interface.name.contains(" ")) {
            continue;
          }

          for (var addr in interface.addresses) {
            print('Interface ${interface.name} has address ${addr.address}');
            if (addr.type == InternetAddressType.IPv4 && !addr.isLoopback) {
              _localIP = addr.address;
              break;
            }
          }
        }
      }
    }
  }
}
