use acadrust::entities::{AttributeDefinition, AttributeEntity, Entity, Insert};
use acadrust::types::Vector3;
use acadrust::EntityType;
use glam::{DVec3, Vec3};

use crate::command::{CadCommand, CmdResult};
use crate::modules::{IconKind, ModuleEvent, ToolDef};
use crate::scene::model::wire_model::WireModel;

pub fn tool() -> ToolDef {
    ToolDef {
        id: "INSERT",
        label: "Insert Block",
        icon: IconKind::Svg(include_bytes!("../../../assets/icons/blocks/insert.svg")),
        event: ModuleEvent::Command("INSERT".to_string()),
    }
}

enum Step {
    Name,
    Point {
        name: String,
    },
    /// Attribute filling: prompt the user tag by tag.
    FillAttr {
        /// The block's attribute definitions — carried in full so the created
        /// attributes inherit their position / alignment / height / rotation /
        /// style, not just tag + prompt + default (#255).
        attdefs: Vec<AttributeDefinition>,
        /// Index of the attdef currently being prompted.
        idx: usize,
        /// (tag, value) pairs collected so far, in attdef order.
        values: Vec<(String, String)>,
    },
}

/// Which numeric value the insertion-point step is currently waiting for after
/// a Scale / Rotate keyword.
#[derive(Clone, Copy)]
enum AwaitKind {
    Scale,
    Rotation,
}

pub struct InsertBlockCommand {
    available: Vec<String>,
    step: Step,
    /// Uniform X/Y scale applied to the placed block (default 1).
    x_scale: f64,
    y_scale: f64,
    /// Rotation applied to the placed block, in radians (default 0).
    rotation_rad: f64,
    /// Set while a Scale/Rotate value is being typed at the insertion step.
    awaiting: Option<AwaitKind>,
    /// Pending Insert entity stored while attr-filling is in progress.
    pending_insert: Option<Insert>,
    /// Optional drag preview: the block's wire geometry plus the base point it
    /// is measured from, so `on_preview_wires` can rubber-band it to the
    /// cursor. Set by paste-as-block; empty for a plain INSERT.
    preview: Option<(Vec<WireModel>, Vec3)>,
}

impl InsertBlockCommand {
    pub fn new(available: Vec<String>) -> Self {
        Self {
            available,
            step: Step::Name,
            x_scale: 1.0,
            y_scale: 1.0,
            rotation_rad: 0.0,
            awaiting: None,
            pending_insert: None,
            preview: None,
        }
    }

    /// Start the command already locked to `name`, skipping the name prompt and
    /// going straight to "specify insertion point". `preview_wires` (measured
    /// from `base`) rubber-band under the cursor. Used by paste-as-block, which
    /// has just defined the block and only needs the drop point.
    pub fn new_for_block(name: String, preview_wires: Vec<WireModel>, base: Vec3) -> Self {
        Self {
            available: vec![name.clone()],
            step: Step::Point { name },
            x_scale: 1.0,
            y_scale: 1.0,
            rotation_rad: 0.0,
            awaiting: None,
            pending_insert: None,
            preview: Some((preview_wires, base)),
        }
    }
}

impl CadCommand for InsertBlockCommand {
    fn name(&self) -> &'static str {
        "INSERT"
    }

    fn prompt(&self) -> String {
        match &self.step {
            Step::Name => {
                let hint = if self.available.is_empty() {
                    String::new()
                } else {
                    format!("  [{}]", self.available.join(", "))
                };
                format!("INSERT  Enter block name:{hint}")
            }
            Step::Point { name } => match self.awaiting {
                Some(AwaitKind::Scale) => "INSERT  Specify scale factor <1>:".to_string(),
                Some(AwaitKind::Rotation) => "INSERT  Specify rotation angle <0>:".to_string(),
                None => format!(
                    "INSERT  Specify insertion point for \"{}\"  [Scale/Rotate]:",
                    name
                ),
            },
            Step::FillAttr { attdefs, idx, .. } => {
                if let Some(ad) = attdefs.get(*idx) {
                    let default_hint = if ad.default_value.is_empty() {
                        String::new()
                    } else {
                        format!("  <{}>", ad.default_value)
                    };
                    let prompt_text = if ad.prompt.is_empty() {
                        ad.tag.as_str()
                    } else {
                        ad.prompt.as_str()
                    };
                    format!("INSERT  {prompt_text}{default_hint}:")
                } else {
                    "INSERT  Filling attributes...".into()
                }
            }
        }
    }

    fn on_point(&mut self, pt: DVec3) -> CmdResult {
        match &self.step {
            Step::Name => CmdResult::NeedPoint,
            Step::Point { name } => {
                let mut ins = Insert::new(name.clone(), Vector3::new(pt.x, pt.y, pt.z));
                ins.set_x_scale(self.x_scale);
                ins.set_y_scale(self.y_scale);
                ins.rotation = self.rotation_rad;
                let block_name = name.clone();
                self.pending_insert = Some(ins);
                // Signal the host to check for attdefs.
                CmdResult::AttreqNeeded { block_name }
            }
            Step::FillAttr { .. } => CmdResult::NeedPoint,
        }
    }

    fn on_enter(&mut self) -> CmdResult {
        match &self.step {
            // A bare Enter while a scale/rotation value is awaited keeps the
            // current default instead of cancelling the command.
            Step::Point { .. } if self.awaiting.is_some() => {
                self.awaiting = None;
                CmdResult::NeedPoint
            }
            Step::Name | Step::Point { .. } => CmdResult::Cancel,
            Step::FillAttr { .. } => {
                // Treat Enter as accepting the default.
                self.accept_attr_value("")
            }
        }
    }

    fn wants_text_input(&self) -> bool {
        matches!(
            self.step,
            Step::Name | Step::FillAttr { .. } | Step::Point { .. }
        )
    }

    fn point_step_accepts_keywords(&self) -> bool {
        // The insertion-point step also takes Scale / Rotate keywords while
        // still accepting a point pick. Once a value is being typed (awaiting),
        // route the whole number through `on_text_input` instead.
        matches!(self.step, Step::Point { .. }) && self.awaiting.is_none()
    }

    fn wants_text_with_spaces(&self) -> bool {
        // Block names don't embed whitespace, but attribute values do.
        matches!(self.step, Step::FillAttr { .. })
    }

    fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
        match &self.step {
            Step::Name => {
                let name = text.trim();
                if !self.available.iter().any(|c| c.eq_ignore_ascii_case(name)) {
                    return None;
                }
                self.step = Step::Point {
                    name: name.to_string(),
                };
                Some(CmdResult::NeedPoint)
            }
            Step::FillAttr { .. } => Some(self.accept_attr_value(text)),
            Step::Point { .. } => {
                // Typing the value awaited after a Scale / Rotate keyword.
                if let Some(kind) = self.awaiting {
                    if let Ok(v) = text.trim().parse::<f64>() {
                        match kind {
                            AwaitKind::Scale if v != 0.0 => {
                                self.x_scale = v;
                                self.y_scale = v;
                            }
                            AwaitKind::Scale => {}
                            AwaitKind::Rotation => self.rotation_rad = v.to_radians(),
                        }
                    }
                    self.awaiting = None;
                    return Some(CmdResult::NeedPoint);
                }
                match text.trim().to_uppercase().as_str() {
                    "S" | "SCALE" => {
                        self.awaiting = Some(AwaitKind::Scale);
                        Some(CmdResult::NeedPoint)
                    }
                    "R" | "ROTATE" => {
                        self.awaiting = Some(AwaitKind::Rotation);
                        Some(CmdResult::NeedPoint)
                    }
                    _ => None,
                }
            }
        }
    }

    fn on_preview_wires(&mut self, pt: DVec3) -> Vec<WireModel> { let pt = pt.as_vec3();
        match (&self.step, &self.preview) {
            (Step::Point { .. }, Some((wires, base))) => {
                let delta = pt - *base;
                wires.iter().map(|w| w.translated(delta)).collect()
            }
            _ => vec![],
        }
    }

    fn attreq_set_attdefs(&mut self, attdefs: Vec<AttributeDefinition>) {
        self.step = Step::FillAttr {
            attdefs,
            idx: 0,
            values: vec![],
        };
    }

    fn attreq_take_insert(&mut self) -> Option<acadrust::EntityType> {
        self.pending_insert
            .take()
            .map(|ins| EntityType::Insert(ins))
    }
}

impl InsertBlockCommand {
    /// Accept the current attribute value (empty = use default) and advance.
    /// Returns CommitAndExit when all attdefs have been filled.
    fn accept_attr_value(&mut self, text: &str) -> CmdResult {
        let (tag, default, next_idx, total) = match &self.step {
            Step::FillAttr { attdefs, idx, .. } => {
                let ad = &attdefs[*idx];
                (ad.tag.clone(), ad.default_value.clone(), idx + 1, attdefs.len())
            }
            _ => return CmdResult::Cancel,
        };

        let value = if text.trim().is_empty() {
            default
        } else {
            text.trim().to_string()
        };

        if let Step::FillAttr {
            ref mut values,
            ref mut idx,
            ..
        } = self.step
        {
            values.push((tag, value));
            *idx = next_idx;
        }

        if next_idx >= total {
            // All attdefs filled — build the INSERT's attribute list. Each
            // attribute inherits its ATTDEF's geometry (position, alignment,
            // height, rotation, style) via `from_definition`, then the block's
            // insertion transform places it into WCS so it lands where the block
            // put it instead of stacking at the origin (#255).
            let (attdefs, values) = match &self.step {
                Step::FillAttr {
                    attdefs, values, ..
                } => (attdefs.clone(), values.clone()),
                _ => (vec![], vec![]),
            };
            let mut ins = match self.pending_insert.take() {
                Some(i) => i,
                None => return CmdResult::Cancel,
            };
            let xform = ins.get_transform();
            for (ad, (_tag, value)) in attdefs.iter().zip(values.iter()) {
                let mut attr = AttributeEntity::from_definition(ad, Some(value.clone()));
                attr.apply_transform(&xform);
                ins.attributes.push(attr);
            }
            CmdResult::CommitAndExit(EntityType::Insert(ins))
        } else {
            CmdResult::NeedPoint
        }
    }
}


// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["INSERT"] });  // InsertBlockCommand
