//! Individual loose prop canvas component for per-prop rendering.
//!
//! Each loose prop gets its own canvas element positioned via CSS transforms.
//! This enables pixel-perfect hit detection using getImageData().

use leptos::prelude::*;
use uuid::Uuid;

use chattyness_db::models::LooseProp;

#[cfg(feature = "hydrate")]
pub use super::canvas_utils::hit_test_canvas;
#[cfg(feature = "hydrate")]
use super::canvas_utils::normalize_asset_path;
use super::settings::{BASE_AVATAR_SCALE, BASE_PROP_SCALE, BASE_PROP_SIZE};

/// Get a unique key for a loose prop (for Leptos For keying).
pub fn loose_prop_key(p: &LooseProp) -> Uuid {
    p.id
}

/// Individual loose prop canvas component.
///
/// Renders a single prop with:
/// - CSS transform for position (GPU-accelerated, no redraw on move)
/// - Canvas for prop sprite (redraws only on appearance change)
/// - Pixel-perfect hit detection via getImageData()
#[component]
pub fn LoosePropCanvas(
    /// The prop data (as a signal for reactive updates).
    prop: Signal<LooseProp>,
    /// X scale factor for coordinate conversion.
    scale_x: Signal<f64>,
    /// Y scale factor for coordinate conversion.
    scale_y: Signal<f64>,
    /// X offset for coordinate conversion.
    offset_x: Signal<f64>,
    /// Y offset for coordinate conversion.
    offset_y: Signal<f64>,
    /// Base prop size in screen pixels (already includes viewport scaling).
    base_prop_size: Signal<f64>,
    /// Z-index for stacking order.
    z_index: i32,
) -> impl IntoView {
    let canvas_ref = NodeRef::<leptos::html::Canvas>::new();

    // Reactive style for CSS positioning (GPU-accelerated transforms)
    let style = move || {
        let p = prop.get();
        let sx = scale_x.get();
        let sy = scale_y.get();
        let ox = offset_x.get();
        let oy = offset_y.get();
        let base_size = base_prop_size.get();

        // Calculate rendered prop size
        let prop_scale_ratio = BASE_PROP_SCALE / BASE_AVATAR_SCALE;
        let prop_size = base_size * prop_scale_ratio * p.scale as f64;

        // Screen position (center of prop)
        let screen_x = p.position_x * sx + ox;
        let screen_y = p.position_y * sy + oy;

        // Canvas positioned at top-left corner
        let canvas_x = screen_x - prop_size / 2.0;
        let canvas_y = screen_y - prop_size / 2.0;

        // Add amber dashed border for locked props
        let border_style = if p.is_locked {
            "border: 2px dashed #f59e0b; box-sizing: border-box;"
        } else {
            ""
        };

        format!(
            "position: absolute; \
             left: 0; top: 0; \
             transform: translate({}px, {}px); \
             z-index: {}; \
             pointer-events: auto; \
             width: {}px; \
             height: {}px; {}",
            canvas_x, canvas_y, z_index, prop_size, prop_size, border_style
        )
    };

    #[cfg(feature = "hydrate")]
    {
        use std::cell::RefCell;
        use std::rc::Rc;
        use wasm_bindgen::closure::Closure;
        use wasm_bindgen::JsCast;

        // Image cache for this prop
        let image_cache: Rc<RefCell<Option<web_sys::HtmlImageElement>>> =
            Rc::new(RefCell::new(None));

        // Redraw trigger - incremented when image loads
        let (redraw_trigger, set_redraw_trigger) = signal(0u32);

        // Effect to draw the prop when canvas is ready or appearance changes
        Effect::new(move |_| {
            // Subscribe to redraw trigger
            let _ = redraw_trigger.get();

            let p = prop.get();
            let base_size = base_prop_size.get();

            let Some(canvas) = canvas_ref.get() else {
                return;
            };

            // Calculate rendered prop size
            let prop_scale_ratio = BASE_PROP_SCALE / BASE_AVATAR_SCALE;
            let prop_size = base_size * prop_scale_ratio * p.scale as f64;

            let canvas_el: &web_sys::HtmlCanvasElement = &canvas;

            // Set canvas resolution
            canvas_el.set_width(prop_size as u32);
            canvas_el.set_height(prop_size as u32);

            let Ok(Some(ctx)) = canvas_el.get_context("2d") else {
                return;
            };
            let ctx: web_sys::CanvasRenderingContext2d = ctx.dyn_into().unwrap();

            // Clear canvas
            ctx.clear_rect(0.0, 0.0, prop_size, prop_size);

            // Draw prop sprite if asset path available
            if !p.prop_asset_path.is_empty() {
                let normalized_path = normalize_asset_path(&p.prop_asset_path);
                let mut cache = image_cache.borrow_mut();

                if let Some(ref img) = *cache {
                    // Image in cache - draw if loaded
                    if img.complete() && img.natural_width() > 0 {
                        let _ = ctx.draw_image_with_html_image_element_and_dw_and_dh(
                            img, 0.0, 0.0, prop_size, prop_size,
                        );
                    }
                } else {
                    // Not in cache - create and load
                    let img = web_sys::HtmlImageElement::new().unwrap();

                    let trigger = set_redraw_trigger;
                    let onload = Closure::once(Box::new(move || {
                        trigger.update(|v| *v += 1);
                    }) as Box<dyn FnOnce()>);
                    img.set_onload(Some(onload.as_ref().unchecked_ref()));
                    onload.forget();

                    img.set_src(&normalized_path);
                    *cache = Some(img);
                }
            } else {
                // Fallback: draw placeholder circle with prop name
                ctx.begin_path();
                let _ = ctx.arc(
                    prop_size / 2.0,
                    prop_size / 2.0,
                    prop_size / 2.0 - 2.0,
                    0.0,
                    std::f64::consts::PI * 2.0,
                );
                ctx.set_fill_style_str("#f59e0b");
                ctx.fill();
                ctx.set_stroke_style_str("#d97706");
                ctx.set_line_width(2.0);
                ctx.stroke();

                // Draw prop name
                let text_scale = prop_size / (BASE_PROP_SIZE * BASE_PROP_SCALE);
                ctx.set_fill_style_str("#fff");
                ctx.set_font(&format!("{}px sans-serif", 10.0 * text_scale));
                ctx.set_text_align("center");
                ctx.set_text_baseline("middle");
                let _ = ctx.fill_text(&p.prop_name, prop_size / 2.0, prop_size / 2.0);
            }
        });
    }

    // Compute data-prop-id reactively
    let data_prop_id = move || prop.get().id.to_string();

    view! {
        <canvas
            node_ref=canvas_ref
            style=style
            data-prop-id=data_prop_id
        />
    }
}

