use termichart_core::{Candle, ChartConfig, Indicator, OverlayKind, Point, Result, Style, Viewport};
use termichart_data::{BollingerBandsIndicator, EmaIndicator, MacdIndicator, RsiIndicator, SmaIndicator, VwapIndicator};
use termichart_render::{BlockCanvas, BrailleCanvas, CellGrid};

/// Renders a candlestick chart using block elements for bodies and box-drawing
/// characters for wicks.
pub struct CandlestickChart {
    candles: Vec<Candle>,
    overlays: Vec<(String, Vec<Point>)>,
}

impl CandlestickChart {
    /// Creates a new candlestick chart from the given candle data.
    pub fn new(candles: Vec<Candle>) -> Self {
        Self {
            candles,
            overlays: Vec::new(),
        }
    }

    /// Computes and adds overlay indicators from the candle data.
    pub fn compute_overlays(&mut self, overlay_kinds: &[OverlayKind]) {
        for kind in overlay_kinds {
            let (name, points): (String, Vec<Point>) = match kind {
                OverlayKind::Sma(period) => {
                    let ind = SmaIndicator::new(*period);
                    (ind.name().to_string(), ind.compute(&self.candles))
                }
                OverlayKind::Ema(period) => {
                    let ind = EmaIndicator::new(*period);
                    (ind.name().to_string(), ind.compute(&self.candles))
                }
                OverlayKind::Vwap => {
                    let ind = VwapIndicator;
                    (ind.name().to_string(), ind.compute(&self.candles))
                }
                OverlayKind::BollingerBands(period, mult_x10) => {
                    let bb = BollingerBandsIndicator::new(*period, *mult_x10 as f64 / 10.0);
                    let (middle, upper, lower) = bb.compute_bands(&self.candles);
                    self.overlays.push((format!("BB Upper"), upper));
                    self.overlays.push((format!("BB Mid"), middle));
                    self.overlays.push((format!("BB Lower"), lower));
                    continue;
                }
                OverlayKind::Rsi(period) => {
                    let rsi = RsiIndicator::new(*period);
                    (rsi.name().to_string(), rsi.compute(&self.candles))
                }
                OverlayKind::Macd(fast, slow, signal) => {
                    let macd = MacdIndicator::new(*fast, *slow, *signal);
                    (macd.name().to_string(), macd.compute(&self.candles))
                }
            };
            self.overlays.push((name, points));
        }
    }

    /// Adds pre-computed overlay points.
    pub fn add_overlay(&mut self, name: String, points: Vec<Point>) {
        self.overlays.push((name, points));
    }

    /// Renders the candlestick chart onto the given CellGrid.
    pub fn render(
        &self,
        grid: &mut CellGrid,
        viewport: &Viewport,
        config: &ChartConfig,
        offset_x: u16,
        offset_y: u16,
    ) -> Result<()> {
        if self.candles.is_empty() {
            return Ok(());
        }

        let block = BlockCanvas::new(viewport.width, viewport.height);

        // Compute column spacing between adjacent candles so we can auto-size
        // body width and leave a gap for the wick to be visible.
        let body_half = if self.candles.len() >= 2 {
            let first_col = viewport.map_col(self.candles[0].time) as i32;
            let second_col = viewport.map_col(self.candles[1].time) as i32;
            let spacing = (second_col - first_col).unsigned_abs() as u16;
            // Body occupies spacing-1 columns (leave 1 col gap), half on each side.
            // Minimum 0 (single-column body), maximum 3 per side.
            let body_width = spacing.saturating_sub(1).min(7);
            body_width / 2
        } else {
            0
        };

        for candle in &self.candles {
            let center = viewport.map_col(candle.time) + offset_x;
            let is_bull = candle.close >= candle.open;
            let color = if is_bull {
                config.theme.bull_color()
            } else {
                config.theme.bear_color()
            };
            let style = Style {
                fg: color,
                ..Default::default()
            };

            // Draw wick (single column at center, high to low)
            let wick_top = viewport.map_row(candle.high) + offset_y;
            let wick_bottom = viewport.map_row(candle.low) + offset_y;
            block.draw_wick(grid, center, wick_top, wick_bottom, style);

            // Draw body (multiple columns centered on the candle position)
            let body_top = viewport.map_row(if is_bull { candle.close } else { candle.open }) + offset_y;
            let body_bottom = viewport.map_row(if is_bull { candle.open } else { candle.close }) + offset_y;

            let left = center.saturating_sub(body_half);
            let right = (center + body_half).min(grid.width.saturating_sub(1));
            for col in left..=right {
                block.draw_candle_body(grid, col, body_top, body_bottom, style);
            }
        }

        // Draw overlays using braille lines
        for (idx, (_name, overlay_pts)) in self.overlays.iter().enumerate() {
            if overlay_pts.len() < 2 {
                continue;
            }
            let mut canvas = BrailleCanvas::new(viewport.width, viewport.height);
            for i in 1..overlay_pts.len() {
                let x0 = viewport.map_x(overlay_pts[i - 1].x);
                let y0 = viewport.map_y(overlay_pts[i - 1].y);
                let x1 = viewport.map_x(overlay_pts[i].x);
                let y1 = viewport.map_y(overlay_pts[i].y);
                canvas.draw_line(
                    (x0 * 2.0) as i32,
                    (y0 * 4.0) as i32,
                    (x1 * 2.0) as i32,
                    (y1 * 4.0) as i32,
                );
            }
            let overlay_style = Style {
                fg: config.theme.overlay_colors(idx),
                ..Default::default()
            };
            canvas.render_to_grid(grid, offset_x, offset_y, overlay_style);
        }

        Ok(())
    }
}
