use super::*;

pub(super) fn asset_toolbar(world: &mut World, ui: &mut egui::Ui) {
    let current_folder = world.resource::<EditorAssets>().current_folder.clone();
    let selection_count = world.resource::<EditorAssets>().selections.len();
    let has_editable_selection = !selected_content_paths(world).is_empty();
    let can_rename = selection_count == 1 && has_editable_selection;
    let pending_repairs = world
        .get_resource::<PendingExternalMoveRepair>()
        .and_then(|pending| pending.review.as_ref())
        .map_or(0, |review| review.conflicts.len());
    let (
        search,
        view,
        sort,
        kind_filter,
        thumbnail_size,
        recursive,
        show_details,
        can_undo,
        can_paste,
        can_go_back,
        can_go_forward,
    ) = {
        let state = world.resource::<AssetBrowserUiState>();
        (
            state.search.clone(),
            state.view,
            state.sort,
            state.kind_filter,
            state.thumbnail_size,
            state.recursive,
            state.show_details,
            !state.content_undo.is_empty(),
            state.clipboard.is_some() && current_folder != BUILTINS_FOLDER,
            !state.navigation_back.is_empty(),
            !state.navigation_forward.is_empty(),
        )
    };
    let model = editor_ui::content_browser::ContentBrowserToolbarViewModel {
        path: content_path_segments(&current_folder),
        search,
        kind_filter: kind_filter.into(),
        sort: sort.into(),
        view: view.into(),
        recursive,
        show_details,
        thumbnail_size,
        can_go_parent: current_folder.contains('/'),
        can_go_back,
        can_go_forward,
        can_edit_folder: current_folder != BUILTINS_FOLDER,
        can_rename,
        can_edit_selection: has_editable_selection,
        can_paste,
        can_undo,
        pending_repairs,
    };
    let actions = editor_ui::content_browser::content_browser_toolbar(ui, &model);
    let mut visible_results_changed = false;
    for action in actions {
        use editor_ui::content_browser::ContentBrowserToolbarAction as Action;
        match action {
            Action::Back => navigate_content_back(world),
            Action::Forward => navigate_content_forward(world),
            Action::Parent => navigate_to_parent(world),
            Action::Refresh => crate::assets::refresh_content_browser(world),
            Action::Navigate(path) => navigate_content_folder(world, path),
            Action::ImportHere => request_import_to_destination(world, current_folder.clone()),
            Action::ImportTo => begin_import_to(world),
            Action::NewFolder => begin_new_folder(world, current_folder.clone()),
            Action::Rename => begin_rename(world),
            Action::Duplicate => duplicate_selection(world),
            Action::Undo => undo_last_content_operation(world),
            Action::Cut => set_content_clipboard(world, true),
            Action::Copy => set_content_clipboard(world, false),
            Action::Paste => paste_content_clipboard(world),
            Action::OpenTrash => world.resource_mut::<AssetBrowserUiState>().show_trash = true,
            Action::ReviewMoves => {
                if let Some(review) = world
                    .resource_mut::<PendingExternalMoveRepair>()
                    .review
                    .as_mut()
                {
                    review.open = true;
                }
            }
            Action::SetSearch(search) => {
                world.resource_mut::<AssetBrowserUiState>().search = search;
                visible_results_changed = true;
            }
            Action::SetKindFilter(filter) => {
                world.resource_mut::<AssetBrowserUiState>().kind_filter = filter.into();
                visible_results_changed = true;
            }
            Action::SetSort(sort) => world.resource_mut::<AssetBrowserUiState>().sort = sort.into(),
            Action::SetView(view) => world.resource_mut::<AssetBrowserUiState>().view = view.into(),
            Action::SetRecursive(recursive) => {
                world.resource_mut::<AssetBrowserUiState>().recursive = recursive;
                visible_results_changed = true;
            }
            Action::SetShowDetails(show) => {
                world.resource_mut::<AssetBrowserUiState>().show_details = show;
            }
            Action::SetThumbnailSize(size) => {
                world.resource_mut::<AssetBrowserUiState>().thumbnail_size = size;
            }
        }
    }
    clear_selection_when_visible_results_change(world, visible_results_changed);
}

fn content_path_segments(
    current_folder: &str,
) -> Vec<editor_ui::content_browser::ContentPathSegment> {
    let mut segments = Vec::new();
    let mut current = String::new();
    for part in current_folder.split('/').filter(|part| !part.is_empty()) {
        if !current.is_empty() {
            current.push('/');
        }
        current.push_str(part);
        segments.push(editor_ui::content_browser::ContentPathSegment {
            path: current.clone(),
            label: if current == BUILTINS_FOLDER {
                "Built-ins".into()
            } else {
                part.to_string()
            },
        });
    }
    segments
}

impl From<AssetBrowserView> for editor_ui::content_browser::ContentBrowserViewMode {
    fn from(value: AssetBrowserView) -> Self {
        match value {
            AssetBrowserView::Grid => Self::Grid,
            AssetBrowserView::List => Self::List,
        }
    }
}

impl From<editor_ui::content_browser::ContentBrowserViewMode> for AssetBrowserView {
    fn from(value: editor_ui::content_browser::ContentBrowserViewMode) -> Self {
        match value {
            editor_ui::content_browser::ContentBrowserViewMode::Grid => Self::Grid,
            editor_ui::content_browser::ContentBrowserViewMode::List => Self::List,
        }
    }
}

impl From<AssetSort> for editor_ui::content_browser::ContentBrowserSort {
    fn from(value: AssetSort) -> Self {
        match value {
            AssetSort::Name => Self::Name,
            AssetSort::Kind => Self::Kind,
            AssetSort::Modified => Self::Modified,
            AssetSort::Size => Self::Size,
        }
    }
}

impl From<editor_ui::content_browser::ContentBrowserSort> for AssetSort {
    fn from(value: editor_ui::content_browser::ContentBrowserSort) -> Self {
        match value {
            editor_ui::content_browser::ContentBrowserSort::Name => Self::Name,
            editor_ui::content_browser::ContentBrowserSort::Kind => Self::Kind,
            editor_ui::content_browser::ContentBrowserSort::Modified => Self::Modified,
            editor_ui::content_browser::ContentBrowserSort::Size => Self::Size,
        }
    }
}

impl From<AssetKindFilter> for editor_ui::content_browser::ContentBrowserKindFilter {
    fn from(value: AssetKindFilter) -> Self {
        match value {
            AssetKindFilter::All => Self::All,
            AssetKindFilter::Model => Self::Model,
            AssetKindFilter::Texture => Self::Texture,
            AssetKindFilter::Material => Self::Material,
            AssetKindFilter::Audio => Self::Audio,
            AssetKindFilter::Level => Self::Level,
            AssetKindFilter::Prefab => Self::Prefab,
            AssetKindFilter::Builtin => Self::Builtin,
        }
    }
}

impl From<editor_ui::content_browser::ContentBrowserKindFilter> for AssetKindFilter {
    fn from(value: editor_ui::content_browser::ContentBrowserKindFilter) -> Self {
        match value {
            editor_ui::content_browser::ContentBrowserKindFilter::All => Self::All,
            editor_ui::content_browser::ContentBrowserKindFilter::Model => Self::Model,
            editor_ui::content_browser::ContentBrowserKindFilter::Texture => Self::Texture,
            editor_ui::content_browser::ContentBrowserKindFilter::Material => Self::Material,
            editor_ui::content_browser::ContentBrowserKindFilter::Audio => Self::Audio,
            editor_ui::content_browser::ContentBrowserKindFilter::Level => Self::Level,
            editor_ui::content_browser::ContentBrowserKindFilter::Prefab => Self::Prefab,
            editor_ui::content_browser::ContentBrowserKindFilter::Builtin => Self::Builtin,
        }
    }
}

pub(super) fn clear_selection_when_visible_results_change(world: &mut World, changed: bool) {
    if changed && !world.resource::<EditorAssets>().selections.is_empty() {
        let mut assets = world.resource_mut::<EditorAssets>();
        assets.clear_selection();
        assets.status = "Selection cleared because visible results changed".into();
    }
}

pub(super) fn request_import_to_destination(world: &mut World, destination: String) {
    let destination = if destination == BUILTINS_FOLDER {
        ASSETS_ROOT.to_string()
    } else {
        destination
    };
    world.resource_mut::<SceneIo>().request = Some(SceneIoRequest::ImportAssets { destination });
}

pub(super) fn begin_import_to(world: &mut World) {
    let current = world.resource::<EditorAssets>().current_folder.clone();
    let destination = if current == BUILTINS_FOLDER {
        ASSETS_ROOT.to_string()
    } else {
        current
    };
    world
        .resource_mut::<AssetBrowserUiState>()
        .pending_import_destination = Some(destination);
}

pub(super) fn draw_import_to_modal(world: &mut World, context: &egui::Context) {
    let Some(selected) = world
        .resource::<AssetBrowserUiState>()
        .pending_import_destination
        .clone()
    else {
        return;
    };
    let mut folders = world
        .resource::<EditorAssets>()
        .folders
        .iter()
        .filter(|folder| {
            folder.path == ASSETS_ROOT || folder.path.starts_with(&format!("{ASSETS_ROOT}/"))
        })
        .map(|folder| (folder.path.clone(), folder.name.clone()))
        .collect::<Vec<_>>();
    folders.sort_by(|left, right| left.0.cmp(&right.0));
    let mut action = context
        .input(|input| input.key_pressed(egui::Key::Escape))
        .then_some(false);
    let mut next_selection = None;
    egui::Window::new("Import To Project Folder")
        .id(egui::Id::new("asset_browser_import_to"))
        .collapsible(false)
        .resizable(true)
        .default_width(460.0)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .show(context, |ui| {
            ui.label("Choose the destination beneath the managed project content root.");
            ui.add_space(8.0);
            egui::ScrollArea::vertical()
                .id_salt("asset_browser_import_to_folders")
                .max_height(320.0)
                .auto_shrink([false, false])
                .show(ui, |ui| {
                    for (path, name) in &folders {
                        let depth = path.matches('/').count();
                        ui.horizontal(|ui| {
                            ui.add_space(depth.saturating_sub(1) as f32 * 14.0);
                            if ui
                                .selectable_label(path == &selected, format!("{name}  —  {path}"))
                                .clicked()
                            {
                                next_selection = Some(path.clone());
                            }
                        });
                    }
                });
            ui.add_space(8.0);
            ui.label(format!("Destination: {selected}"));
            ui.small(
                egui::RichText::new(
                    "Imported files keep their source names here; safe dependency-relative bundle paths are preserved.",
                )
                .color(TEXT_DIM),
            );
            ui.add_space(12.0);
            ui.horizontal(|ui| {
                if ui.button("Cancel").clicked() {
                    action = Some(false);
                }
                if ui.button("Choose Files...").clicked() {
                    action = Some(true);
                }
            });
        });
    if let Some(next_selection) = next_selection {
        world
            .resource_mut::<AssetBrowserUiState>()
            .pending_import_destination = Some(next_selection);
    }
    match action {
        Some(true) => {
            world
                .resource_mut::<AssetBrowserUiState>()
                .pending_import_destination = None;
            request_import_to_destination(world, selected);
        }
        Some(false) => {
            world
                .resource_mut::<AssetBrowserUiState>()
                .pending_import_destination = None;
        }
        None => {}
    }
}

pub(crate) fn draw_asset_import_review_modal(world: &mut World, context: &egui::Context) {
    let Some(pending) = world
        .resource::<crate::scene_io::PendingAssetImportReview>()
        .0
        .clone()
    else {
        return;
    };
    let plan = &pending.plan;
    let mut action = context
        .input(|input| input.key_pressed(egui::Key::Escape))
        .then_some(false);
    egui::Window::new("Review Asset Import")
        .id(egui::Id::new("asset_import_transaction_review"))
        .collapsible(false)
        .resizable(true)
        .default_width(640.0)
        .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO)
        .show(context, |ui| {
            ui.label(format!("Destination: {}", plan.destination.display()));
            ui.small(
                egui::RichText::new(
                    "Every source dependency and final project path is fingerprinted before the batch publishes.",
                )
                .color(TEXT_DIM),
            );
            ui.add_space(8.0);
            egui::ScrollArea::vertical()
                .id_salt("asset_import_transaction_entries")
                .max_height(360.0)
                .auto_shrink([false, false])
                .show(ui, |ui| {
                    for entry in &plan.entries {
                        egui::Frame::new()
                            .fill(WIDGET_BG.linear_multiply(0.8))
                            .stroke(egui::Stroke::new(1.0_f32, BORDER))
                            .corner_radius(egui::CornerRadius::same(4))
                            .inner_margin(egui::Margin::symmetric(8, 6))
                            .show(ui, |ui| {
                                ui.label(egui::RichText::new(entry.source.display().to_string()).strong());
                                if entry.adopt_in_place {
                                    ui.small("Adopt existing project file in place");
                                } else {
                                    for target in &entry.targets {
                                        ui.small(format!("→ {}", target.display()));
                                    }
                                }
                                if entry.source_files.len() > 1 {
                                    ui.small(
                                        egui::RichText::new(format!(
                                            "{} source bundle files",
                                            entry.source_files.len()
                                        ))
                                        .color(TEXT_DIM),
                                    );
                                }
                            });
                        ui.add_space(4.0);
                    }
                });
            if !plan.conflicts.is_empty() {
                ui.add_space(8.0);
                ui.colored_label(egui::Color32::LIGHT_RED, "Resolve these collisions before import:");
                for conflict in &plan.conflicts {
                    ui.colored_label(egui::Color32::LIGHT_RED, format!("• {conflict}"));
                }
            }
            ui.add_space(12.0);
            ui.horizontal(|ui| {
                if ui.button("Cancel").clicked() {
                    action = Some(false);
                }
                if ui
                    .add_enabled(
                        plan.can_commit(),
                        egui::Button::new(format!("Import {} Asset(s)", plan.imported_count())),
                    )
                    .clicked()
                {
                    action = Some(true);
                }
            });
        });
    match action {
        Some(false) => {
            world
                .resource_mut::<crate::scene_io::PendingAssetImportReview>()
                .0 = None;
            world.resource_mut::<SceneIo>().status = pending.previous_status;
        }
        Some(true) => match commit_reviewed_asset_import(world, plan) {
            Ok(count) => {
                world
                    .resource_mut::<crate::scene_io::PendingAssetImportReview>()
                    .0 = None;
                world.resource_mut::<EditorAssets>().refresh();
                invalidate_on_catalog_refresh(world);
                world.resource_mut::<SceneIo>().status = format!("Imported {count} asset(s)");
            }
            Err(error) => {
                world.resource_mut::<SceneIo>().status = format!("Import failed: {error}");
            }
        },
        None => {}
    }
}

pub(super) fn commit_reviewed_asset_import(
    world: &mut World,
    plan: &crate::assets::ExternalAssetImportPlan,
) -> Result<usize, String> {
    let previous = world.resource::<AssetRegistry>().document();
    let imported_paths = plan
        .entries
        .iter()
        .flat_map(|entry| entry.targets.iter())
        .map(|path| path.to_string_lossy().replace('\\', "/"))
        .collect::<HashSet<_>>();
    let mut files_committed = false;
    let mut publication_backups: Vec<(PathBuf, Option<Vec<u8>>)> = Vec::new();

    crate::assets::begin_content_watch_transaction(world);
    let result: Result<(usize, AssetRegistryDocument), String> = (|| {
        let count = crate::assets::commit_external_assets_import(plan)?;
        files_committed = true;

        let mut processed = content_pipeline::scan_project(&plan.project_root, &previous)?;
        let mut model_plans = Vec::new();
        for record in processed.registry.records.iter_mut().filter(|record| {
            record.kind == AssetKind::Model && imported_paths.contains(&record.path)
        }) {
            let model_plan = crate::assets::plan_model_artifacts_at(&plan.project_root, record)
                .map_err(|error| {
                    format!("could not process imported model {}: {error}", record.path)
                })?;
            *record = model_plan.record.clone();
            model_plans.push(model_plan);
        }
        processed.runtime_catalog = shared::RuntimeContentCatalog::from(&processed.registry);
        let mut texture_plans = Vec::new();
        for record in processed
            .registry
            .records
            .iter()
            .filter(|record| record.kind == AssetKind::Texture)
        {
            let texture_plan = content_pipeline::plan_texture_artifact(&plan.project_root, record)
                .map_err(|error| format!("could not process Texture {}: {error}", record.path))?;
            content_pipeline::apply_texture_plan_to_catalog(
                &mut processed.runtime_catalog,
                &record.id,
                &texture_plan,
            )?;
            texture_plans.push(texture_plan);
        }
        let mut material_plans = Vec::new();
        for record in processed.registry.records.iter().filter(|record| {
            matches!(
                record.kind,
                AssetKind::Material | AssetKind::MaterialInstance
            )
        }) {
            if let Some(material_plan) = content_pipeline::plan_material_artifact(
                &plan.project_root,
                record,
                &processed.registry,
            )? {
                content_pipeline::apply_material_plan_to_catalog(
                    &mut processed.runtime_catalog,
                    &record.id,
                    &material_plan,
                )?;
                material_plans.push(material_plan);
            }
        }

        let mut publication_paths = model_plans
            .iter()
            .flat_map(|model_plan| {
                [
                    model_plan.static_path.clone(),
                    model_plan.animation_path.clone(),
                ]
            })
            .collect::<Vec<_>>();
        publication_paths.extend(
            texture_plans
                .iter()
                .map(|texture_plan| texture_plan.output_path.clone()),
        );
        publication_paths.extend(
            material_plans
                .iter()
                .map(|material_plan| material_plan.output_path.clone()),
        );
        publication_paths.push(plan.project_root.join(content_pipeline::REGISTRY_PATH));
        publication_paths.push(
            plan.project_root
                .join(content_pipeline::RUNTIME_CATALOG_PATH),
        );
        publication_paths.sort();
        publication_paths.dedup();
        publication_backups = publication_paths
            .iter()
            .map(|path| (path.clone(), fs::read(path).ok()))
            .collect();

        for model_plan in &model_plans {
            crate::assets::publish_model_artifacts(model_plan)?;
        }
        for texture_plan in &texture_plans {
            content_pipeline::publish_texture_artifact(texture_plan)?;
        }
        for material_plan in &material_plans {
            content_pipeline::publish_material_artifact(material_plan)?;
        }
        content_pipeline::publish_content_documents_with_catalog(
            &plan.project_root,
            &processed.registry,
            &processed.runtime_catalog,
        )?;
        Ok((count, processed.registry))
    })();

    let result = match result {
        Ok((count, document)) => {
            apply_registry_document(world, document);
            Ok(count)
        }
        Err(error) => {
            for (path, bytes) in publication_backups.iter().rev() {
                restore_import_publication_file(path, bytes.as_deref());
            }
            let rollback_error = files_committed
                .then(|| crate::assets::rollback_external_assets_import(plan))
                .and_then(Result::err);
            Err(match rollback_error {
                Some(rollback_error) => format!(
                    "import processing failed: {error}; file rollback was incomplete: {rollback_error}"
                ),
                None => format!("import processing rolled back: {error}"),
            })
        }
    };
    crate::assets::end_content_watch_transaction(world);
    result
}

pub(super) fn restore_import_publication_file(path: &Path, bytes: Option<&[u8]>) {
    if let Some(bytes) = bytes {
        let _ = fs::write(path, bytes);
    } else {
        let _ = fs::remove_file(path);
    }
}
