use core::f32;
use egui::{CollapsingHeader, RichText, Sense, TextEdit, Ui, UiBuilder, vec2};
use serde::{Deserialize, Serialize};

use crate::{
    PROJECT_FOLDER, RightPanelContent,
    editors::{
        tags::Tag,
        template_editor::{FieldValue, Template},
    },
    util,
};

pub type ObjectId = String;

#[derive(Debug, Serialize, Deserialize)]
pub struct ObjectInstance {
    // template info
    pub id: ObjectId,
    pub template_id: String,

    // instance info
    pub name: String,
    pub fields: std::collections::HashMap<String, FieldValue>,

    #[serde(default)]
    pub tags: Vec<String>,

    #[serde(skip)]
    pub saved: bool,

    #[serde(skip)]
    pub dialog: Option<egui_file::FileDialog>,
}

impl Clone for ObjectInstance {
    fn clone(&self) -> Self {
        Self {
            id: self.id.clone(),
            template_id: self.template_id.clone(),
            name: self.name.clone(),
            fields: self.fields.clone(),
            tags: self.tags.clone(),
            saved: self.saved,
            dialog: None,
        }
    }
}

impl Default for ObjectInstance {
    fn default() -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            template_id: "new_template_instance".to_string(),
            name: "new_object".to_string(),
            fields: std::collections::HashMap::new(),
            tags: Vec::new(),
            saved: false,
            dialog: None,
        }
    }
}

impl ObjectInstance {
    pub fn new(template: &Template) -> Self {
        let mut fields = std::collections::HashMap::new();

        for field in &template.fields {
            fields.insert(field.name.clone(), FieldValue::from_type(&field.field_type));
        }

        Self {
            id: uuid::Uuid::new_v4().to_string(),
            template_id: template.id.clone(),
            name: "new_object".to_string(),
            fields,
            tags: Vec::new(),
            saved: false,
            dialog: None,
        }
    }

    pub fn save(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let path = PROJECT_FOLDER
            .join("objects")
            .join(format!("{}.json", &self.id));

        let content = serde_json::to_string_pretty(self)?;
        std::fs::write(&path, content)?;
        self.saved = true;
        Ok(())
    }

    pub fn load(id: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let path = PROJECT_FOLDER.join("objects").join(format!("{id}.json"));

        let content = std::fs::read_to_string(&path)?;
        let mut instance: ObjectInstance = serde_json::from_str(&content)?;
        instance.saved = true;
        Ok(instance)
    }

    pub fn ui(
        &mut self,
        ui: &mut Ui,
        template: &Template,
        right_panel: &mut Option<RightPanelContent>,
        objects: &mut [ObjectInstance],
    ) {
        let _ = right_panel;
        if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
            if let Err(e) = self.save() {
                eprintln!("Failed to save: {e}");
            }
        }

        ui.vertical(|ui| {
            // Show save status and button

            util::saved_status(ui, self.saved, &self.id, &self.name);

            ui.horizontal(|ui| {
                if ui.button("Save").clicked() {
                    if let Err(e) = self.save() {
                        eprintln!("Failed to save: {e}");
                    }
                }

                if ui.button("Create Copy").clicked() {
                    let mut copy = self.clone();
                    copy.id = uuid::Uuid::new_v4().to_string();
                    copy.dialog = None;
                    copy.name = format!("{} (Copy)", self.name);
                    copy.save().unwrap();

                    *right_panel = Some(RightPanelContent::Object(Box::new(copy)));
                }

                if ui.button("Delete").clicked() {
                    std::fs::remove_file(
                        PROJECT_FOLDER
                            .join("objects")
                            .join(format!("{}.json", self.id)),
                    )
                    .unwrap();

                    *right_panel = Some(RightPanelContent::None);
                }
            });

            ui.separator();

            egui::ScrollArea::vertical().show(ui, |ui| {
                // Render each field

                // allow name to be edited
                CollapsingHeader::new("Name")
                    .default_open(true)
                    .show(ui, |ui| {
                        ui.separator();
                        let _ = TextEdit::singleline(&mut self.name)
                            .desired_width(f32::INFINITY)
                            .frame(false)
                            .show(ui)
                            .response;
                        ui.separator();
                    });

                CollapsingHeader::new("Tags")
                    .default_open(true)
                    .show(ui, |ui| {
                        ui.separator();
                        Tag::selector_ui(&mut self.tags, ui, Some(&mut self.saved));
                        ui.separator();
                    });

                for field_def in &template.fields {
                    if let Some(field_value) = self.fields.get_mut(&field_def.name) {
                        let id = ui.make_persistent_id(format!("field_{}", field_def.name));
                        egui::collapsing_header::CollapsingState::load_with_default_open(
                            ui.ctx(),
                            id,
                            true,
                        )
                        .show_header(ui, |ui| {
                            ui.strong(&field_def.name);
                        })
                        .body(|ui| {
                            if let Some(desc) = &field_def.description {
                                ui.label(RichText::new(desc).italics().weak());
                            }

                            ui.separator();

                            Self::render_field(field_value, ui, &mut self.saved, objects);

                            ui.separator();
                        });
                    }
                }
            });
        });
    }

    fn render_field(
        field_value: &mut FieldValue,
        ui: &mut egui::Ui,
        saved: &mut bool,
        objects: &mut [ObjectInstance],
    ) {
        match field_value {
            FieldValue::SingleLine(value) => {
                if TextEdit::singleline(value)
                    .desired_width(f32::INFINITY)
                    .frame(false)
                    .show(ui)
                    .response
                    .changed()
                {
                    *saved = false;
                }
            }
            FieldValue::MultiLine(value) => {
                if TextEdit::multiline(value)
                    .desired_width(f32::INFINITY)
                    .desired_rows(5)
                    .frame(false)
                    .show(ui)
                    .response
                    .changed()
                {
                    *saved = false;
                }
            }
            FieldValue::Date(value) => {
                let response = ui.add(egui_extras::DatePickerButton::new(value));
                if response.changed() {
                    *saved = false;
                }
            }
            FieldValue::Number(value) => {
                let response = ui.add(egui::DragValue::new(value).speed(0.1));
                if response.changed() {
                    *saved = false;
                }
            }
            FieldValue::Image(value) => {
                ui.scope_builder(UiBuilder::new().sense(Sense::HOVER), |ui| {
                    let id = ui.make_persistent_id("is_hovered");
                    let should_show = value.is_empty()
                        || ui.response().hovered()
                        || ui.memory(|mem| mem.data.get_temp(id).unwrap_or(false))
                        || !PROJECT_FOLDER.join("assets").join(&value).exists();

                    // Simple path input for now
                    if should_show {
                        let response = TextEdit::singleline(value)
                            .hint_text("Asset name (ignore file extension)")
                            .desired_width(f32::INFINITY)
                            .frame(false)
                            .show(ui)
                            .response;

                        if response.changed() {
                            *saved = false;
                        }

                        ui.memory_mut(|mem| {
                            *mem.data.get_temp_mut_or_insert_with(id, || true) = response.hovered();
                        });
                    }

                    // If we have a valid path, try to display a preview
                    if !value.is_empty() {
                        let path = PROJECT_FOLDER.join("assets").join(&value);

                        if let Ok(bytes) = std::fs::read(&path) {
                            let image_source = egui::ImageSource::Bytes {
                                uri: std::borrow::Cow::Owned(path.to_str().unwrap().to_string()),
                                bytes: bytes.into(),
                            };
                            ui.add(
                                egui::Image::new(image_source).max_size(vec2(256.0, f32::INFINITY)),
                            );
                        }
                    }
                });
            }
            FieldValue::Link(template_id) => {
                ObjectInstance::selector_ui(template_id, objects, ui, saved)
            }
            FieldValue::Links(_template_ids) => {
                let mut value = String::new();
                if ui.text_edit_singleline(&mut value).changed() {
                    *saved = false;
                }
            }
        };
    }

    fn selector_ui(
        selected: &mut ObjectId,
        objects: &mut [ObjectInstance],
        ui: &mut egui::Ui,
        saved: &mut bool,
    ) {
        if !selected.is_empty() {
            if let Ok(object) = ObjectInstance::load(selected) {
                ui.strong(&object.name);
            }
        }

        ui.horizontal(|ui| {
            let id = ui.make_persistent_id("new_object");

            let ctx = ui.ctx();
            let mut object_selection: usize =
                ctx.memory(|mem| mem.data.get_temp::<usize>(id).unwrap_or(0));

            if objects.is_empty() {
                ui.label("No objects available");
            } else {
                egui::ComboBox::from_id_salt(id)
                    .selected_text(&objects[object_selection].name)
                    .show_ui(ui, |ui| {
                        for (i, obj) in objects.iter().enumerate() {
                            ui.selectable_value(&mut object_selection, i, &obj.name);
                        }
                    });
            }

            let ctx = ui.ctx();
            ctx.memory_mut(|mem| {
                *mem.data.get_temp_mut_or_default::<usize>(id) = object_selection;
            });

            if ui.button("Set").clicked() && object_selection < objects.len() {
                *selected = objects[object_selection].id.clone();
                *saved = false;
            }

            if ui.button("Remove").clicked() {
                *selected = String::new();
                *saved = false;
            }
        });
    }
}
