use ::clap::Parser;
use crossbeam_channel::{Sender, bounded};
use crossterm::style::{Color, Stylize};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use log::{LevelFilter, error, info, warn};
use messager::{Message, Messager, Signals};
use std::process;
use std::sync::atomic::Ordering;
use std::time::Duration;
use std::{path::Path, sync::Arc};

mod apperror;
mod cipher;
mod clap;
mod logger;
mod messager;
mod ncmdump;
mod opendir;
mod pathparse;
mod threadpool;
mod time;
use apperror::AppError;
use ncmdump::Ncmfile;
use time::TimeCompare;

fn main() -> Result<(), AppError> {
    // 初始化日志系统
    match logger::init_logger() {
        Ok(_) => (),
        Err(_) => {
            println!("初始化日志系统失败")
        }
    };

    // 全局取消标志
    let cancel = threadpool::cancel_flag();
    {
        let cancel = cancel.clone();
        let mut ctrlc_count = 0u32;
        ctrlc::set_handler(move || {
            ctrlc_count += 1;
            if ctrlc_count == 1 {
                eprintln!("收到中断信号，正在等待当前任务完成... (再次按 Ctrl+C 强制退出)");
                cancel.store(true, Ordering::SeqCst);
            } else {
                eprintln!("强制退出");
                process::exit(1);
            }
        })
        .expect("无法设置 Ctrl+C 信号处理器");
    }

    let timer = match TimeCompare::new() {
        Ok(t) => t,
        Err(e) => {
            error!("无法初始化时间戳系统。{}", e);
            process::exit(1)
        }
    };

    let cli = clap::Cli::parse();

    //设置彩色输出
    if cli.nocolor {
        // 2024 Edition新标准，被标记为不安全函数
        // 在单线程还是安全的
        unsafe {
            std::env::set_var("NO_COLOR", "true");
        }
    };

    //获取cpu核心数
    let cpus = num_cpus::get();
    // 最大线程数
    let max_workers = match cli.workers {
        Some(n) => {
            if n >= 1 {
                n
            } else {
                1
            }
        }
        None => cpus, //默认使用cpu核心数作为线程数
    };
    //输入目录
    let input = cli.input;
    //输出目录
    let outputdir = cli.output;
    // 强制覆盖
    let forcesave = cli.forcesave;
    if forcesave {
        warn!("文件{}已开启！", "强制覆盖".with(Color::Red))
    }
    let level = match cli.debug {
        0 | 3 => LevelFilter::Info,
        1 => LevelFilter::Error,
        2 => LevelFilter::Warn,
        4 => LevelFilter::Debug,
        5 => LevelFilter::Trace,
        _ => LevelFilter::Off,
    };
    info!("日志等级：{}", level);
    log::set_max_level(level);

    let undumpfile = pathparse::pathparse(input);

    let taskcount = undumpfile.len();
    let mut success_count = 0;
    let mut ignore_count = 0;
    let mut failure_count = 0;
    let mut cancelled_count = 0;

    if taskcount == 0 {
        if cli.autoopen {
            opendir::autoopen(cli.autoopen, outputdir);
        } else {
            error!(
                "没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。使用-a参数自动打开输出文件夹。"
            );
        }

        process::exit(2);
    };
    // 创建完整的父目录
    if std::fs::create_dir_all(&outputdir).is_err() {
        return Err(AppError::CannotCreateDir);
    }
    // 初始化线程池
    let pool = threadpool::Pool::new(max_workers);

    info!("将启用{}线程", max_workers.to_string().with(Color::Green));
    // 初始化通讯
    let (tx, rx) = bounded(max_workers * 6);

    // 循环开始
    for filepath in undumpfile {
        let output = outputdir.clone();
        let senderin: Sender<Message> = tx.clone();
        let senderon: Sender<Message> = tx.clone();
        let cancel = cancel.clone();
        // 多线程
        pool.execute(move || match Ncmfile::new(filepath.as_str()) {
            Ok(mut n) => match n.dump(Path::new(&output), senderin, forcesave, cancel) {
                Ok(_) => {}
                Err(e) => {
                    let messager = Messager::new(senderon);
                    let _ = messager.send(Signals::Err(e));
                }
            },
            Err(e) => {
                let messager = Messager::new(senderon);
                let _ = messager.send(Signals::Err(e));
            }
        });
    }

    //进度条
    let pb = ProgressBar::new(taskcount as u64)
        .with_elapsed(Duration::from_millis(50))
        .with_style(
            ProgressStyle::default_bar()
                .progress_chars("#>-")
                .template("{spinner:.green} [{wide_bar:.cyan/blue}] {percent_precise}% ({eta})")
                .unwrap(),
        )
        .with_message("解密中");
    let progressbar = MP.add(pb);

    // 接受消息
    for messages in rx {
        match messages.signal {
            Signals::End => {
                success_count += 1;
                progressbar.inc(1);
            }
            Signals::Err(AppError::ProtectFile) => {
                ignore_count += 1;
                progressbar.inc(1);
            }
            Signals::Err(AppError::Cancelled) => {
                cancelled_count += 1;
                progressbar.inc(1);
            }
            Signals::Err(e) => {
                error!("{}", e);
                failure_count += 1;
                progressbar.inc(1);
            }
            _ => (),
        }
        if (success_count + ignore_count + failure_count + cancelled_count) >= taskcount {
            break;
        }
    }
    progressbar.finish_and_clear();

    let timecount = timer.compare_start().unwrap();
    let showtime = || {
        if timecount > 2000 {
            format!("共计用时{}秒", timecount / 1000)
        } else {
            format!("共计用时{}毫秒", timecount)
        }
    };
    info!(
        "成功解密{}个文件,跳过{}个文件,{}个文件解密失败,{}个文件被取消，{}",
        success_count.to_string().with(Color::Green),
        ignore_count.to_string().with(Color::Magenta),
        failure_count.to_string().with(Color::Red),
        cancelled_count.to_string().with(Color::Yellow),
        showtime()
    );

    // 自动打开输出文件夹
    opendir::autoopen(cli.autoopen, outputdir);
    Ok(())
}

lazy_static! {
    static ref MP: Arc<MultiProgress> = Arc::new(MultiProgress::new());
}
