use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;

use crate::security::path_guard::sanitize_filename;
use crate::core::shared::parse_hex_color;



pub struct PdfDocument {
    name: String,
    pages: Vec<PdfPage>,
    fill_color: String,
    stroke_color: String,
}

struct PdfPage {}

impl PdfDocument {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            pages: Vec::new(),
            fill_color: "#000000".to_string(),
            stroke_color: "#000000".to_string(),
        }
    }

    pub fn add_page(&mut self, width: f32, height: f32) {
        let _ = (width, height);
        self.pages.push(PdfPage {
        });
    }

    pub fn set_fill_color(&mut self, color: &str) {
        self.fill_color = color.to_string();
    }

    pub fn set_stroke_color(&mut self, color: &str) {
        self.stroke_color = color.to_string();
    }

    pub fn set_line_width(&mut self, _width: f32) {}

    pub fn draw_rect(&mut self, _x: f32, _y: f32, _w: f32, _h: f32, _fill: bool, _stroke: bool) {}

    pub fn draw_ellipse(&mut self, _cx: f32, _cy: f32, _rx: f32, _ry: f32, _fill: bool, _stroke: bool) {}

    pub fn draw_line(&mut self, _x1: f32, _y1: f32, _x2: f32, _y2: f32) {}

    pub fn draw_path(&mut self, _points: &[(f32, f32)]) {}

    pub fn draw_text(&mut self, _text: &str, _x: f32, _y: f32, _font_size: f32) {}

    pub fn draw_image(&mut self, _data: &[u8], _x: f32, _y: f32, _w: f32, _h: f32) {}

    pub fn add_metadata(&mut self, _title: &str, _date: &str) {}

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut output = Vec::new();
        output.extend_from_slice(b"%PDF-1.4\n");
        output.extend_from_slice(format!("% {}\n", self.name).as_bytes());
        output.extend_from_slice(b"%%EOF\n");
        output
    }
}

#[derive(Debug, Clone)]
pub struct ExportBounds {
    pub min_x: f64,
    pub min_y: f64,
    pub max_x: f64,
    pub max_y: f64,
    pub width: f64,
    pub height: f64,
}

#[derive(Debug, Clone)]
pub enum ExportError {
    InvalidFormat(String),
    RenderError(String),
    RenderFailed(String),
    IoError(String),
    EmptyCanvas,
    InvalidDimensions,
}

impl std::fmt::Display for ExportError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidFormat(s) => write!(f, "Invalid format: {s}"),
            Self::RenderError(s) => write!(f, "Render error: {s}"),
            Self::RenderFailed(s) => write!(f, "Render failed: {s}"),
            Self::IoError(s) => write!(f, "IO error: {s}"),
            Self::EmptyCanvas => write!(f, "Empty canvas"),
            Self::InvalidDimensions => write!(f, "Invalid dimensions"),
        }
    }
}

impl std::error::Error for ExportError {}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ExportFormat {
    Png,
    Pdf,
    Svg,
    Json,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportOptions {
    pub format: ExportFormat,
    pub width: u32,
    pub height: u32,
    pub background_color: Option<String>,
    pub include_grid: bool,
    pub scale: f32,
    pub padding: u32,
    pub quality: u8,
    pub include_metadata: bool,
    pub selected_shapes_only: bool,
    pub selected_shape_ids: Vec<Uuid>,
}

impl Default for ExportOptions {
    fn default() -> Self {
        Self {
            format: ExportFormat::Png,
            width: 1920,
            height: 1080,
            background_color: Some("#ffffff".to_string()),
            include_grid: false,
            scale: 1.0,
            padding: 20,
            quality: 90,
            include_metadata: false,
            selected_shapes_only: false,
            selected_shape_ids: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportResult {
    pub id: Uuid,
    pub whiteboard_id: Uuid,
    pub format: ExportFormat,
    pub file_name: String,
    pub file_size: u64,
    pub content_type: String,
    pub data: Vec<u8>,
    pub created_at: DateTime<Utc>,
    pub created_by: Uuid,
    pub metadata: ExportMetadata,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportMetadata {
    pub whiteboard_name: String,
    pub shape_count: u32,
    pub export_dimensions: (u32, u32),
    pub original_dimensions: (u32, u32),
    pub exported_by: String,
    pub export_time: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhiteboardShape {
    pub id: Uuid,
    pub shape_type: ShapeType,
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
    pub rotation: f64,
    pub fill_color: Option<String>,
    pub stroke_color: Option<String>,
    pub stroke_width: f32,
    pub opacity: f32,
    pub points: Vec<Point>,
    pub text: Option<String>,
    pub font_size: Option<f32>,
    pub font_family: Option<String>,
    pub z_index: i32,
    pub locked: bool,
    pub image_data: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ShapeType {
    Rectangle,
    Ellipse,
    Line,
    Arrow,
    Freehand,
    Text,
    Image,
    Sticky,
    Connector,
    Triangle,
    Diamond,
    Star,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WhiteboardData {
    pub id: Uuid,
    pub name: String,
    pub width: u32,
    pub height: u32,
    pub background_color: String,
    pub shapes: Vec<WhiteboardShape>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

pub struct WhiteboardExportService {
    export_history: Arc<RwLock<HashMap<Uuid, Vec<ExportResult>>>>,
}

impl WhiteboardExportService {
    pub fn new() -> Self {
        Self {
            export_history: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub async fn export(
        &self,
        whiteboard: &WhiteboardData,
        options: ExportOptions,
        user_id: Uuid,
        user_name: &str,
    ) -> Result<ExportResult, ExportError> {
        let shapes = if options.selected_shapes_only {
            whiteboard
                .shapes
                .iter()
                .filter(|s| options.selected_shape_ids.contains(&s.id))
                .cloned()
                .collect()
        } else {
            whiteboard.shapes.clone()
        };

        let bounds = self.calculate_bounds(&shapes, &options);

        let (data, content_type, extension) = match options.format {
            ExportFormat::Png => {
                let png_data = self.render_to_png(&shapes, &bounds, &options)?;
                (png_data, "image/png".to_string(), "png")
            }
            ExportFormat::Pdf => {
                let pdf_data = self.render_to_pdf(&shapes, &bounds, &options, whiteboard)?;
                (pdf_data, "application/pdf".to_string(), "pdf")
            }
            ExportFormat::Svg => {
                let svg_data = self.render_to_svg(&shapes, &bounds, &options)?;
                (svg_data.into_bytes(), "image/svg+xml".to_string(), "svg")
            }
            ExportFormat::Json => {
                let json_data = self.export_to_json(whiteboard, &shapes)?;
                (json_data.into_bytes(), "application/json".to_string(), "json")
            }
        };

        let file_name = format!(
            "{}_{}.{}",
            sanitize_filename(&whiteboard.name),
            Utc::now().format("%Y%m%d_%H%M%S"),
            extension
        );

        let result = ExportResult {
            id: Uuid::new_v4(),
            whiteboard_id: whiteboard.id,
            format: options.format.clone(),
            file_name,
            file_size: data.len() as u64,
            content_type,
            data,
            created_at: Utc::now(),
            created_by: user_id,
            metadata: ExportMetadata {
                whiteboard_name: whiteboard.name.clone(),
                shape_count: shapes.len() as u32,
                export_dimensions: (bounds.width as u32, bounds.height as u32),
                original_dimensions: (whiteboard.width, whiteboard.height),
                exported_by: user_name.to_string(),
                export_time: Utc::now(),
            },
        };

        let mut history = self.export_history.write().await;
        history
            .entry(whiteboard.id)
            .or_default()
            .push(result.clone());

        Ok(result)
    }

    fn calculate_bounds(&self, shapes: &[WhiteboardShape], options: &ExportOptions) -> ExportBounds {
        if shapes.is_empty() {
            return ExportBounds {
                min_x: 0.0,
                min_y: 0.0,
                max_x: options.width as f64,
                max_y: options.height as f64,
                width: options.width as f64,
                height: options.height as f64,
            };
        }

        let mut min_x = f64::MAX;
        let mut min_y = f64::MAX;
        let mut max_x = f64::MIN;
        let mut max_y = f64::MIN;

        for shape in shapes {
            let shape_bounds = self.get_shape_bounds(shape);
            min_x = min_x.min(shape_bounds.0);
            min_y = min_y.min(shape_bounds.1);
            max_x = max_x.max(shape_bounds.2);
            max_y = max_y.max(shape_bounds.3);
        }

        let padding = options.padding as f64;
        min_x -= padding;
        min_y -= padding;
        max_x += padding;
        max_y += padding;

        let width = (max_x - min_x) * options.scale as f64;
        let height = (max_y - min_y) * options.scale as f64;

        ExportBounds {
            min_x,
            min_y,
            max_x,
            max_y,
            width,
            height,
        }
    }

    fn get_shape_bounds(&self, shape: &WhiteboardShape) -> (f64, f64, f64, f64) {
        match shape.shape_type {
            ShapeType::Freehand | ShapeType::Line | ShapeType::Arrow => {
                if shape.points.is_empty() {
                    return (shape.x, shape.y, shape.x + shape.width, shape.y + shape.height);
                }
                let min_x = shape.points.iter().map(|p| p.x).fold(f64::MAX, f64::min);
                let min_y = shape.points.iter().map(|p| p.y).fold(f64::MAX, f64::min);
                let max_x = shape.points.iter().map(|p| p.x).fold(f64::MIN, f64::max);
                let max_y = shape.points.iter().map(|p| p.y).fold(f64::MIN, f64::max);
                (min_x, min_y, max_x, max_y)
            }
            _ => (shape.x, shape.y, shape.x + shape.width, shape.y + shape.height),
        }
    }

    fn render_to_png(
        &self,
        shapes: &[WhiteboardShape],
        bounds: &ExportBounds,
        options: &ExportOptions,
    ) -> Result<Vec<u8>, ExportError> {
        let width = bounds.width.max(1.0) as u32;
        let height = bounds.height.max(1.0) as u32;

        let mut pixels = vec![255u8; (width * height * 4) as usize];

        if let Some(bg_color) = &options.background_color {
            let (r, g, b) = parse_hex_color(bg_color).unwrap_or((255, 255, 255));
            for chunk in pixels.chunks_mut(4) {
                chunk[0] = r;
                chunk[1] = g;
                chunk[2] = b;
                chunk[3] = 255;
            }
        }

        let mut sorted_shapes = shapes.to_vec();
        sorted_shapes.sort_by_key(|s| s.z_index);

        for shape in &sorted_shapes {
            self.render_shape_to_pixels(shape, &mut pixels, width, height, bounds, options);
        }

        let mut png_data = Vec::new();
        {
            let mut encoder = png::Encoder::new(&mut png_data, width, height);
            encoder.set_color(png::ColorType::Rgba);
            encoder.set_depth(png::BitDepth::Eight);

            if let Ok(mut writer) = encoder.write_header() {
                let _ = writer.write_image_data(&pixels);
            }
        }

        if png_data.is_empty() {
            png_data = self.create_placeholder_png(width, height, options)?;
        }

        Ok(png_data)
    }

    fn render_shape_to_pixels(
        &self,
        shape: &WhiteboardShape,
        pixels: &mut [u8],
        width: u32,
        height: u32,
        bounds: &ExportBounds,
        options: &ExportOptions,
    ) {
        let scale = options.scale as f64;
        let offset_x = bounds.min_x;
        let offset_y = bounds.min_y;

        let x = ((shape.x - offset_x) * scale) as i32;
        let y = ((shape.y - offset_y) * scale) as i32;
        let w = (shape.width * scale) as i32;
        let h = (shape.height * scale) as i32;

        let fill = shape
            .fill_color
            .as_ref()
            .and_then(|c| parse_hex_color(c));

        let stroke = shape
            .stroke_color
            .as_ref()
            .and_then(|c| parse_hex_color(c))
            .unwrap_or((0, 0, 0));

        let alpha = (shape.opacity * 255.0) as u8;

        match shape.shape_type {
            ShapeType::Rectangle | ShapeType::Sticky => {
                if let Some((r, g, b)) = fill {
                    self.fill_rect(pixels, width, height, x, y, w, h, r, g, b, alpha);
                }
                self.draw_rect_outline(pixels, width, height, x, y, w, h, stroke.0, stroke.1, stroke.2, alpha);
            }
            ShapeType::Ellipse => {
                if let Some((r, g, b)) = fill {
                    self.fill_ellipse(pixels, width, height, x, y, w, h, r, g, b, alpha);
                }
            }
            ShapeType::Line | ShapeType::Arrow | ShapeType::Freehand => {
                for i in 0..shape.points.len().saturating_sub(1) {
                    let p1 = &shape.points[i];
                    let p2 = &shape.points[i + 1];
                    let x1 = ((p1.x - offset_x) * scale) as i32;
                    let y1 = ((p1.y - offset_y) * scale) as i32;
                    let x2 = ((p2.x - offset_x) * scale) as i32;
                    let y2 = ((p2.y - offset_y) * scale) as i32;
                    self.draw_line(pixels, width, height, x1, y1, x2, y2, stroke.0, stroke.1, stroke.2, alpha);
                }
            }
            _ => {
                if let Some((r, g, b)) = fill {
                    self.fill_rect(pixels, width, height, x, y, w, h, r, g, b, alpha);
                }
            }
        }
    }

    fn fill_rect(
        &self,
        pixels: &mut [u8],
        width: u32,
        height: u32,
        x: i32,
        y: i32,
        w: i32,
        h: i32,
        r: u8,
        g: u8,
        b: u8,
        a: u8,
    ) {
        let x_start = x.max(0) as u32;
        let y_start = y.max(0) as u32;
        let x_end = ((x + w) as u32).min(width);
        let y_end = ((y + h) as u32).min(height);

        for py in y_start..y_end {
            for px in x_start..x_end {
                let idx = ((py * width + px) * 4) as usize;
                if idx + 3 < pixels.len() {
                    self.blend_pixel(pixels, idx, r, g, b, a);
                }
            }
        }
    }

    fn draw_rect_outline(
        &self,
        pixels: &mut [u8],
        width: u32,
        height: u32,
        x: i32,
        y: i32,
        w: i32,
        h: i32,
        r: u8,
        g: u8,
        b: u8,
        a: u8,
    ) {
        self.draw_line(pixels, width, height, x, y, x + w, y, r, g, b, a);
        self.draw_line(pixels, width, height, x + w, y, x + w, y + h, r, g, b, a);
        self.draw_line(pixels, width, height, x + w, y + h, x, y + h, r, g, b, a);
        self.draw_line(pixels, width, height, x, y + h, x, y, r, g, b, a);
    }

    fn fill_ellipse(
        &self,
        pixels: &mut [u8],
        width: u32,
        height: u32,
        x: i32,
        y: i32,
        w: i32,
        h: i32,
        r: u8,
        g: u8,
        b: u8,
        a: u8,
    ) {
        let cx = x + w / 2;
        let cy = y + h / 2;
        let rx = w / 2;
        let ry = h / 2;

        if rx <= 0 || ry <= 0 {
            return;
        }

        let x_start = (x.max(0)) as u32;
        let y_start = (y.max(0)) as u32;
        let x_end = ((x + w) as u32).min(width);
        let y_end = ((y + h) as u32).min(height);

        for py in y_start..y_end {
            for px in x_start..x_end {
                let dx = (px as i32 - cx) as f64 / rx as f64;
                let dy = (py as i32 - cy) as f64 / ry as f64;
                if dx * dx + dy * dy <= 1.0 {
                    let idx = ((py * width + px) * 4) as usize;
                    if idx + 3 < pixels.len() {
                        self.blend_pixel(pixels, idx, r, g, b, a);
                    }
                }
            }
        }
    }

    fn draw_line(
        &self,
        pixels: &mut [u8],
        width: u32,
        height: u32,
        x0: i32,
        y0: i32,
        x1: i32,
        y1: i32,
        r: u8,
        g: u8,
        b: u8,
        a: u8,
    ) {
        let dx = (x1 - x0).abs();
        let dy = -(y1 - y0).abs();
        let sx = if x0 < x1 { 1 } else { -1 };
        let sy = if y0 < y1 { 1 } else { -1 };
        let mut err = dx + dy;

        let mut x = x0;
        let mut y = y0;

        loop {
            if x >= 0 && y >= 0 && (x as u32) < width && (y as u32) < height {
                let idx = ((y as u32 * width + x as u32) * 4) as usize;
                if idx + 3 < pixels.len() {
                    self.blend_pixel(pixels, idx, r, g, b, a);
                }
            }

            if x == x1 && y == y1 {
                break;
            }

            let e2 = 2 * err;
            if e2 >= dy {
                err += dy;
                x += sx;
            }
            if e2 <= dx {
                err += dx;
                y += sy;
            }
        }
    }

    fn blend_pixel(&self, pixels: &mut [u8], idx: usize, r: u8, g: u8, b: u8, a: u8) {
        let alpha = a as f32 / 255.0;
        let inv_alpha = 1.0 - alpha;

        pixels[idx] = (r as f32 * alpha + pixels[idx] as f32 * inv_alpha) as u8;
        pixels[idx + 1] = (g as f32 * alpha + pixels[idx + 1] as f32 * inv_alpha) as u8;
        pixels[idx + 2] = (b as f32 * alpha + pixels[idx + 2] as f32 * inv_alpha) as u8;
        pixels[idx + 3] = 255;
    }

    fn create_placeholder_png(
        &self,
        width: u32,
        height: u32,
        options: &ExportOptions,
    ) -> Result<Vec<u8>, ExportError> {
        let mut pixels = vec![255u8; (width * height * 4) as usize];

        if let Some(bg) = &options.background_color {
            if let Some((r, g, b)) = parse_hex_color(bg) {
                for chunk in pixels.chunks_mut(4) {
                    chunk[0] = r;
                    chunk[1] = g;
                    chunk[2] = b;
                    chunk[3] = 255;
                }
            }
        }

        let mut png_data = Vec::new();
        {
            let mut encoder = png::Encoder::new(&mut png_data, width, height);
            encoder.set_color(png::ColorType::Rgba);
            encoder.set_depth(png::BitDepth::Eight);

            let mut writer = encoder
                .write_header()
                .map_err(|e| ExportError::RenderError(e.to_string()))?;

            writer
                .write_image_data(&pixels)
                .map_err(|e| ExportError::RenderError(e.to_string()))?;
        }

        Ok(png_data)
    }

    fn render_to_pdf(
        &self,
        shapes: &[WhiteboardShape],
        bounds: &ExportBounds,
        options: &ExportOptions,
        whiteboard: &WhiteboardData,
    ) -> Result<Vec<u8>, ExportError> {
        let mut pdf = PdfDocument::new(&whiteboard.name);

        let page_width = bounds.width.max(595.0) as f32;
        let page_height = bounds.height.max(842.0) as f32;

        pdf.add_page(page_width, page_height);

        if let Some(bg_color) = &options.background_color {
            pdf.set_fill_color(bg_color);
            pdf.draw_rect(0.0, 0.0, page_width, page_height, true, false);
        }

        let mut sorted_shapes = shapes.to_vec();
        sorted_shapes.sort_by_key(|s| s.z_index);

        for shape in &sorted_shapes {
            self.render_shape_to_pdf(&mut pdf, shape, bounds, options);
        }

        if options.include_metadata {
            pdf.add_metadata(&whiteboard.name, &Utc::now().to_rfc3339());
        }

        Ok(pdf.to_bytes())
    }

    fn render_shape_to_pdf(
        &self,
        pdf: &mut PdfDocument,
        shape: &WhiteboardShape,
        bounds: &ExportBounds,
        options: &ExportOptions,
    ) {
        let scale = options.scale as f64;
        let x = ((shape.x - bounds.min_x) * scale) as f32;
        let y = ((shape.y - bounds.min_y) * scale) as f32;
        let w = (shape.width * scale) as f32;
        let h = (shape.height * scale) as f32;

        if let Some(fill) = &shape.fill_color {
            pdf.set_fill_color(fill);
        }
        if let Some(stroke) = &shape.stroke_color {
            pdf.set_stroke_color(stroke);
        }
        pdf.set_line_width(shape.stroke_width as f32);

        match shape.shape_type {
            ShapeType::Rectangle | ShapeType::Sticky => {
                pdf.draw_rect(x, y, w, h, shape.fill_color.is_some(), shape.stroke_color.is_some());
            }
            ShapeType::Ellipse => {
                pdf.draw_ellipse(x + w / 2.0, y + h / 2.0, w / 2.0, h / 2.0, shape.fill_color.is_some(), shape.stroke_color.is_some());
            }
            ShapeType::Line | ShapeType::Arrow | ShapeType::Freehand => {
                if !shape.points.is_empty() {
                    let points: Vec<(f32, f32)> = shape
                        .points
                        .iter()
                        .map(|p| {
                            (((p.x - bounds.min_x) * scale) as f32, ((p.y - bounds.min_y) * scale) as f32)
                        })
                        .collect();
                    pdf.draw_path(&points);
                }
            }
            ShapeType::Text => {
                if let Some(text) = &shape.text {
                    let font_size = (shape.font_size.unwrap_or(12.0) * options.scale) as f32;
                    pdf.draw_text(text, x, y, font_size);
                }
            }
            ShapeType::Triangle => {
                let points: Vec<(f32, f32)> = vec![
                    (x + w / 2.0, y),
                    (x + w, y + h),
                    (x, y + h),
                    (x + w / 2.0, y),
                ];
                pdf.draw_path(&points);
            }
            ShapeType::Diamond => {
                let points: Vec<(f32, f32)> = vec![
                    (x + w / 2.0, y),
                    (x + w, y + h / 2.0),
                    (x + w / 2.0, y + h),
                    (x, y + h / 2.0),
                    (x + w / 2.0, y),
                ];
                pdf.draw_path(&points);
            }
            _ => {
                pdf.draw_rect(x, y, w, h, shape.fill_color.is_some(), shape.stroke_color.is_some());
            }
        }
    }

    fn render_to_svg(
        &self,
        shapes: &[WhiteboardShape],
        bounds: &ExportBounds,
        options: &ExportOptions,
    ) -> Result<String, ExportError> {
        let width = bounds.width.max(1.0);
        let height = bounds.height.max(1.0);

        let mut svg = String::new();
        svg.push_str(&format!(
            r#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="{}" height="{}" viewBox="0 0 {} {}">"#,
            width, height, width, height
        ));

        svg.push_str(r#"
<defs>
    <style>
        .shape { transition: all 0.2s ease; }
        .shape:hover { filter: brightness(1.1); }
    </style>
</defs>"#);

        if let Some(bg_color) = &options.background_color {
            svg.push_str(&format!(
                r#"<rect width="100%" height="100%" fill="{}"/>"#,
                bg_color
            ));
        }

        if options.include_grid {
            svg.push_str(&self.generate_svg_grid(width, height));
        }

        let mut sorted_shapes = shapes.to_vec();
        sorted_shapes.sort_by_key(|s| s.z_index);

        for shape in &sorted_shapes {
            svg.push_str(&self.shape_to_svg(shape, bounds, options));
        }

        svg.push_str("</svg>");

        Ok(svg)
    }

    fn generate_svg_grid(&self, width: f64, height: f64) -> String {
        let grid_size = 20.0;
        let mut grid = String::new();

        grid.push_str(r##"<g class="grid" stroke="#e0e0e0" stroke-width="0.5">"##);

        let mut x = 0.0;
        while x <= width {
            grid.push_str(&format!(
                r#"<line x1="{}" y1="0" x2="{}" y2="{}"/>"#,
                x, x, height
            ));
            x += grid_size;
        }

        let mut y = 0.0;
        while y <= height {
            grid.push_str(&format!(
                r#"<line x1="0" y1="{}" x2="{}" y2="{}"/>"#,
                y, width, y
            ));
            y += grid_size;
        }

        grid.push_str("</g>");
        grid
    }

    fn shape_to_svg(
        &self,
        shape: &WhiteboardShape,
        bounds: &ExportBounds,
        options: &ExportOptions,
    ) -> String {
        let scale = options.scale as f64;
        let x = (shape.x - bounds.min_x) * scale;
        let y = (shape.y - bounds.min_y) * scale;
        let w = shape.width * scale;
        let h = shape.height * scale;

        let fill = shape
            .fill_color
            .as_ref()
            .map(|c| c.as_str())
            .unwrap_or("none");

        let stroke = shape
            .stroke_color
            .as_ref()
            .map(|c| c.as_str())
            .unwrap_or("none");

        let stroke_width = shape.stroke_width * options.scale;
        let opacity = shape.opacity;

        let transform = if shape.rotation != 0.0 {
            format!(
                r#" transform="rotate({} {} {})""#,
                shape.rotation,
                x + w / 2.0,
                y + h / 2.0
            )
        } else {
            String::new()
        };

        match shape.shape_type {
            ShapeType::Rectangle | ShapeType::Sticky => {
                format!(
                    r#"<rect class="shape" x="{}" y="{}" width="{}" height="{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                    x, y, w, h, fill, stroke, stroke_width, opacity, transform
                )
            }
            ShapeType::Ellipse => {
                format!(
                    r#"<ellipse class="shape" cx="{}" cy="{}" rx="{}" ry="{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                    x + w / 2.0,
                    y + h / 2.0,
                    w / 2.0,
                    h / 2.0,
                    fill,
                    stroke,
                    stroke_width,
                    opacity,
                    transform
                )
            }
            ShapeType::Line | ShapeType::Arrow | ShapeType::Freehand => {
                if shape.points.is_empty() {
                    return String::new();
                }
                let points: Vec<String> = shape
                    .points
                    .iter()
                    .map(|p| {
                        format!(
                            "{},{}",
                            (p.x - bounds.min_x) * scale,
                            (p.y - bounds.min_y) * scale
                        )
                    })
                    .collect();

                if shape.shape_type == ShapeType::Freehand {
                    let mut path = format!("M {}", points[0]);
                    for point in points.iter().skip(1) {
                        path.push_str(&format!(" L {}", point));
                    }
                    format!(
                        r#"<path class="shape" d="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                        path, stroke, stroke_width, opacity, transform
                    )
                } else {
                    let line_points = points.join(" ");
                    let marker = if shape.shape_type == ShapeType::Arrow {
                        r#" marker-end="url(#arrowhead)""#
                    } else {
                        ""
                    };
                    format!(
                        r#"<polyline class="shape" points="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}"{}{}/>"#,
                        line_points, stroke, stroke_width, opacity, marker, transform
                    )
                }
            }
            ShapeType::Text => {
                let font_size = f64::from(shape.font_size.unwrap_or(16.0)) * scale;
                let text_content = shape.text.as_deref().unwrap_or("");
                format!(
                    r#"<text class="shape" x="{}" y="{}" font-size="{}" fill="{}" opacity="{}"{}>{}</text>"#,
                    x, y + font_size, font_size, fill, opacity, transform, text_content
                )
            }
            ShapeType::Image => {
                if let Some(src) = &shape.image_data {
                    format!(
                        r#"<image class="shape" x="{}" y="{}" width="{}" height="{}" href="{}" opacity="{}"{}/>"#,
                        x, y, w, h, src, opacity, transform
                    )
                } else {
                    String::new()
                }
            }
            ShapeType::Connector => {
                if shape.points.len() >= 2 {
                    let points: Vec<String> = shape
                        .points
                        .iter()
                        .map(|p| {
                            format!(
                                "{},{}",
                                (p.x - bounds.min_x) * scale,
                                (p.y - bounds.min_y) * scale
                            )
                        })
                        .collect();
                    let line_points = points.join(" ");
                    format!(
                        r#"<polyline class="shape" points="{}" fill="none" stroke="{}" stroke-width="{}" opacity="{}" marker-end="url(#arrowhead)"{}/>"#,
                        line_points, stroke, stroke_width, opacity, transform
                    )
                } else {
                    String::new()
                }
            }
            ShapeType::Triangle => {
                let x1 = x + w / 2.0;
                let y1 = y;
                let x2 = x;
                let y2 = y + h;
                let x3 = x + w;
                let y3 = y + h;
                format!(
                    r#"<polygon class="shape" points="{},{} {},{} {},{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                    x1, y1, x2, y2, x3, y3, fill, stroke, stroke_width, opacity, transform
                )
            }
            ShapeType::Diamond => {
                let x1 = x + w / 2.0;
                let y1 = y;
                let x2 = x + w;
                let y2 = y + h / 2.0;
                let x3 = x + w / 2.0;
                let y3 = y + h;
                let x4 = x;
                let y4 = y + h / 2.0;
                format!(
                    r#"<polygon class="shape" points="{},{} {},{} {},{} {},{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                    x1, y1, x2, y2, x3, y3, x4, y4, fill, stroke, stroke_width, opacity, transform
                )
            }
            ShapeType::Star => {
                let cx = x + w / 2.0;
                let cy = y + h / 2.0;
                let outer_r = w.min(h) / 2.0;
                let inner_r = outer_r * 0.4;
                let mut points = Vec::new();
                for i in 0..10 {
                    let angle = std::f64::consts::PI / 2.0 - (i as f64) * std::f64::consts::PI / 5.0;
                    let r = if i % 2 == 0 { outer_r } else { inner_r };
                    let px = cx + r * angle.cos();
                    let py = cy - r * angle.sin();
                    points.push(format!("{px},{py}"));
                }
                format!(
                    r#"<polygon class="shape" points="{}" fill="{}" stroke="{}" stroke-width="{}" opacity="{}"{}/>"#,
                    points.join(" "), fill, stroke, stroke_width, opacity, transform
                )
            }
        }
    }

    fn export_to_json(
        &self,
        whiteboard: &WhiteboardData,
        shapes: &[WhiteboardShape],
    ) -> Result<String, ExportError> {
        let export_data = serde_json::json!({
            "id": whiteboard.id,
            "name": whiteboard.name,
            "created_at": whiteboard.created_at,
            "updated_at": whiteboard.updated_at,
            "shapes": shapes,
        });

        serde_json::to_string_pretty(&export_data)
            .map_err(|e| ExportError::RenderFailed(format!("JSON serialization failed: {e}")))
    }
}
