use std::sync::OnceLock;

use bevy::{prelude::*, tasks::IoTaskPool};
use bevy_egui::egui::Align2;
use bevy_egui::EguiContexts;
use bevy_egui::{egui, EguiClipboard};
use libp2p::{relay, swarm::SwarmEvent, PeerId};

use crate::{
    menu::{MenuAssets, MenuData},
    networking::{BehaviourEvent, NetworkEvent, NetworkManager},
    AppState,
};

pub struct RoomsPlugin;

impl Plugin for RoomsPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(OnEnter(AppState::Rooms), setup_rooms)
            .add_systems(Update, rooms.run_if(in_state(AppState::Rooms)))
            .add_systems(OnExit(AppState::Rooms), cleanup_rooms);
    }
}

fn cleanup_rooms(mut commands: Commands, menu_data: Res<MenuData>) {
    commands
        .entity(menu_data.logo_bw_entity)
        .despawn_recursive();
    commands.remove_resource::<MenuAssets>();
}

#[derive(Resource, Default)]
struct ChatBuffer {
    buffer: String,
    history: String,
}

fn rooms(
    mut next_state: ResMut<NextState<AppState>>,
    mut text_buffer: ResMut<ChatBuffer>,
    net_manager: Res<NetworkManager>,
    mut clipboard_ctx: ResMut<EguiClipboard>,
    mut contexts: EguiContexts,
) {
    egui::Window::new("Chat room")
        .anchor(Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .movable(false)
        .show(contexts.ctx_mut(), |ui| {
            egui::ScrollArea::vertical().show(ui, |ui| {
                ui.label(&text_buffer.history);
            });
            ui.text_edit_singleline(&mut text_buffer.buffer);
            if ui.button("Send").clicked() {
                if text_buffer.buffer.starts_with("/dial") {
                    let address: PeerId = text_buffer
                        .buffer
                        .split(' ')
                        .nth(1)
                        .unwrap()
                        .parse()
                        .unwrap();

                    net_manager
                        .command_sender
                        .send(crate::networking::NetworkCommand::Dial(address))
                        .unwrap();
                } else {
                    let mut buffer = String::new();
                    core::mem::swap(&mut buffer, &mut text_buffer.buffer);
                    net_manager
                        .command_sender
                        .send(crate::networking::NetworkCommand::Publish(
                            crate::networking::Message::Chat {
                                content: std::borrow::Cow::Owned(buffer),
                            },
                        ))
                        .unwrap();
                }
                text_buffer.buffer.clear();
            }
            if ui.button("Copy Peer ID").clicked() {
                clipboard_ctx.set_contents(net_manager.peer_id.get().unwrap().as_str());
            }
        });

    if let Ok(event) = net_manager.events.try_recv() {
        match event {
            NetworkEvent::Event(event) => match event {
                SwarmEvent::NewListenAddr { address, .. } => {
                    tracing::info!(%address, "Listening on address");
                }
                SwarmEvent::Behaviour(BehaviourEvent::RelayClient(
                    relay::client::Event::ReservationReqAccepted { .. },
                )) => {
                    // assert!(opts.mode == Mode::Listen);
                    tracing::info!("Relay accepted our reservation request");
                }
                SwarmEvent::Behaviour(BehaviourEvent::RelayClient(event)) => {
                    tracing::info!(?event)
                }
                SwarmEvent::Behaviour(BehaviourEvent::Dcutr(event)) => {
                    tracing::info!(?event)
                }
                SwarmEvent::Behaviour(BehaviourEvent::Identify(event)) => {
                    tracing::info!(?event)
                }
                SwarmEvent::Behaviour(BehaviourEvent::Ping(_)) => {}
                SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => {
                    tracing::info!(peer=?peer_id, "Outgoing connection failed: {error}");
                }
                _ => {}
            },
            NetworkEvent::LocalPeerID(id) => {
                std::fmt::Write::write_fmt(
                    &mut text_buffer.history,
                    format_args!("Peer ID: {}\n", id),
                )
                .unwrap();
                let mut peer_id = String::new();
                std::fmt::Write::write_fmt(&mut peer_id, format_args!("{}", id)).unwrap();
                net_manager.peer_id.set(peer_id).unwrap();
            }
            NetworkEvent::Message(p, m) => {
                std::fmt::Write::write_fmt(
                    &mut text_buffer.history,
                    format_args!("{:#?}\n", (p, m)),
                )
                .unwrap();
            }
        }
    }
}

fn setup_rooms(mut commands: Commands) {
    let task_pool = IoTaskPool::get();
    let (command_tx, command_rx) = flume::unbounded::<super::networking::NetworkCommand>();
    let (event_tx, event_rx) = flume::unbounded::<super::networking::NetworkEvent>();
    task_pool
        .spawn(super::networking::open_network(command_rx, event_tx))
        .detach();
    commands.insert_resource(NetworkManager {
        events: event_rx,
        command_sender: command_tx,
        peer_id: OnceLock::new(),
    });
    commands.insert_resource(ChatBuffer::default());
}
