use color_eyre::owo_colors::OwoColorize;
use spvirit_scry::common::stats::IfStats;
use spvirit_scry::utils::common_version_print::version_header;
use spvirit_scry::utils::influxdb_utils::InfluxDB;
use spvirit_scry::utils::loki_utils::LokiLogger;
use spvirit_scry::utils::packet_processor::process_packets;

use argparse::{ArgumentParser, Store, StoreFalse, StoreTrue};
use std::sync::{Arc, Mutex};
use std::thread;
// use colored::Colorize;
use colored::*;
use tracing::{debug, error, info, Level};

// toml parsing
use serde_derive::Deserialize;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut verbose = true;
    let mut show_payload = false;
    let mut device = String::from("bond10gbe");
    let mut integration_period = 1;
    let mut tui_mode = false;
    let mut debug_mode = false;
    let mut resolve = false;
    let mut buffer_size: usize = 5000;
    let mut influxdb_config_file = String::from("influxdb_config.toml");
    let mut version = false;

    #[derive(Deserialize, Debug)]
    struct Config {
        url: String,
        bucket: String,
        org: String,
        token: String,
        loki_url: String,
    }

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Packet capture tool");
        ap.refer(&mut verbose)
            .add_option(&["-q", "--quiet"], StoreFalse, "Be quiet");
        ap.refer(&mut device)
            .add_option(&["-i", "--device"], Store, "Device to capture from");
        ap.refer(&mut integration_period).add_option(
            &["-t", "--integration-time"],
            Store,
            "Integration period in seconds",
        );
        ap.refer(&mut show_payload).add_option(
            &["-p", "--show-payload"],
            StoreTrue,
            "Show packet payload",
        );
        ap.refer(&mut tui_mode)
            .add_option(&["-t", "--tui"], StoreTrue, "Show TUI");
        ap.refer(&mut debug_mode)
            .add_option(&["-d", "--debug"], StoreTrue, "Show debug messages");
        ap.refer(&mut resolve)
            .add_option(&["-r", "--resolve"], StoreTrue, "Resolve IP addresses");
        ap.refer(&mut buffer_size).add_option(
            &["-b", "--buffer-size"],
            Store,
            "Buffer size for packet capture 100 - 50000",
        );

        ap.refer(&mut influxdb_config_file).add_option(
            &["-c", "--config-file"],
            Store,
            "InfluxDB config file",
        );
        ap.refer(&mut version)
            .add_option(&["-v", "--version"], StoreTrue, "Show version and exit");

        ap.parse_args_or_exit();
    }
    if version {
        version_header();
        std::process::exit(0);
    }

    let if_stats = Arc::new(Mutex::new(IfStats::new()));
    if resolve {
        let mut if_stats = if_stats.lock().unwrap();
        if_stats.enable_resolve();
    }
    if !(100..=50_000).contains(&buffer_size) {
        eprintln!("Buffer size must be between 100 and 50000");
        std::process::exit(1);
    } else {
        let mut if_stats = if_stats.lock().unwrap();
        if_stats.set_buffer_size(buffer_size);
    }

    let config: Config = toml::from_str(&std::fs::read_to_string(influxdb_config_file)?)?;

    let loki_enabled = false;

    let loki_logger = LokiLogger::new(config.loki_url);

    let influxdb_client: InfluxDB =
        InfluxDB::new(config.url, config.bucket, config.org, config.token);

    let influxdb_ping = influxdb_client.ping();
    let loki_ping = loki_logger.ping();

    if verbose {
        if debug_mode {
            tracing_subscriber::fmt::fmt()
                .with_max_level(Level::DEBUG)
                .init();
        } else {
            tracing_subscriber::fmt::fmt()
                .with_max_level(Level::INFO)
                .init();
        }
        if !tui_mode {
            info!("Starting {} v{} ({}) [{}]",
                env!("CARGO_PKG_NAME"),
                env!("VERGEN_GIT_DESCRIBE"),
                env!("VERGEN_GIT_BRANCH"),
                env!("VERGEN_GIT_SHA"),
            );
            info!("{}", format!("Using device {}", device).green());
        }
        info!("InfluxDB ping: {:?}", influxdb_ping.await.unwrap());
        info!(
            "InfluxDB bucket check: {:?}",
            influxdb_client.check_bucket_exists().await.unwrap()
        );
    }

    let mut handles_vec = vec![];

    let mut if_stats_clone = Arc::clone(&if_stats);
    debug!("Starting packet processing thread");
    let handle_scan = thread::spawn(move || {
        process_packets(
            verbose,
            show_payload,
            &device,
            integration_period,
            &mut if_stats_clone,
            Vec::new(),
            300,    // pva_ttl_secs: 5 minutes default
            40_000, // pva_max_channels: 40k default
            false,  // tracked_only: disabled by default in collector
            10_000, // pva_max_update_rate: 10k default
            None,
        );
    });
    handles_vec.push(handle_scan);
    debug!("Packet processing thread started");

    // if tui_mode {
    //     debug!("Starting TUI thread");
    //     let mut if_stats_clone_tui = Arc::clone(&if_stats);
    //     let handle_tui = thread::spawn(move || {
    //         let _res = main_funcs(&mut if_stats_clone_tui);
    //     });
    //     handles_vec.push(handle_tui);
    //     debug!("TUI thread started");
    // }

    // if verbose && !tui_mode {
    loop {
        let data = {
            let stats = if_stats.lock().unwrap();
            stats.to_line_protocol().concat()
        };
        let loki_logs = {
            let stats = if_stats.lock().unwrap();
            stats.to_loki_stream()
        };
        influxdb_client.write(data).await.unwrap();
        loki_logger
            .log(loki_logs)
            .await
            .unwrap_or_else(|e| error!("Failed to send logs to Loki: {}", e));
        {
            let mut stats = if_stats.lock().unwrap();
            stats.clear_events();
            stats.rotate_stats();
        }
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;

        if !show_payload {
            let if_stats = if_stats.lock().unwrap();
            let rate = if_stats.data_rate;
            let packet_rate = if_stats.packet_rate;
            let _timestamp = if_stats.timestamp.elapsed().as_secs();

            let str_rate = if rate > 1_000_000.0 {
                format!("{:.2} Mb/s", rate / 1_000_000.0)
            } else if rate > 1_000.0 {
                format!("{:.2} Kb/s", rate / 1_000.0)
            } else {
                format!("{:.2} b/s", rate)
            };
            let str_packet_rate = format!("{:.2} pps", packet_rate);
            info!(
                "{}",
                format!("Data rate: {}, Packet rate: {}", str_rate, str_packet_rate).green()
            );

            for (k, v) in if_stats.source_ips.iter() {
                info!("\tsource_ip: {} count: {}", k, v);
            }
            for (k, v) in if_stats.destination_ips.iter() {
                info!("\tdestination_ip: {} count: {}", k, v);
            }
            for (k, v) in if_stats.request_types.iter() {
                info!("\trequest_type:\t {:?} count: {:?}", k, v);
            }
        }
    }
    // }

    for handle in handles_vec {
        handle.join().unwrap();
        debug!("Thread closed");
    }
    debug!("All threads closed");

    Ok(())
}
