use crate::protobuf::gou::{MessageType, Starpoles};
use crate::utils::cxx_string_to_string;
use ::log::{debug, error, info, trace, warn};
use bytes::Bytes;
use cxx::CxxString;
use encoding_rs::Encoding;
use futures_util::SinkExt;
use prost::Message as WebMessage;
use spin::Mutex;
use std::ffi::CString;
use std::sync::OnceLock;
use std::sync::mpsc;
use tokio::net::TcpStream;
use tokio::runtime::Runtime;
use tokio_tungstenite::tungstenite::Message;

static LOGGER_SENDER: OnceLock<mpsc::Sender<Starpoles>> = OnceLock::new();

mod config;
mod log;
mod login;
mod network;
pub mod protobuf;
pub mod utils;

#[cxx::bridge]
mod ffi {
    extern "Rust" {
        fn download_file(url: &CxxString, path: &CxxString) -> Result<()>;

        fn http_get(url: &CxxString) -> Result<String>;

        /// 尝试将未知编码的字符串转换为UTF-8
        fn to_utf8(string: &CxxString) -> String;

        /// UTF-16LE转换为UTF-8
        fn unicode_to_utf_8(string: &CxxString) -> String;

        /// 猜测编码
        fn guess_encoding(string: &CxxString) -> String;

        fn init_log(is_debug: bool, ws_uel: &CxxString);

        fn log_trace(msg: &CxxString);

        fn log_debug(msg: &CxxString);

        fn log_info(msg: &CxxString);

        fn log_warning(msg: &CxxString);

        fn log_error(msg: &CxxString);
    }
}

fn unicode_to_utf_8(string: &CxxString) -> String {
    // 约定：传入的内容按 UTF-16LE 字节序解释
    let mut bytes = string.as_bytes();

    // UTF-16 必须是偶数长度；若是奇数长度，丢弃最后 1 个字节避免越界/误解码
    if (bytes.len() & 1) == 1 {
        bytes = &bytes[..bytes.len() - 1];
    }

    // 用 encoding_rs 直接把 UTF-16LE 转成 Rust 的 UTF-8 String
    let (cow, _actual_used, _had_errors) = encoding_rs::UTF_16LE.decode(bytes);
    cow.into_owned()
}

fn guess_encoding(string: &CxxString) -> String {
    utils::guess_encoding_label(string)
}

fn to_utf8(string: &CxxString) -> String {
    cxx_string_to_string(string).to_string()
}

fn push_log(t: MessageType, msg: String) {
    if let Some(lock) = LOGGER_SENDER.get() {
        let _ = lock.send(Starpoles {
            r#type: t.into(),
            message: msg.clone(),
        });
    }
}

fn log_error(msg: &cxx::CxxString) {
    let msg = cxx_string_to_string(msg);
    push_log(MessageType::Error, msg.clone());
    error!("{}", msg);
}

fn log_warning(msg: &cxx::CxxString) {
    let msg = cxx_string_to_string(msg);
    push_log(MessageType::Warning, msg.clone());
    warn!("{}", msg);
}

fn log_info(msg: &cxx::CxxString) {
    let msg = cxx_string_to_string(msg);
    push_log(MessageType::Info, msg.clone());
    info!("{}", msg);
}

fn log_debug(msg: &cxx::CxxString) {
    let msg = cxx_string_to_string(msg);
    push_log(MessageType::Debug, msg.clone());
    debug!("{}", msg);
}

fn log_trace(msg: &cxx::CxxString) {
    let msg = cxx_string_to_string(msg);
    push_log(MessageType::Trace, msg.clone());
    trace!("{}", msg);
}

fn init_log(is_debug: bool, ws_uel: &CxxString) {
    let url = cxx_string_to_string(ws_uel);
    let (tx, rx) = mpsc::channel::<Starpoles>();
    LOGGER_SENDER.set(tx).expect("已经初始化过了");
    std::thread::spawn(move || {
        let rt = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();

        rt.block_on(async move {
            loop {
                match tokio_tungstenite::connect_async(url.clone()).await {
                    Ok((mut stream, _resp)) => {
                        while let Ok(line) = rx.recv() {
                            let mut buffer = vec![];
                            //  Vec总是具备足够的空间
                            line.encode(&mut buffer).unwrap();
                            if stream
                                .send(Message::Binary(Bytes::from(buffer)))
                                .await
                                .is_err()
                            {
                                break;
                            }
                        }
                    }
                    Err(_) => {
                        // 先丢弃管道内数据
                        while let Ok(_) = rx.try_recv() {}
                        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
                    }
                }
            }
        });
    });

    log::init_log(is_debug);
}

fn get_runtime() -> Runtime {
    Runtime::new().expect("创建Tokio运行时失败")
}

fn download_file(
    url: &cxx::CxxString,
    path: &cxx::CxxString,
) -> Result<(), Box<dyn std::error::Error>> {
    let rt = get_runtime();
    let url = cxx_string_to_string(url);
    let path = cxx_string_to_string(path);
    rt.block_on(network::download_file(&url, &path))?;
    Ok(())
}

fn http_get(url: &cxx::CxxString) -> Result<String, Box<dyn std::error::Error>> {
    let rt = get_runtime();
    let url = cxx_string_to_string(url);
    rt.block_on(network::http_get(&url))
}
