use termichart_core::{ChartConfig, Point, Result, Style, Viewport};
use termichart_render::CellGrid;

const FULL_BLOCK: char = '\u{2588}';

/// Renders vertical bars from Point data.
///
/// Each point's `x` is the bar index and `y` is the bar height.
pub struct BarChart {
    points: Vec<Point>,
}

impl BarChart {
    pub fn new(points: Vec<Point>) -> Self {
        Self { points }
    }

    pub fn render(
        &self,
        grid: &mut CellGrid,
        viewport: &Viewport,
        config: &ChartConfig,
        offset_x: u16,
        offset_y: u16,
    ) -> Result<()> {
        if self.points.is_empty() {
            return Ok(());
        }

        let n = self.points.len();
        let chart_w = viewport.width as usize;
        let chart_h = viewport.height as usize;

        // Calculate bar width and gap
        let total_space = chart_w;
        let bar_width = ((total_space as f64) / (n as f64 * 1.5)).max(1.0) as usize;
        let gap = (bar_width / 2).max(1);
        let step = bar_width + gap;

        let y_min = viewport.y_range.min;
        let y_span = viewport.y_range.span();

        for (i, point) in self.points.iter().enumerate() {
            let bar_x = i * step;
            if bar_x >= chart_w {
                break;
            }

            // Determine bar height in rows
            let bar_frac = if y_span > 0.0 {
                (point.y - y_min) / y_span
            } else {
                0.5
            };
            let bar_rows = (bar_frac * chart_h as f64).round() as usize;

            // Determine color: positive = bull color, negative = bear color
            let color = if point.y >= 0.0 {
                config.theme.bull_color()
            } else {
                config.theme.bear_color()
            };

            let style = Style {
                fg: color,
                ..Default::default()
            };

            // Draw from bottom up
            for row in 0..bar_rows.min(chart_h) {
                let grid_row = offset_y + (chart_h - 1 - row) as u16;
                for col_offset in 0..bar_width.min(chart_w - bar_x) {
                    let grid_col = offset_x + (bar_x + col_offset) as u16;
                    if grid.in_bounds(grid_col, grid_row) {
                        grid.set(grid_col, grid_row, FULL_BLOCK, style);
                    }
                }
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use termichart_core::{ChartConfig, Range, Viewport};
    use termichart_render::CellGrid;

    #[test]
    fn bar_chart_renders() {
        let points = vec![
            Point { x: 0.0, y: 5.0 },
            Point { x: 1.0, y: 10.0 },
            Point { x: 2.0, y: 3.0 },
        ];
        let chart = BarChart::new(points);
        let mut grid = CellGrid::new(40, 10);
        let viewport = Viewport {
            x_range: Range { min: 0.0, max: 2.0 },
            y_range: Range { min: 0.0, max: 10.0 },
            width: 40,
            height: 10,
        };
        let config = ChartConfig::default();
        assert!(chart.render(&mut grid, &viewport, &config, 0, 0).is_ok());
    }

    #[test]
    fn bar_chart_empty() {
        let chart = BarChart::new(vec![]);
        let mut grid = CellGrid::new(40, 10);
        let viewport = Viewport {
            x_range: Range { min: 0.0, max: 1.0 },
            y_range: Range { min: 0.0, max: 1.0 },
            width: 40,
            height: 10,
        };
        let config = ChartConfig::default();
        assert!(chart.render(&mut grid, &viewport, &config, 0, 0).is_ok());
    }
}
