use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span, Text};
use ratatui::widgets::{Bar, BarChart, BarGroup, Block, Borders, Gauge, Paragraph};
use ratatui::Frame;
use std::time::Duration;

use chrono::Datelike;

use crate::app::{sorted_top, App};
use crate::models::ViewMode;

pub fn draw(app: &App, f: &mut Frame) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(1)
        .constraints([
            Constraint::Length(3),
            Constraint::Min(10),
            Constraint::Length(1),
        ])
        .split(f.size());

    let title = Paragraph::new("TapStat - 实时输入分析")
        .style(
            Style::default()
                .fg(Color::Cyan)
                .add_modifier(Modifier::BOLD),
        )
        .alignment(ratatui::layout::Alignment::Center)
        .block(
            Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(Color::Cyan)),
        );
    f.render_widget(title, chunks[0]);

    match app.ui.view_mode {
        ViewMode::Dashboard => {
            let main_chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([
                    Constraint::Percentage(25),
                    Constraint::Percentage(25),
                    Constraint::Percentage(50),
                ])
                .split(chunks[1]);
            draw_summary(app, f, main_chunks[0]);
            draw_rates(app, f, main_chunks[1]);
            draw_rankings(app, f, main_chunks[2]);
        }
        ViewMode::Hourly => {
            let main_chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
                .split(chunks[1]);
            draw_summary(app, f, main_chunks[0]);
            draw_hourly_chart(app, f, main_chunks[1]);
        }
        ViewMode::Trend => {
            let main_chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
                .split(chunks[1]);
            draw_summary(app, f, main_chunks[0]);
            draw_trend_chart(app, f, main_chunks[1]);
        }
        ViewMode::Calendar => {
            let main_chunks = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
                .split(chunks[1]);
            draw_summary(app, f, main_chunks[0]);
            draw_calendar(app, f, main_chunks[1]);
        }
    }

    let (hint_text, hint_style) = if let Some((msg, _, color)) = &app.ui.last_message {
        (msg.clone(), Style::default().fg(*color))
    } else if app.ui.start_time.elapsed() < Duration::from_secs(4) {
        (
            "提示: macOS 需在「系统设置→隐私与安全性→辅助功能」中授权；Linux 需加入 input 组"
                .into(),
            Style::default().fg(Color::Yellow),
        )
    } else if !app.ui.has_received_input
        && app.ui.start_time.elapsed()
            > Duration::from_secs(app.config.permission_warning_delay_secs)
    {
        (
            "⚠️ 未检测到输入事件，请检查系统辅助功能权限（macOS）或运行权限（Linux）".into(),
            Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
        )
    } else {
        let hint = match app.ui.view_mode {
            ViewMode::Hourly => {
                "[1]Dash [2]24h [3]7d [4]Month [H/L]Day [0]Today [E]Exp [Q]Quit".to_string()
            }
            ViewMode::Trend => "[1]Dash [2]24h [3]7d [4]Month [0]Today [E]Exp [Q]Quit".to_string(),
            _ => {
                let mode_hint = if app.ui.show_history {
                    "历史"
                } else {
                    "今日"
                };
                format!(
                    "[1]Dash [2]24h [3]7d [4]Month [< >]Month [0]Today [H]{} [E]Exp [Q]Quit",
                    mode_hint
                )
            }
        };
        (hint, Style::default().fg(Color::DarkGray))
    };
    let hint = Paragraph::new(hint_text)
        .alignment(ratatui::layout::Alignment::Center)
        .style(hint_style);
    f.render_widget(hint, chunks[2]);
}

/// 绘制左侧汇总面板：运行时长、今日/历史按键点击数、纠错率。
fn draw_summary(app: &App, f: &mut Frame, area: Rect) {
    let summary_text = Text::from(vec![
        Line::from(vec![Span::styled(
            "本次运行",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.ui.elapsed_str(),
            Style::default()
                .fg(Color::White)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(""),
        Line::from(vec![Span::styled(
            "今日按键",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.stats.total_keys.to_string(),
            Style::default()
                .fg(Color::Green)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(vec![Span::styled(
            "今日点击",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.stats.total_mouse.to_string(),
            Style::default()
                .fg(Color::Yellow)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(""),
        Line::from(vec![Span::styled(
            "纠错率",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.stats.correction_rate_str(),
            Style::default()
                .fg(app.stats.correction_rate_color())
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(""),
        Line::from(vec![Span::styled(
            "历史按键",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.history.all_time_keys(&app.stats).to_string(),
            Style::default()
                .fg(Color::LightGreen)
                .add_modifier(Modifier::BOLD),
        )]),
        Line::from(vec![Span::styled(
            "历史点击",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            app.history.all_time_mouse(&app.stats).to_string(),
            Style::default()
                .fg(Color::LightYellow)
                .add_modifier(Modifier::BOLD),
        )]),
    ]);
    let summary =
        Paragraph::new(summary_text).block(Block::default().borders(Borders::ALL).title("汇总"));
    f.render_widget(summary, area);
}

/// 绘制中间速率面板：KPS/CPS Gauge 条 + 今日峰值。
fn draw_rates(app: &App, f: &mut Frame, area: Rect) {
    let kps = app.stats.key_window.len() as f64;
    let cps = app.stats.mouse_window.len() as f64;
    let kps_ratio = if app.stats.today_peak_kps(&app.history.today_base) > 0.0 {
        (kps / app.stats.today_peak_kps(&app.history.today_base)).min(1.0)
    } else {
        0.0
    };
    let cps_ratio = if app.stats.today_peak_cps(&app.history.today_base) > 0.0 {
        (cps / app.stats.today_peak_cps(&app.history.today_base)).min(1.0)
    } else {
        0.0
    };

    let rate_chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(4),
            Constraint::Length(4),
            Constraint::Length(4),
            Constraint::Length(4),
            Constraint::Min(0),
        ])
        .split(area);

    let kps_gauge = Gauge::default()
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(format!("KPS {:.1}", kps)),
        )
        .gauge_style(Style::default().fg(Color::Green))
        .ratio(kps_ratio);
    f.render_widget(kps_gauge, rate_chunks[0]);

    let cps_gauge = Gauge::default()
        .block(
            Block::default()
                .borders(Borders::ALL)
                .title(format!("CPS {:.1}", cps)),
        )
        .gauge_style(Style::default().fg(Color::Yellow))
        .ratio(cps_ratio);
    f.render_widget(cps_gauge, rate_chunks[1]);

    let peak_kps_text = Paragraph::new(Text::from(vec![
        Line::from(vec![Span::styled(
            "今日峰值 KPS",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            format!("{:.1}", app.stats.today_peak_kps(&app.history.today_base)),
            Style::default()
                .fg(Color::White)
                .add_modifier(Modifier::BOLD),
        )]),
    ]))
    .block(Block::default().borders(Borders::ALL));
    f.render_widget(peak_kps_text, rate_chunks[2]);

    let peak_cps_text = Paragraph::new(Text::from(vec![
        Line::from(vec![Span::styled(
            "今日峰值 CPS",
            Style::default().fg(Color::Gray),
        )]),
        Line::from(vec![Span::styled(
            format!("{:.1}", app.stats.today_peak_cps(&app.history.today_base)),
            Style::default()
                .fg(Color::White)
                .add_modifier(Modifier::BOLD),
        )]),
    ]))
    .block(Block::default().borders(Borders::ALL));
    f.render_widget(peak_cps_text, rate_chunks[3]);
}

/// 绘制右侧排行面板：键盘 Top 10 条形图、组合键 Top 8、鼠标排行。
fn draw_rankings(app: &App, f: &mut Frame, area: Rect) {
    let right_chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage(52),
            Constraint::Percentage(28),
            Constraint::Percentage(20),
        ])
        .split(area);

    if app.ui.show_history {
        let merged = app.history.merged_key_counts(&app.stats);
        draw_key_bars(f, right_chunks[0], &merged, "键盘 Top 10（历史）");
    } else {
        draw_key_bars(
            f,
            right_chunks[0],
            &app.stats.key_counts,
            "键盘 Top 10（今日）",
        );
    }

    draw_combo_list(f, right_chunks[1], &app.input.combo_counts);

    if app.ui.show_history {
        let merged = app.history.merged_mouse_counts(&app.stats);
        draw_mouse_list(f, right_chunks[2], &merged, "鼠标排行（历史）");
    } else {
        draw_mouse_list(
            f,
            right_chunks[2],
            &app.stats.mouse_counts,
            "鼠标排行（今日）",
        );
    }
}

/// 绘制横向条形图展示按键 Top 排行。
fn draw_key_bars(
    f: &mut Frame,
    area: Rect,
    counts: &std::collections::HashMap<String, u64>,
    title: &str,
) {
    let top = sorted_top(counts, 10);
    let max_key = top.first().map_or(1, |(_, v)| **v).max(1);
    let bars: Vec<Bar> = top
        .iter()
        .map(|(name, count)| {
            Bar::default()
                .value(**count)
                .label(Line::from(name.as_str()))
                .style(Style::default().fg(Color::Green))
                .text_value(format!("{}", count))
        })
        .collect();
    let chart = BarChart::default()
        .block(Block::default().borders(Borders::ALL).title(title))
        .bar_width(1)
        .bar_gap(1)
        .direction(Direction::Horizontal)
        .data(BarGroup::default().bars(&bars))
        .max(max_key);
    f.render_widget(chart, area);
}

/// 绘制组合键排行列表。
fn draw_combo_list(f: &mut Frame, area: Rect, counts: &std::collections::HashMap<String, u64>) {
    let top = sorted_top(counts, 8);
    let lines: Vec<Line> = top
        .iter()
        .map(|(name, count)| {
            Line::from(vec![
                Span::styled(format!("{:16}", name), Style::default().fg(Color::Magenta)),
                Span::raw(" "),
                Span::styled(
                    count.to_string(),
                    Style::default()
                        .fg(Color::White)
                        .add_modifier(Modifier::BOLD),
                ),
            ])
        })
        .collect();
    let widget = Paragraph::new(Text::from(lines))
        .block(Block::default().borders(Borders::ALL).title("组合键 Top 8"));
    f.render_widget(widget, area);
}

/// 绘制鼠标按钮排行列表。
fn draw_mouse_list(
    f: &mut Frame,
    area: Rect,
    counts: &std::collections::HashMap<String, u64>,
    title: &str,
) {
    let top = sorted_top(counts, 10);
    let lines: Vec<Line> = top
        .iter()
        .map(|(name, count)| {
            Line::from(vec![
                Span::styled(format!("{:10}", name), Style::default().fg(Color::Yellow)),
                Span::raw(" "),
                Span::styled(
                    count.to_string(),
                    Style::default()
                        .fg(Color::White)
                        .add_modifier(Modifier::BOLD),
                ),
            ])
        })
        .collect();
    let widget = Paragraph::new(Text::from(lines))
        .block(Block::default().borders(Borders::ALL).title(title));
    f.render_widget(widget, area);
}

/// 将步长向上取整到美观的刻度值（1/2/5 × 10^n，round=true 分界）。
fn nice_step(step: f64) -> f64 {
    let magnitude = 10f64.powf(step.log10().floor());
    let normalized = step / magnitude;
    let ceil = match normalized {
        n if n <= 1.5 => 1.0,
        n if n <= 3.0 => 2.0,
        n if n <= 7.0 => 5.0,
        _ => 10.0,
    };
    ceil * magnitude
}

/// 根据最大值和固定 4 段间隔，计算美观的 Y 轴上限。
fn nice_max(max_val: u64) -> u64 {
    if max_val == 0 {
        return 1;
    }
    let ticks = 4;
    let raw_step = max_val as f64 / ticks as f64;
    let step = nice_step(raw_step);
    let mut chart_max = step * ticks as f64;
    while chart_max < max_val as f64 {
        chart_max += step;
    }
    chart_max.ceil() as u64
}

/// 绘制 Y 轴刻度：大刻度数字（DarkGray）+ 小刻度短线（White）。
fn draw_y_axis(f: &mut Frame, y_axis_area: Rect, chart_area: Rect, chart_max: u64) {
    let bar_area_h = chart_area.height.saturating_sub(3);
    let ticks = if bar_area_h > 8 {
        4
    } else if bar_area_h > 4 {
        2
    } else {
        1
    };
    let tick_px = bar_area_h.saturating_sub(1);
    for i in 0..=ticks {
        let val = (chart_max * i as u64) / ticks as u64;
        let y_offset = (tick_px * i as u16) / ticks as u16;
        let y = chart_area.y + 1 + tick_px - y_offset;
        let text = format!("{}", val);
        let text_len = text.len() as u16;
        let x_start = y_axis_area.x + y_axis_area.width.saturating_sub(text_len + 1);
        for (j, c) in text.chars().enumerate() {
            let x = x_start + j as u16;
            if x >= y_axis_area.x && x < y_axis_area.x + y_axis_area.width {
                let cell = f.buffer_mut().get_mut(x, y);
                cell.set_char(c);
                cell.set_style(Style::default().fg(Color::DarkGray));
            }
        }
    }
    if ticks >= 2 {
        for i in 0..ticks {
            let y_curr = chart_area.y + 1 + tick_px - (tick_px * i as u16) / ticks as u16;
            let y_next = chart_area.y + 1 + tick_px - (tick_px * (i + 1) as u16) / ticks as u16;
            let y_small = (y_curr + y_next) / 2;
            if y_small != y_curr
                && y_small != y_next
                && y_small > chart_area.y
                && y_small < chart_area.y + chart_area.height - 1
            {
                let x = y_axis_area.x + y_axis_area.width.saturating_sub(1);
                if x >= y_axis_area.x && x < y_axis_area.x + y_axis_area.width {
                    let cell = f.buffer_mut().get_mut(x, y_small);
                    cell.set_char('─');
                    cell.set_style(Style::default().fg(Color::White));
                }
            }
        }
    }
}

/// 绘制 24 小时按键分布条形图，左侧带 Y 轴刻度。
fn draw_hourly_chart(app: &App, f: &mut Frame, area: Rect) {
    let hourly = app.hourly_data();
    let max_val = hourly.hours.iter().map(|h| h.total_keys).max().unwrap_or(0);
    let chart_max = nice_max(max_val);

    let chunks = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([Constraint::Length(6), Constraint::Min(0)])
        .split(area);
    let y_axis_area = chunks[0];
    let chart_area = chunks[1];

    if chart_area.height < 8 || chart_area.width < 30 {
        let hint = Paragraph::new("终端空间不足，请拉大窗口")
            .style(Style::default().fg(Color::Yellow))
            .alignment(ratatui::layout::Alignment::Center);
        f.render_widget(hint, chart_area);
        return;
    }

    // 根据面板宽度动态计算柱子宽度，确保两位小时标签不被截断
    let inner = chart_area.width.saturating_sub(2);
    let bar_width = (inner.saturating_sub(23)) / 24;
    let bar_width = bar_width.max(1);
    let use_two_digit = bar_width >= 2;

    draw_y_axis(f, y_axis_area, chart_area, chart_max);

    let bars: Vec<Bar> = hourly
        .hours
        .iter()
        .enumerate()
        .map(|(i, h)| {
            let label_color = match i {
                0..=5 => Color::DarkGray,
                6..=11 => Color::Green,
                12..=17 => Color::Yellow,
                18..=23 => Color::Blue,
                _ => Color::White,
            };
            let label_text = if use_two_digit {
                format!("{:02}", i)
            } else {
                format!("{}", i)
            };
            Bar::default()
                .value(h.total_keys)
                .label(Line::from(vec![Span::styled(
                    label_text,
                    Style::default().fg(label_color),
                )]))
                .style(Style::default().fg(Color::Cyan))
                .text_value("".to_string())
        })
        .collect();
    let title = if app.hourly_view_date == app.history.current_date {
        format!("今日 24 小时按键分布 (当前: {})", app.history.current_date)
    } else {
        format!("{} 24 小时按键分布", app.hourly_view_date)
    };
    let chart = BarChart::default()
        .block(Block::default().borders(Borders::ALL).title(title))
        .bar_width(bar_width)
        .bar_gap(1)
        .bar_style(Style::default().fg(Color::Cyan))
        .data(BarGroup::default().bars(&bars))
        .max(chart_max);
    f.render_widget(chart, chart_area);
}

/// 绘制最近 N 天按键趋势条形图，左侧带 Y 轴刻度。
fn draw_trend_chart(app: &App, f: &mut Frame, area: Rect) {
    let cached_days = &app.history.current_trend().days;

    // 如果今天不在趋势缓存中（今天刚开始运行还没触发自动保存），
    // 注入今天的实时数据，并保持窗口大小。
    let mut days = cached_days.clone();
    let has_today = days.iter().any(|(d, _)| d == &app.history.current_date);
    if !has_today {
        let today_record = crate::models::Record {
            total_keys: app.stats.total_keys,
            total_mouse: app.stats.total_mouse,
            peak_kps: app.stats.today_peak_kps(&app.history.today_base),
            peak_cps: app.stats.today_peak_cps(&app.history.today_base),
            key_counts: app.stats.key_counts.clone(),
            mouse_counts: app.stats.mouse_counts.clone(),
            backspace_count: app.stats.backspace_count,
            delete_count: app.stats.delete_count,
        };
        days.push((app.history.current_date.clone(), today_record));
        days.sort_by(|a, b| a.0.cmp(&b.0));
        let window = app.history.trend_window as usize;
        if days.len() > window {
            days.remove(0);
        }
    }

    let max_val = days
        .iter()
        .map(|(date, r)| {
            if *date == app.history.current_date {
                app.stats.total_keys
            } else {
                r.total_keys
            }
        })
        .max()
        .unwrap_or(0);
    let chart_max = nice_max(max_val);

    let chunks = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([Constraint::Length(6), Constraint::Min(0)])
        .split(area);
    let y_axis_area = chunks[0];
    let chart_area = chunks[1];

    if chart_area.height < 8 || chart_area.width < 15 {
        let hint = Paragraph::new("终端空间不足，请拉大窗口")
            .style(Style::default().fg(Color::Yellow))
            .alignment(ratatui::layout::Alignment::Center);
        f.render_widget(hint, chart_area);
        return;
    }

    // 根据面板宽度和天数动态计算柱子宽度
    let day_count = days.len() as u16;
    let inner = chart_area.width.saturating_sub(2);
    let bar_width = if day_count > 0 {
        (inner.saturating_sub(day_count.saturating_sub(1))) / day_count
    } else {
        1
    };
    let bar_width = bar_width.max(1);
    let max_digits = chart_max.to_string().len() as u16;
    let show_values = bar_width >= max_digits;

    let bars: Vec<Bar> = days
        .iter()
        .map(|(date, r)| {
            let label = date.strip_prefix("20").unwrap_or(date);
            let short = label.split_once('-').map(|x| x.1).unwrap_or(label);
            let val = if *date == app.history.current_date {
                app.stats.total_keys
            } else {
                r.total_keys
            };
            let text = if show_values {
                let s = val.to_string();
                if s.len() <= bar_width as usize {
                    s
                } else {
                    String::new()
                }
            } else {
                String::new()
            };
            Bar::default()
                .value(val)
                .label(Line::from(short))
                .style(Style::default().fg(Color::Cyan))
                .text_value(text)
        })
        .collect();
    let title = format!(
        "最近 {} 天按键趋势 (共 {} 天)",
        app.history.trend_window,
        days.len()
    );

    draw_y_axis(f, y_axis_area, chart_area, chart_max);

    let chart = BarChart::default()
        .block(Block::default().borders(Borders::ALL).title(title))
        .bar_width(bar_width)
        .bar_gap(1)
        .bar_style(Style::default().fg(Color::Cyan))
        .data(BarGroup::default().bars(&bars))
        .max(chart_max);
    f.render_widget(chart, chart_area);
}

/// 绘制日历热力图：7 列网格，颜色编码按键量。
fn draw_calendar(app: &App, f: &mut Frame, area: Rect) {
    let today = match chrono::NaiveDate::parse_from_str(&app.history.current_date, "%Y-%m-%d") {
        Ok(d) => d,
        Err(_) => return,
    };

    // 根据 calendar_month_offset 计算目标月份
    let offset = app.ui.calendar_month_offset;
    let total_months = today.year() * 12 + (today.month() as i32 - 1) + offset;
    let target_year = total_months / 12;
    let target_month = (total_months % 12).unsigned_abs() + 1;

    let month_start =
        chrono::NaiveDate::from_ymd_opt(target_year, target_month, 1).unwrap_or(today);
    let month_end = if target_month == 12 {
        chrono::NaiveDate::from_ymd_opt(target_year + 1, 1, 1)
    } else {
        chrono::NaiveDate::from_ymd_opt(target_year, target_month + 1, 1)
    }
    .map(|d| d - chrono::Duration::days(1))
    .unwrap_or(today);

    let today_keys = app.stats.total_keys;
    let month_records = app.store.load_month_records(target_year, target_month);
    let month_max = month_records
        .iter()
        .map(|(_, k)| *k)
        .chain(std::iter::once(today_keys))
        .max()
        .unwrap_or(1)
        .max(1);

    let mut data_map = std::collections::HashMap::new();
    for (date, keys) in month_records.iter() {
        data_map.insert(date.as_str(), *keys);
    }
    data_map.insert(app.history.current_date.as_str(), today_keys);

    // 补齐到完整周：上月周日 ~ 下月周六
    let start_offset = month_start.weekday().num_days_from_sunday() as i64;
    let calendar_start = month_start - chrono::Duration::days(start_offset);
    let end_offset = month_end.weekday().num_days_from_sunday() as i64;
    let calendar_end = month_end + chrono::Duration::days(6 - end_offset);

    let total_days = (calendar_end - calendar_start).num_days() + 1;
    let weeks = (total_days as f64 / 7.0).ceil() as usize;

    let title = format!("{}", month_start.format("%B %Y"));

    let main_block = Block::default().borders(Borders::ALL).title(title);
    let inner = main_block.inner(area);
    f.render_widget(main_block, area);

    if inner.width < 21 || inner.height < 2 {
        let hint = Paragraph::new("终端空间不足，请拉大窗口")
            .style(Style::default().fg(Color::Yellow))
            .alignment(ratatui::layout::Alignment::Center);
        f.render_widget(hint, inner);
        return;
    }

    let col_width = inner.width / 7;
    let header_height = 1u16;
    let grid_height = inner.height.saturating_sub(header_height);
    let row_height = if weeks > 0 {
        (grid_height as usize / weeks).max(1) as u16
    } else {
        1
    };

    if row_height < 3 {
        let hint = Paragraph::new("终端高度不足，请拉大窗口")
            .style(Style::default().fg(Color::Yellow))
            .alignment(ratatui::layout::Alignment::Center);
        f.render_widget(hint, inner);
        return;
    }

    let weekday_names = ["日", "一", "二", "三", "四", "五", "六"];
    let col_constraints: Vec<Constraint> = (0..7).map(|_| Constraint::Length(col_width)).collect();
    let cols = Layout::default()
        .direction(Direction::Horizontal)
        .constraints(col_constraints)
        .split(inner);

    for (col_idx, &col_area) in cols.iter().enumerate() {
        let header_color = match col_idx {
            0 => Color::Red,    // 周日
            6 => Color::Yellow, // 周六
            _ => Color::Gray,
        };
        let header_para = Paragraph::new(weekday_names[col_idx])
            .style(Style::default().fg(header_color))
            .alignment(ratatui::layout::Alignment::Center);
        let header_rect = Rect {
            x: col_area.x,
            y: col_area.y,
            width: col_area.width,
            height: header_height,
        };
        f.render_widget(header_para, header_rect);

        for week_idx in 0..weeks {
            let cell_y = col_area.y + header_height + (week_idx as u16 * row_height);
            if cell_y + row_height > col_area.y + col_area.height {
                break;
            }
            let cell_area = Rect {
                x: col_area.x,
                y: cell_y,
                width: col_area.width,
                height: row_height,
            };

            let day_offset = (week_idx * 7 + col_idx) as i64;
            let cell_date = calendar_start + chrono::Duration::days(day_offset);
            let in_month = cell_date >= month_start && cell_date <= month_end;

            let date_str = cell_date.format("%Y-%m-%d").to_string();
            let val = data_map.get(date_str.as_str()).copied().unwrap_or(0);

            let is_today = date_str == app.history.current_date;
            let selected_day = app.ui.calendar_selected_day.unwrap_or_else(|| {
                if app.ui.calendar_month_offset == 0 {
                    today.day()
                } else {
                    1
                }
            });
            let is_selected = in_month && cell_date.day() == selected_day;

            let content_color = if is_today {
                Color::White
            } else if !in_month {
                Color::DarkGray
            } else if val == 0 {
                Color::Gray
            } else {
                let ratio = val as f64 / month_max as f64;
                if ratio <= 0.33 {
                    Color::Green
                } else if ratio <= 0.67 {
                    Color::Yellow
                } else {
                    Color::Red
                }
            };

            let border_color = if is_selected {
                Color::Magenta
            } else if is_today {
                Color::White
            } else {
                content_color
            };

            let day_num = cell_date.day();

            let cell_block = Block::default()
                .borders(Borders::ALL)
                .border_style(Style::default().fg(border_color))
                .style(Style::default().bg(if is_today {
                    Color::DarkGray
                } else {
                    Color::Black
                }));
            let cell_inner = cell_block.inner(cell_area);
            f.render_widget(cell_block, cell_area);

            if cell_inner.height == 0 || cell_inner.width == 0 {
                continue;
            }

            let day_str = format!("{}", day_num);
            let num_str = if in_month {
                if val >= 1000 {
                    format!("{:.1}k", val as f64 / 1000.0)
                } else {
                    val.to_string()
                }
            } else {
                String::new()
            };

            if cell_inner.height == 1 {
                // 单行：今天优先显示合并内容，其他只显示日期
                let combined = if is_today && !num_str.is_empty() {
                    format!("{} {}", day_str, num_str)
                } else {
                    day_str.clone()
                };
                let display = if combined.len() <= cell_inner.width as usize {
                    combined
                } else {
                    day_str
                };
                for (i, c) in display.chars().enumerate() {
                    let x = cell_inner.x + i as u16;
                    if x >= cell_inner.x + cell_inner.width {
                        break;
                    }
                    let cell = f.buffer_mut().get_mut(x, cell_inner.y);
                    cell.set_char(c);
                    cell.set_style(Style::default().fg(content_color));
                }
            } else {
                // 多行：日期左上
                for (i, c) in day_str.chars().enumerate() {
                    let x = cell_inner.x + i as u16;
                    if x >= cell_inner.x + cell_inner.width {
                        break;
                    }
                    let cell = f.buffer_mut().get_mut(x, cell_inner.y);
                    cell.set_char(c);
                    cell.set_style(Style::default().fg(content_color));
                }
                // 数值右下（当月）
                if !num_str.is_empty() {
                    let y = cell_inner.y + cell_inner.height - 1;
                    let start_x =
                        cell_inner.x + cell_inner.width.saturating_sub(num_str.len() as u16);
                    for (i, c) in num_str.chars().enumerate() {
                        let x = start_x + i as u16;
                        if x >= cell_inner.x + cell_inner.width {
                            break;
                        }
                        let cell = f.buffer_mut().get_mut(x, y);
                        cell.set_char(c);
                        cell.set_style(Style::default().fg(content_color));
                    }
                }
            }
        }
    }
}
