use crossterm::event::{self, Event as CrosstermEvent, KeyCode, KeyEventKind};
use crossterm::execute;
use crossterm::terminal::{
    disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use fs2::FileExt;
use ratatui::backend::CrosstermBackend;
use ratatui::Terminal;
use rdev::listen;
use std::io::{self, stdout};
use std::path::{Path, PathBuf};
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};

use crate::app::App;
use crate::heatmap::export_heatmap;
use crate::models::{AppEvent, Config, ViewMode};
use crate::storage::Store;
use ratatui::style::Color;

mod app;
mod draw;
mod heatmap;
mod input;
mod models;
mod storage;

/// 解析最终数据目录。
/// 优先级：环境变量 TAPSTAT_DATA_DIR > config.data_dir > 默认 ~/.tapstat
fn resolve_data_dir(config: &Config) -> PathBuf {
    std::env::var("TAPSTAT_DATA_DIR")
        .map(PathBuf::from)
        .ok()
        .or_else(|| config.data_dir.as_ref().map(PathBuf::from))
        .unwrap_or_else(|| Store::default().data_dir().to_path_buf())
}

/// 获取单实例文件锁。进程退出（包括崩溃）时锁自动释放。
fn acquire_singleton_lock(data_dir: &Path) -> io::Result<std::fs::File> {
    std::fs::create_dir_all(data_dir)?;
    let lock_path = data_dir.join(".lock");
    let file = std::fs::OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(false)
        .open(&lock_path)?;
    file.try_lock_exclusive().map_err(|_| {
        io::Error::new(
            io::ErrorKind::AlreadyExists,
            "TapStat 已经在运行中，无法启动多个实例。",
        )
    })?;
    Ok(file)
}

/// Linux 启动前权限检测：确保能读取 /dev/input 下的输入设备。
#[cfg(target_os = "linux")]
fn check_linux_permission() -> io::Result<()> {
    let dev_input = std::path::Path::new("/dev/input");
    if !dev_input.exists() {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "无法访问 /dev/input 目录。请将当前用户加入 input 组后重新登录，或以 root 运行。",
        ));
    }
    let has_readable_event = std::fs::read_dir(dev_input)?.flatten().any(|e| {
        if !e.file_name().to_string_lossy().starts_with("event") {
            return false;
        }
        std::fs::File::open(e.path()).is_ok()
    });
    if !has_readable_event {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            "未在 /dev/input 下发现当前用户可读取的输入设备。请将当前用户加入 input 组后重新登录，或以 root 运行。",
        ));
    }
    Ok(())
}

fn main() -> io::Result<()> {
    // 在进入 TUI 前先在标准屏幕输出权限提示，避免在 Alternate Screen 里看不到。
    // 权限提示已移至 TUI 内部显示，避免启动延迟

    #[cfg(target_os = "linux")]
    if let Err(e) = check_linux_permission() {
        println!("启动失败: {}", e);
        return Ok(());
    }

    // 加载配置 → 解析数据目录 → 单实例锁 → 创建 Store
    let config = Store::default().load_config();
    let data_dir = resolve_data_dir(&config);
    match acquire_singleton_lock(&data_dir) {
        Ok(_lock) => {}
        Err(e) => {
            println!("{}", e);
            return Ok(());
        }
    }
    let store = Store::new(data_dir);
    // 如果数据目录和默认不同，把配置同步过去
    if store.data_dir() != Store::default().data_dir() {
        if let Err(e) = store.save_config(&config) {
            eprintln!("警告: 配置同步失败: {}", e);
        }
    }

    // 进入 raw mode 和 alternate screen，启用 ratatui 的双缓冲渲染。
    enable_raw_mode()?;
    let mut stdout = stdout();
    execute!(stdout, EnterAlternateScreen)?;

    // panic 时确保终端状态恢复，避免用户卡死在 raw mode
    let original_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |info| {
        let _ = disable_raw_mode();
        let _ = execute!(std::io::stdout(), LeaveAlternateScreen);
        original_hook(info);
    }));

    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    // mpsc sync channel 作为所有事件源的统一入口，设置 10000 条缓冲上限防止内存无限增长。
    let (tx, rx) = mpsc::sync_channel::<AppEvent>(10000);

    // 监听线程错误通道，避免 panic 炸掉整个进程
    let (listen_err_tx, listen_err_rx) = mpsc::sync_channel(1);

    // ────────── 全局输入监听线程 ──────────
    {
        let tx = tx.clone();
        thread::spawn(move || {
            if let Err(e) = listen(move |event| {
                let _ = tx.send(AppEvent::Input(event.event_type));
            }) {
                let _ = listen_err_tx.send(format!("{:?}", e));
            }
        });
    }

    // ────────── Tick 心跳线程 ──────────
    {
        let tx = tx.clone();
        thread::spawn(move || loop {
            if tx.send(AppEvent::Tick).is_err() {
                break;
            }
            thread::sleep(Duration::from_millis(100));
        });
    }

    // ────────── 终端按键事件线程 ──────────
    {
        let tx = tx.clone();
        thread::spawn(move || loop {
            if let Ok(CrosstermEvent::Key(key)) = event::read() {
                if key.kind == KeyEventKind::Press && tx.send(AppEvent::Key(key.code)).is_err() {
                    break;
                }
            }
        });
    }

    let mut app = App::new(store, config);
    let res = run_app(&mut terminal, &mut app, rx, listen_err_rx);

    // 恢复终端状态
    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    terminal.show_cursor()?;

    // 退出时再做一次最终保存，确保数据完整。
    let record = app.current_record();
    if let Err(e) = app
        .store
        .save_record_for_date(&record, &app.history.current_date)
    {
        eprintln!("最终保存失败: {}", e);
    } else {
        match std::fs::remove_file(app.store.data_dir().join("today.tmp")) {
            Ok(()) => {}
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
            Err(e) => eprintln!("警告: 删除 today.tmp 失败: {}", e),
        }
    }

    if let Err(err) = res {
        eprintln!("运行错误: {:?}", err);
    }

    app.final_report();
    Ok(())
}

/// 导出指定日期的热图。今天用内存实时数据，历史从磁盘加载。
fn export_for_date(app: &App, date: &str) -> io::Result<PathBuf> {
    if date == app.history.current_date {
        export_heatmap(&app.stats.key_counts, app.stats.total_keys, Some(date))
    } else if let Some(record) = app.store.load_record_for_date(date) {
        export_heatmap(&record.key_counts, record.total_keys, Some(date))
    } else {
        Err(io::Error::new(io::ErrorKind::NotFound, "该日期无记录"))
    }
}

/// 处理热图导出，根据当前视图模式决定数据源。
fn handle_export(app: &App) -> io::Result<PathBuf> {
    match app.ui.view_mode {
        ViewMode::Calendar => {
            let Some(date_str) = app.selected_calendar_date() else {
                return export_heatmap(&app.stats.key_counts, app.stats.total_keys, None);
            };
            export_for_date(app, &date_str)
        }
        ViewMode::Hourly => export_for_date(app, &app.hourly_view_date),
        _ => export_heatmap(&app.stats.key_counts, app.stats.total_keys, None),
    }
}

/// 处理终端按键事件。
fn handle_key_event(app: &mut App, code: KeyCode) {
    match code {
        KeyCode::Char('q') | KeyCode::Esc => app.ui.running = false,
        KeyCode::Char('1') => app.ui.view_mode = ViewMode::Dashboard,
        KeyCode::Char('2') => {
            // 如果在 Calendar 视图且用户明确选中了某天，Hourly 视图加载该日期
            if app.ui.view_mode == ViewMode::Calendar {
                if app.ui.calendar_selected_day.is_some() {
                    if let Some(date) = app.selected_calendar_date() {
                        app.hourly_view_date = date;
                    }
                } else {
                    app.hourly_view_date = app.history.current_date.clone();
                }
            } else if app.ui.view_mode != ViewMode::Hourly {
                // Dashboard / Trend 切换到 Hourly 时重置为今天
                app.hourly_view_date = app.history.current_date.clone();
            }
            // Hourly 视图下按 2 保持当前日期不变（不做任何操作）
            app.ui.view_mode = ViewMode::Hourly;
        }
        KeyCode::Char('3') => app.ui.view_mode = ViewMode::Trend,
        KeyCode::Char('4') => app.ui.view_mode = ViewMode::Calendar,
        KeyCode::Char(',') | KeyCode::Char('<') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.try_shift_calendar_month(-1);
            } else if app.ui.view_mode == ViewMode::Hourly {
                app.shift_hourly_date(-1);
            }
        }
        KeyCode::Char('.') | KeyCode::Char('>') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.try_shift_calendar_month(1);
            } else if app.ui.view_mode == ViewMode::Hourly {
                app.shift_hourly_date(1);
            }
        }
        KeyCode::Char('0') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.ui.calendar_month_offset = 0;
            }
            app.hourly_view_date = app.history.current_date.clone();
        }
        KeyCode::Char('h') | KeyCode::Char('H') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.shift_calendar_selection(-1);
            } else if app.ui.view_mode == ViewMode::Hourly {
                app.shift_hourly_date(-1);
            } else {
                app.ui.show_history = !app.ui.show_history;
            }
        }
        KeyCode::Char('l') | KeyCode::Char('L') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.shift_calendar_selection(1);
            } else if app.ui.view_mode == ViewMode::Hourly {
                app.shift_hourly_date(1);
            }
        }
        KeyCode::Char('k') | KeyCode::Char('K') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.shift_calendar_selection(-7);
            }
        }
        KeyCode::Char('j') | KeyCode::Char('J') => {
            if app.ui.view_mode == ViewMode::Calendar {
                app.shift_calendar_selection(7);
            }
        }
        KeyCode::Char('e') | KeyCode::Char('E') => match handle_export(app) {
            Ok(path) => {
                app.ui.last_message = Some((
                    format!("热图已导出到 {}", path.display()),
                    Instant::now(),
                    Color::DarkGray,
                ));
            }
            Err(e) => {
                app.ui.last_message =
                    Some((format!("导出失败: {}", e), Instant::now(), Color::Red));
            }
        },
        _ => {}
    }
}

/// 主事件循环。从 channel 中接收事件并分发给 App 处理，每处理完一次就重绘一帧。
fn run_app(
    terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
    app: &mut App,
    rx: mpsc::Receiver<AppEvent>,
    listen_err_rx: mpsc::Receiver<String>,
) -> io::Result<()> {
    while app.ui.running {
        // 非阻塞检查输入监听线程是否报错
        if let Ok(msg) = listen_err_rx.try_recv() {
            app.ui.last_message =
                Some((format!("输入监听中断: {}", msg), Instant::now(), Color::Red));
            app.ui.running = false;
            break;
        }
        match rx.recv() {
            Ok(AppEvent::Tick) => app.update_rates(),
            Ok(AppEvent::Input(etype)) => app.on_input(etype),
            Ok(AppEvent::Key(code)) => handle_key_event(app, code),
            Err(_) => app.ui.running = false,
        }
        terminal.draw(|f| crate::draw::draw(app, f))?;
    }
    Ok(())
}
