use crate::color::color_to_pdf_rgb;
use crate::layout::{border_side, LayoutRun, LayoutTable};
use std::collections::HashMap;
use std::io::Write;

use super::{ImageResource, write_f32, write_pdf};

pub fn render_table_item(
    page_height: f32,
    table: &LayoutTable,
    glyph_cache: &HashMap<String, HashMap<char, u16>>,
    custom_font_map: &HashMap<String, String>,
    image_resources: &[ImageResource],
) -> Vec<u8> {
    // Pre-estimate capacity: ~80 bytes per cell fill + ~50 bytes per border + ~100 bytes per run
    let cell_count = table.rows.iter().map(|r| r.cells.len()).sum::<usize>();
    let run_count = table.rows.iter().flat_map(|r| r.cells.iter()).map(|c| c.runs.len()).sum::<usize>();
    let mut stream = Vec::with_capacity(cell_count * 80 + table.borders.len() * 50 + run_count * 128);
    for row in &table.rows {
        for cell in &row.cells {
            if let Some(fc) = &cell.fill_color {
                if let Some((r, g, b)) = color_to_pdf_rgb(fc) {
                    let cy = page_height - cell.y - cell.height;
                    stream.extend_from_slice(b"q ");
                    if let Some(op) = cell.fill_opacity {
                        if op < 1.0 {
                            let pct = (op * 100.0).round() as u8;
                            write_pdf!(stream, "/gs{} gs ", pct);
                        }
                    }
                    write_f32(&mut stream, r, 3); stream.push(b' ');
                    write_f32(&mut stream, g, 3); stream.push(b' ');
                    write_f32(&mut stream, b, 3); stream.extend_from_slice(b" rg ");
                    write_f32(&mut stream, cell.x, 2); stream.push(b' ');
                    write_f32(&mut stream, cy, 2); stream.push(b' ');
                    write_f32(&mut stream, cell.width, 2); stream.push(b' ');
                    write_f32(&mut stream, cell.height, 2); stream.extend_from_slice(b" re f Q\n");
                }
            }
            // Render overlay pattern
            if let Some(ref pattern) = cell.overlay_pattern {
                let opacity = cell.overlay_opacity.unwrap_or(0.3);
                let cy = page_height - cell.y - cell.height;
                stream.extend_from_slice(b"q ");
                // Apply opacity via ExtGState
                if opacity < 1.0 {
                    let pct = (opacity * 100.0).round() as u8;
                    write_pdf!(stream, "/gs{} gs ", pct);
                }
                // Clip to cell
                write_f32(&mut stream, cell.x, 2); stream.push(b' ');
                write_f32(&mut stream, cy, 2); stream.push(b' ');
                write_f32(&mut stream, cell.width, 2); stream.push(b' ');
                write_f32(&mut stream, cell.height, 2); stream.extend_from_slice(b" re W n\n");
                // Set pattern color (gray)
                write_f32(&mut stream, 0.0, 3); stream.extend_from_slice(b" g\n");
                match pattern.as_str() {
                    "diagonal" => {
                        // Diagonal lines
                        let step = 8.0f32;
                        let mut x = 0.0f32;
                        while x < cell.width + cell.height {
                            let x1 = cell.x + x.min(cell.width);
                            let y1 = cy + (x - x.min(cell.width)).max(0.0);
                            let x2 = cell.x + (x - cell.height).max(0.0);
                            let y2 = cy + (x - (x - cell.height).max(0.0)).min(cell.height);
                            write_f32(&mut stream, 0.5, 2); stream.extend_from_slice(b" w ");
                            write_f32(&mut stream, x1, 2); stream.push(b' ');
                            write_f32(&mut stream, y1, 2); stream.extend_from_slice(b" m ");
                            write_f32(&mut stream, x2, 2); stream.push(b' ');
                            write_f32(&mut stream, y2, 2); stream.extend_from_slice(b" l S\n");
                            x += step;
                        }
                    }
                    "dots" => {
                        // Dot pattern
                        let step = 10.0f32;
                        let dot_r = 1.5f32;
                        let mut x = 0.0f32;
                        while x < cell.width {
                            let mut y = 0.0f32;
                            while y < cell.height {
                                let cx = cell.x + x + dot_r;
                                let cy_dot = cy + y + dot_r;
                                write_pdf!(stream, "{:.2} {:.2} m {:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c {:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c {:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c {:.2} {:.2} {:.2} {:.2} {:.2} {:.2} c f\n",
                                    cx + dot_r, cy_dot,
                                    cx + dot_r, cy_dot + dot_r * 0.552, cx + dot_r * 0.552, cy_dot + dot_r, cx, cy_dot + dot_r,
                                    cx - dot_r * 0.552, cy_dot + dot_r, cx - dot_r, cy_dot + dot_r * 0.552, cx - dot_r, cy_dot,
                                    cx - dot_r, cy_dot - dot_r * 0.552, cx - dot_r * 0.552, cy_dot - dot_r, cx, cy_dot - dot_r,
                                    cx + dot_r * 0.552, cy_dot - dot_r, cx + dot_r, cy_dot - dot_r * 0.552, cx + dot_r, cy_dot
                                );
                                y += step;
                            }
                            x += step;
                        }
                    }
                    _ => {} // Unknown pattern, skip
                }
                stream.extend_from_slice(b"Q\n");
            }
            // border_colors order: [left, top, right, bottom]
            // border_side() returns: 0=top, 1=right, 2=bottom, 3=left
            // Remap from border_side to border_colors index
            const SIDE_TO_COLOR: [usize; 4] = [1, 2, 3, 0];
            for border in &cell.borders {
                // Skip default black borders on edges that have colored counterparts from adjacent cells
                let edge_key = crate::layout::edge_key(border);
                if cell.border_colors.is_none() && table.colored_edges.contains(&edge_key) {
                    continue;
                }
                if let Some(ref colors) = cell.border_colors {
                    let color = if let Some(side) = border_side(border, cell.x, cell.y, cell.width, cell.height) {
                        colors.get(SIDE_TO_COLOR[side]).or(colors.first())
                    } else {
                        colors.first()
                    };
                    if let Some(c) = color {
                        if let Some((r, g, b)) = color_to_pdf_rgb(c) {
                            write_f32(&mut stream, r, 3); stream.push(b' ');
                            write_f32(&mut stream, g, 3); stream.push(b' ');
                            write_f32(&mut stream, b, 3); stream.extend_from_slice(b" RG\n");
                        }
                    }
                } else if let Some(ref config) = table.layout_config {
                    let is_horizontal = (border.y1 - border.y2).abs() < 0.01;
                    let color = if is_horizontal { config.h_line_color.as_ref() } else { config.v_line_color.as_ref() };
                    if let Some(c) = color {
                        if let Some((r, g, b)) = color_to_pdf_rgb(c) {
                            write_f32(&mut stream, r, 3); stream.push(b' ');
                            write_f32(&mut stream, g, 3); stream.push(b' ');
                            write_f32(&mut stream, b, 3); stream.extend_from_slice(b" RG\n");
                        }
                    }
                }
                let y1 = page_height - border.y1;
                let y2 = page_height - border.y2;
                let line_width = if let Some(ref config) = table.layout_config {
                    let is_horizontal = (border.y1 - border.y2).abs() < 0.01;
                    if is_horizontal { config.h_line_width.unwrap_or(0.5) } else { config.v_line_width.unwrap_or(0.5) }
                } else {
                    0.5
                };
                write_f32(&mut stream, line_width, 2); stream.extend_from_slice(b" w ");
                write_f32(&mut stream, border.x1, 2); stream.push(b' ');
                write_f32(&mut stream, y1, 2); stream.extend_from_slice(b" m ");
                write_f32(&mut stream, border.x2, 2); stream.push(b' ');
                write_f32(&mut stream, y2, 2); stream.extend_from_slice(b" l S\n");
                if cell.border_colors.is_some() || table.layout_config.as_ref().and_then(|c| c.h_line_color.as_ref().or(c.v_line_color.as_ref())).is_some() {
                    stream.extend_from_slice(b"0.000 0.000 0.000 RG\n");
                }
            }
        }
    }
    for row in &table.rows {
        for cell in &row.cells {
            // Group runs by y_offset to identify lines
            let mut line_groups: Vec<(f32, Vec<&LayoutRun>)> = Vec::new();
            for run in &cell.runs {
                if let Some(last) = line_groups.last_mut() {
                    if (last.0 - run.y_offset).abs() < 0.01 {
                        last.1.push(run);
                        continue;
                    }
                }
                line_groups.push((run.y_offset, vec![run]));
            }

            // Render each line group with justify spacing
            for (_y_offset, line_runs) in &line_groups {
                let is_line_justify = line_runs.iter().any(|r| r.is_justify);
                let mut justify_reset: Option<bool> = None; // Some(is_cjk)
                if is_line_justify {
                    let total_text: String = line_runs.iter().map(|r| r.text.as_str()).collect();
                    let total_width: f32 = line_runs.iter().map(|r| r.width).sum();
                    let available_width = line_runs.iter().find(|r| r.is_justify).map(|r| r.available_width).unwrap_or(total_width);
                    let extra_space = available_width - total_width;
                    if extra_space > 0.0 {
                        let has_cjk = total_text.chars().any(|c| crate::font::needs_cjk_font(c));
                        if has_cjk {
                            let char_count = total_text.chars().filter(|c| !c.is_whitespace()).count();
                            if char_count > 1 {
                                let cs = extra_space / (char_count - 1) as f32;
                                write_f32(&mut stream, cs, 4);
                                stream.extend_from_slice(b" Tc\n");
                                justify_reset = Some(true);
                            }
                        } else {
                            let word_count = total_text.split_whitespace().count();
                            if word_count > 1 {
                                let ws = extra_space / (word_count - 1) as f32;
                                write_f32(&mut stream, ws, 4);
                                stream.extend_from_slice(b" Tw\n");
                                justify_reset = Some(false);
                            }
                        }
                    }
                }

                for run in line_runs {
                    let y = page_height - run.y_offset - run.font_size;
                    super::text::render_run_into(&mut stream, run, run.x_offset, y, glyph_cache, custom_font_map);
                    super::text::draw_decoration_at_into(&mut stream, run, run.x_offset, y);
                }

                if let Some(is_cjk) = justify_reset {
                    if is_cjk {
                        stream.extend_from_slice(b"0.00 Tc\n");
                    } else {
                        stream.extend_from_slice(b"0.00 Tw\n");
                    }
                }
            }

            for item in &cell.items {
                stream.extend_from_slice(&super::render_page_stream_item(page_height, item, glyph_cache, custom_font_map, image_resources));
            }
        }
    }
    stream
}
