use super::*;

pub(crate) fn material_instance_asset_editor(
    world: &mut World,
    ui: &mut egui::Ui,
    asset: &EditorAsset,
    path: &str,
    show_document_chrome: bool,
) {
    let document_key =
        crate::asset_documents::ensure_material_instance_document(world, path, &asset.label);
    let mut draft = world
        .resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
        .material_instance(&document_key)
        .cloned()
        .expect("ensured Material Instance document");
    let base_asset = load_live_base_asset(world, &draft.base);
    super::editor::prefetch_bound_material_textures(
        world,
        base_asset
            .iter()
            .map(|asset| &asset.inputs)
            .chain(std::iter::once(&draft.overrides)),
    );
    let texture_assets = material_texture_candidates(world);
    let texture_drop_candidate = world
        .resource::<EditorAssets>()
        .dragging_selection()
        .and_then(|selection| {
            texture_assets
                .iter()
                .find(|candidate| material_locate_matches_selection(&candidate.locate, selection))
        })
        .cloned();
    let original = draft.clone();

    if show_document_chrome {
        instance_document_chrome(world, ui, &document_key, &mut draft);
    }

    let Some(base_asset) = base_asset else {
        ui.colored_label(
            ERROR,
            "Base Material is missing; this instance renders Default Grid",
        );
        if draft != original {
            crate::asset_documents::update_material_instance_document(world, &document_key, draft);
        }
        return;
    };
    let schema = material_schema_for_asset(&base_asset).unwrap_or_else(standard_lit_input_schema);
    if show_document_chrome {
        ui.horizontal(|ui| {
            ui.add_sized([92.0, 20.0], egui::Label::new("Shader"));
            ui.label(shader_kind_label(base_asset.shader.kind));
            ui.small(egui::RichText::new("Inherited").color(TEXT_MUTED));
        });
    }
    ui.add_enabled_ui(false, |ui| {
        let mut render_state = base_asset.render_state;
        material_surface_options_ui(ui, &mut render_state, Some(&mut draft.overrides));
    });
    ui.add_space(4.0);
    let input_response = super::instance_inputs::material_instance_input_schema_editor(
        ui,
        &schema,
        &base_asset.inputs,
        &mut draft.overrides,
        &texture_assets,
        texture_drop_candidate.as_ref(),
    );
    if let Some(target) = input_response.locate.clone() {
        world
            .resource_mut::<EditorAssets>()
            .select(material_locate_selection(target));
        crate::ui::request_editor_tab(world, crate::ui::EditorTab::AssetBrowser);
    }
    if input_response.browse_library {
        crate::ui::request_editor_tab(world, crate::ui::EditorTab::AssetBrowser);
    }
    if draft != original {
        crate::asset_documents::update_material_instance_document(world, &document_key, draft);
    }
    if input_response.accepted_drop {
        world.resource_mut::<EditorAssets>().clear_drag();
    }
}

fn instance_document_chrome(
    world: &mut World,
    ui: &mut egui::Ui,
    document_key: &crate::asset_documents::AuthoredAssetDocumentKey,
    draft: &mut MaterialInstanceAsset,
) {
    if let Some(snapshot) = world
        .resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
        .snapshot(document_key)
    {
        authored_document_status_ui(ui, &snapshot);
        if let Some(error) = snapshot.error.as_ref() {
            ui.colored_label(WARNING, error);
        }
    }
    compact_text_edit(ui, "Label", &mut draft.label);
    let candidates = material_base_candidates(world);
    ui.horizontal(|ui| {
        ui.add_sized([92.0, 20.0], egui::Label::new("Base material"));
        let selected = candidates
            .iter()
            .find(|(_, candidate)| candidate.0.asset_id == draft.base.0.asset_id)
            .map(|(label, _)| label.as_str())
            .unwrap_or("Missing base");
        egui::ComboBox::from_id_salt("material_instance_base")
            .selected_text(selected)
            .show_ui(ui, |ui| {
                for (label, reference) in &candidates {
                    if ui
                        .selectable_label(reference.0.asset_id == draft.base.0.asset_id, label)
                        .clicked()
                    {
                        draft.base = reference.clone();
                    }
                }
            });
    });
    ui.separator();
}

fn load_live_base_asset(world: &mut World, reference: &MaterialRef) -> Option<MaterialAsset> {
    let path = reference.0.source_path.as_deref()?;
    let key = crate::asset_documents::ensure_material_document(world, path, "Base Material");
    world
        .resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
        .material(&key)
        .cloned()
}

fn material_base_candidates(world: &World) -> Vec<(String, MaterialRef)> {
    let registry = world.get_resource::<crate::asset_db::AssetRegistry>();
    let documents = world.get_resource::<crate::asset_documents::AuthoredAssetDocumentStore>();
    let mut candidates = world
        .resource::<EditorAssets>()
        .assets
        .iter()
        .filter(|asset| matches!(asset.kind, EditorAssetKind::Material))
        .filter_map(|asset| {
            let path = asset.path.as_deref()?;
            let is_material = documents
                .and_then(|store| store.snapshot_for_path(path))
                .map_or_else(
                    || MaterialAsset::load_from_path(path).is_ok(),
                    |snapshot| {
                        snapshot.key.kind
                            == crate::asset_documents::AuthoredAssetDocumentKind::Material
                    },
                );
            if !is_material {
                return None;
            }
            let record = registry.and_then(|registry| find_asset_by_path(registry, path))?;
            Some((
                asset.label.clone(),
                MaterialRef::new(
                    EditorAssetRef::new(
                        record.id.as_string(),
                        "material:source",
                        asset.label.clone(),
                    )
                    .with_source_path(path),
                ),
            ))
        })
        .collect::<Vec<_>>();
    candidates.sort_by(|left, right| natural_cmp(&left.0, &right.0));
    candidates
}

fn material_schema_for_asset(asset: &MaterialAsset) -> Option<MaterialInputSchema> {
    if asset.shader.kind != MaterialShaderKind::Custom {
        return Some(standard_lit_input_schema());
    }
    asset
        .shader_ref
        .as_ref()
        .and_then(|reference| reference.source_path.as_deref())
        .or(asset.shader.schema_path.as_deref())
        .and_then(|path| ShaderSchemaAsset::load_from_path(path).ok())
        .map(|schema| schema.schema)
}

const fn shader_kind_label(kind: MaterialShaderKind) -> &'static str {
    match kind {
        MaterialShaderKind::StandardLit => "Standard Lit",
        MaterialShaderKind::Unlit => "Unlit",
        MaterialShaderKind::Custom => "Custom Surface",
    }
}
