use termichart_core::{
    Candle, ChartConfig, ChartType, OverlayKind, Point, Range, Result, Style, Viewport,
    XAxisFormat,
};
use termichart_data::{auto_range, auto_range_points, to_heikin_ashi};
use termichart_render::{render_to_string, AxisRenderer, CellGrid, Layout, painter};

use crate::bar::BarChart;
use crate::candlestick::CandlestickChart;
use crate::histogram::Histogram;
use crate::line::LineChart;

/// A fluent builder for constructing and rendering charts.
pub struct ChartBuilder {
    config: ChartConfig,
    points: Vec<Point>,
    candles: Vec<Candle>,
    values: Vec<f64>,
    num_bins: usize,
}

impl ChartBuilder {
    /// Creates a new builder for the given chart type.
    pub fn new(chart_type: ChartType) -> Self {
        Self {
            config: ChartConfig {
                chart_type,
                ..Default::default()
            },
            points: Vec::new(),
            candles: Vec::new(),
            values: Vec::new(),
            num_bins: 10,
        }
    }

    /// Creates a line chart builder.
    pub fn line() -> Self {
        Self::new(ChartType::Line)
    }

    /// Creates a candlestick chart builder.
    pub fn candlestick() -> Self {
        Self::new(ChartType::Candlestick)
    }

    /// Creates a bar chart builder.
    pub fn bar() -> Self {
        Self::new(ChartType::Bar)
    }

    /// Creates a histogram chart builder.
    pub fn histogram() -> Self {
        Self::new(ChartType::Histogram)
    }

    /// Sets the chart dimensions.
    pub fn size(mut self, width: u16, height: u16) -> Self {
        self.config.width = Some(width);
        self.config.height = Some(height);
        self
    }

    /// Sets the chart title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.config.title = Some(title.into());
        self
    }

    /// Adds an overlay indicator.
    pub fn overlay(mut self, kind: OverlayKind) -> Self {
        self.config.overlays.push(kind);
        self
    }

    /// Disables axis rendering.
    pub fn no_axis(mut self) -> Self {
        self.config.show_axis = false;
        self
    }

    /// Disables grid rendering.
    pub fn no_grid(mut self) -> Self {
        self.config.show_grid = false;
        self
    }

    /// Sets x-axis labels to display as `HH:MM:SS` timestamps.
    pub fn x_axis_time(mut self) -> Self {
        self.config.x_axis_format = XAxisFormat::Time;
        self
    }

    /// Enables Heikin-Ashi candle transformation.
    pub fn heikin_ashi(mut self) -> Self {
        self.config.heikin_ashi = true;
        self
    }

    /// Sets the chart theme.
    pub fn theme(mut self, theme: termichart_core::Theme) -> Self {
        self.config.theme = theme;
        self
    }

    /// Adds a single data point (for line charts).
    pub fn add_point(mut self, x: f64, y: f64) -> Self {
        self.points.push(Point { x, y });
        self
    }

    /// Sets all data points at once.
    pub fn points(mut self, points: Vec<Point>) -> Self {
        self.points = points;
        self
    }

    /// Adds a single candle (for candlestick charts).
    pub fn add_candle(mut self, candle: Candle) -> Self {
        self.candles.push(candle);
        self
    }

    /// Sets all candles at once.
    pub fn candles(mut self, candles: Vec<Candle>) -> Self {
        self.candles = candles;
        self
    }

    /// Sets raw f64 values (for histogram charts).
    pub fn values(mut self, values: Vec<f64>) -> Self {
        self.values = values;
        self
    }

    /// Sets the number of bins for histogram charts.
    pub fn num_bins(mut self, n: usize) -> Self {
        self.num_bins = n;
        self
    }

    /// Renders the chart and returns it as an ANSI string.
    pub fn render(&self) -> Result<String> {
        let w = self.config.width.unwrap_or(80);
        let h = self.config.height.unwrap_or(24);

        let layout = Layout::compute(w, h, &self.config);
        let mut grid = CellGrid::new(w, h);
        let chart_area = layout.chart_area;

        match self.config.chart_type {
            ChartType::Line => {
                let (x_range, y_range) = if self.points.is_empty() {
                    (Range { min: 0.0, max: 1.0 }, Range { min: 0.0, max: 1.0 })
                } else {
                    auto_range_points(&self.points)
                };
                let viewport = Viewport {
                    x_range,
                    y_range,
                    width: chart_area.width,
                    height: chart_area.height,
                };

                let chart = LineChart::new(self.points.clone());
                chart.render(&mut grid, &viewport, &self.config, chart_area.x, chart_area.y)?;

                if self.config.show_axis {
                    let axis = AxisRenderer;
                    axis.draw_y_axis(&mut grid, &viewport, &layout.y_axis_area, &self.config.theme);
                    axis.draw_x_axis(&mut grid, &viewport, &layout.x_axis_area, &self.config.theme, self.config.x_axis_format);
                }
            }
            ChartType::Candlestick => {
                let candles = if self.config.heikin_ashi {
                    to_heikin_ashi(&self.candles)
                } else {
                    self.candles.clone()
                };
                let (x_range, y_range) = if candles.is_empty() {
                    (Range { min: 0.0, max: 1.0 }, Range { min: 0.0, max: 1.0 })
                } else {
                    auto_range(&candles)
                };
                let viewport = Viewport {
                    x_range,
                    y_range,
                    width: chart_area.width,
                    height: chart_area.height,
                };

                let mut chart = CandlestickChart::new(candles);
                chart.compute_overlays(&self.config.overlays);
                chart.render(&mut grid, &viewport, &self.config, chart_area.x, chart_area.y)?;

                if self.config.show_axis {
                    let axis = AxisRenderer;
                    axis.draw_y_axis(&mut grid, &viewport, &layout.y_axis_area, &self.config.theme);
                    axis.draw_x_axis(&mut grid, &viewport, &layout.x_axis_area, &self.config.theme, self.config.x_axis_format);
                }
            }
            ChartType::Bar => {
                let (x_range, y_range) = if self.points.is_empty() {
                    (Range { min: 0.0, max: 1.0 }, Range { min: 0.0, max: 1.0 })
                } else {
                    auto_range_points(&self.points)
                };
                let viewport = Viewport {
                    x_range,
                    y_range,
                    width: chart_area.width,
                    height: chart_area.height,
                };

                let chart = BarChart::new(self.points.clone());
                chart.render(&mut grid, &viewport, &self.config, chart_area.x, chart_area.y)?;

                if self.config.show_axis {
                    let axis = AxisRenderer;
                    axis.draw_y_axis(&mut grid, &viewport, &layout.y_axis_area, &self.config.theme);
                    axis.draw_x_axis(&mut grid, &viewport, &layout.x_axis_area, &self.config.theme, self.config.x_axis_format);
                }
            }
            ChartType::Histogram => {
                let histogram = Histogram::new(self.values.clone(), self.num_bins);
                let bins = histogram.compute_bins();

                let (x_range, y_range) = if bins.is_empty() {
                    (Range { min: 0.0, max: 1.0 }, Range { min: 0.0, max: 1.0 })
                } else {
                    let x_min = bins.first().map(|(lo, _, _)| *lo).unwrap_or(0.0);
                    let x_max = bins.last().map(|(_, hi, _)| *hi).unwrap_or(1.0);
                    let max_count = bins.iter().map(|(_, _, c)| *c).max().unwrap_or(1);
                    (
                        Range { min: x_min, max: x_max },
                        Range { min: 0.0, max: max_count as f64 },
                    )
                };
                let viewport = Viewport {
                    x_range,
                    y_range,
                    width: chart_area.width,
                    height: chart_area.height,
                };

                histogram.render(&mut grid, &viewport, &self.config, chart_area.x, chart_area.y)?;

                if self.config.show_axis {
                    let axis = AxisRenderer;
                    axis.draw_y_axis(&mut grid, &viewport, &layout.y_axis_area, &self.config.theme);
                    axis.draw_x_axis(&mut grid, &viewport, &layout.x_axis_area, &self.config.theme, self.config.x_axis_format);
                }
            }
        }

        // Draw title
        if let (Some(title), Some(title_area)) = (&self.config.title, &layout.title_area) {
            let style = Style {
                fg: self.config.theme.text_color(),
                bold: true,
                ..Default::default()
            };
            painter::draw_text(&mut grid, title_area.x, title_area.y, title, style);
        }

        Ok(render_to_string(&grid))
    }

    /// Renders the chart and prints it to stdout.
    pub fn show(&self) -> Result<()> {
        let output = self.render()?;
        print!("{}", output);
        Ok(())
    }
}
