use std::{
    collections::HashMap,
    f32::consts::PI,
    sync::{Arc, Mutex, MutexGuard},
    thread::{self, Thread},
    time::Duration,
};

use uuid::Uuid;

use crate::{
    Error,
    connection::{listener::ConnectionConfig, packets::Packets},
    layers::build_client,
    networkers::{ClientTrait, Connection, ServerTrait, TCPClient, TCPServer, run_listener_state},
};

pub struct Node {
    id: String,
    connections: HashMap<String, Box<dyn Connection + Send>>,
    map: HashMap<String, Vec<String>>,
}

fn read(c: &mut Box<dyn Connection + Send>) -> Result<Packets, Error> {
    let a = Packets::decode(c.read()?.as_str());
    info!("Data: {:?}", a);
    a
}

fn write(c: &mut Box<dyn Connection + Send>, packet: Packets) -> Result<(), Error> {
    info!("Wrote: {:?}", packet);
    c.write(packet.encode()?.as_str())
}

impl Node {
    pub fn run_node(
        id: String,
        clients: Vec<ConnectionConfig>,
        listeners: Vec<ConnectionConfig>,
    ) -> Result<(), Error> {
        // let mut parent = build_client(TCPClient::connect(&parent.socket)?, parent.layers)?;

        let state = Arc::new(Mutex::new(Self {
            id: id, //Uuid::new_v4().to_string(), //TODO: Calling an OS RNG can pose a problem for security;
            connections: HashMap::new(),
            map: HashMap::new(),
        }));

        for listener in listeners {
            run_listener_state(
                TCPServer::bind(&listener.socket)?,
                listener.layers,
                Self::on_listener_client,
                Arc::clone(&state),
            );
        }

        for client in clients {
            let state = Arc::clone(&state);
            thread::spawn(move || {
                loop {
                    if let Err(e) = Self::run_client(client.clone(), &state) {
                        error!("{}", e);
                    }

                    thread::sleep(Duration::from_millis(1000));
                }
            });
        }

        thread::sleep(Duration::MAX);

        Ok(())
    }

    fn run_client(client: ConnectionConfig, state: &Arc<Mutex<Node>>) -> Result<(), Error> {
        Self::run_connection(
            build_client(TCPClient::connect(&client.socket)?, client.layers)?,
            state,
        );

        Ok(())
    }

    fn on_listener_client(
        connection: Box<dyn Connection + Send + 'static>,
        state: Arc<Mutex<Node>>,
    ) {
        thread::spawn(move || {
            Self::run_connection(connection, &state);
        });
    }

    fn run_connection(connection: Box<dyn Connection + Send + 'static>, state: &Arc<Mutex<Node>>) {
        let mut connection = connection;
        let s = state.lock().unwrap();

        let this_uuid = s.id.clone();
        std::mem::drop(s);

        write(
            &mut connection,
            Packets::UpdateRoutes(this_uuid, (&mut state.lock().unwrap()).get_known_clients()),
        )
        .unwrap();

        let other_uuid = if let Ok(Packets::UpdateRoutes(src, routes)) = read(&mut connection) {
            (&mut state.lock().unwrap()).extend_routes(src.clone(), routes);
            src
        } else {
            error!("Could not get UUID!");
            return;
        };

        // info!("Connection: {}", other_uuid);

        (&mut state.lock().unwrap())
            .connections
            .insert(other_uuid.clone(), connection.try_clone().unwrap());

        // (&mut state.lock().unwrap()).connect(other_uuid.clone(), None);

        // let is_root = s.parent.is_none();

        loop {
            match read(&mut connection) {
                Ok(packet) => {
                    let result: Result<(), Error> = match packet {
                        // Packets::UpdateRoutes(src, routes) => {
                        //     Ok((&mut state.lock().unwrap()).extend_routes(src, routes))
                        // }
                        // Packets::Connect(id) => {
                        //     let source = if other_uuid == id {
                        //         None
                        //     } else {
                        //         Some(other_uuid.clone())
                        //     };

                        //     Ok((&mut state.lock().unwrap()).connect(id, source))
                        // }
                        Packets::Disconnect(id) => {
                            let direct = other_uuid == id;
                            Ok((&mut state.lock().unwrap()).disconnect(id, direct))
                        }
                        Packets::UpdateRoutes(src, routes) => {
                            Ok((&mut state.lock().unwrap()).extend_routes(src, routes))
                        }
                        _ => {
                            error!("Unsupported packet: {:?}", packet);

                            Ok(())
                        }
                    };

                    if let Err(e) = result {
                        error!("Got error: {}", e);
                    }
                }
                Err(e) => {
                    if !connection.is_alive() {
                        warn!("Connection {} Disconnected!", connection.get_info());
                        let state = &mut state.lock().unwrap();
                        state.connections.remove(&other_uuid);
                        state.disconnect(other_uuid, true);

                        break;
                    }

                    error!("Got error: {}", e);
                }
            }
        }
    }

    // fn get_best_route(&self, uuid: &String) -> Result<(usize, String), Error> {
    //     let routes = self.map.get(uuid).ok_or("Route does not exist!")?;

    //     if routes.is_empty() {
    //         return Ok((0, uuid.clone()));
    //     }

    //     let mut min_hops = usize::MAX;
    //     let mut min_uuid = "".to_string();

    //     for (hops, uuid) in routes.iter() {
    //         if hops < &min_hops {
    //             min_hops = hops.clone();
    //             min_uuid = uuid.clone();
    //         }
    //     }

    //     Ok((min_hops, min_uuid))
    // }

    // fn route_get_index_of_uuid(routes: &Vec<(usize, String)>, uuid: &String) -> Option<usize> {
    //     for (i, route) in routes.iter().enumerate() {
    //         if &route.1 == uuid {
    //             return Some(i);
    //         }
    //     }

    //     return None;
    // }

    // fn route_inc_one(routes: Vec<(usize, String)>) -> Vec<(usize, String)> {
    //     routes.iter().map(|r| (r.0 + 1, *r.1)).collect()
    // }

    // fn get_known_routes(&self) -> Vec<(usize, String)> {
    //     let mut routes = self
    //         .map
    //         .keys()
    //         .map(|k| self.get_best_route(k).unwrap())
    //         .collect::<Vec<(usize, String)>>();

    //     routes.push((0, self.id.clone()));

    //     println!("Known routes: {:?}", routes);

    //     routes
    // }

    fn get_known_clients(&self) -> Vec<String> {
        let mut clients = self.map.keys().map(|k| k.clone()).collect::<Vec<String>>();

        clients.push(self.id.clone());

        clients
    }

    fn knows_client(&self, id: &String) -> bool {
        self.get_known_clients().contains(id)
        // self.connections.contains_key(id)
    }

    fn broadcast(&mut self, data: Packets, disclude: Option<&String>) {
        for (uuid, connection) in self.connections.iter_mut() {
            if disclude.is_some() && disclude.unwrap() == uuid {
                continue;
            }
            if let Err(e) = write(connection, data.clone()) {
                error!("Failed to send packet to {}, {}", uuid, e);
            }
        }
    }

    fn broadcast_table(&mut self, disclude: Option<&String>) {
        self.broadcast(
            Packets::UpdateRoutes(self.id.clone(), self.get_known_clients()),
            disclude,
        );
    }

    // fn connect(&mut self, id: String, source: Option<String>) {
    //     if !self.knows_client(&id) && id != self.id {
    //         self.broadcast(Packets::Connect(id.clone()), Some(&id));
    //         if let Some(source) = source {
    //             // Is direct
    //             self.extend_routes(id.clone(), vec![(9999, source.clone())]);
    //             self.broadcast_table(None);
    //         } else {
    //             self.map.insert(id.clone(), Vec::new());
    //             self.broadcast_table(None);
    //         }
    //         self.print_map();
    //     }
    // }

    fn disconnect(&mut self, id: String, direct: bool) {
        if self.knows_client(&id) {
            self.broadcast(Packets::Disconnect(id.clone()), None);

            if direct {
                for (i, uuid) in self.get_known_clients().iter().enumerate() {
                    // if let Some(i) =
                    //     Self::route_get_index_of_uuid(self.map.get(&uuid).unwrap(), &uuid)
                    // {
                    //     self.map.get_mut(&uuid).unwrap().remove(i);

                    //     self.broadcast(Packets::Disconnect(uuid.clone()), Some(&uuid));
                    // }

                    if self.map.contains_key(uuid) {
                        self.map.get_mut(uuid).unwrap().remove(i);

                        self.broadcast(Packets::Disconnect(uuid.clone()), Some(&uuid));
                    }
                }
            }

            self.map.remove(&id);

            // self.broadcast_table(Some(&id));
            self.print_map();
        }
    }

    fn extend_routes(&mut self, src: String, routes: Vec<String>) {
        let mut updated = false;

        println!("{:?}", routes);
        for route in routes {
            if route == self.id {
                continue;
            }

            if self.map.contains_key(&route) {
                // println!("{:?}", route);

                // let update = if let Some(i) =
                //     Self::route_get_index_of_uuid(self.map.get(&route).unwrap(), &route)
                // {
                //     println!(
                //         "contains, {}, {:?} {:?}",
                //         i,
                //         self.map.get(&route.1).unwrap(),
                //         route
                //     );
                //     if self.map.get(&route.1).unwrap()[i].0 != route.0 {
                //         let _ = self.map.get_mut(&route.1).unwrap().remove(i);

                //         println!("true");

                //         true
                //     } else {
                //         println!("false");
                //         false
                //     }
                //     // println!("{:?}", self.map.get(&route.1).unwrap());
                //     // true
                // } else {
                //     true
                // };

                // if update {
                //     self.map
                //         .get_mut(&route.1)
                //         .unwrap()
                //         .push((route.0 + 1, src.clone()));
                //     updated = true;
                // }
                if !self.map.get(&route).unwrap().contains(&route) {
                    self.map.get_mut(&route).unwrap().push(src.clone());
                    updated = true;
                }
            } else {
                self.map.insert(route.clone(), vec![route]);
                updated = true;
            }
        }

        if updated {
            self.broadcast_table(None);
            self.print_map();
        }
    }

    fn print_map(&self) {
        info!("\n\n");
        info!("Local addr: {}", self.id);
        info!("Table: ");
        for (uuid, route) in self.map.iter() {
            info!("{} -> [ {:?} ]", uuid, route);
        }
    }
}
