//! Asset browser dock panel: project tree, file grid/list, and asset details.

use std::collections::{BTreeMap, HashSet};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::SystemTime;

use bevy::prelude::*;
use bevy_egui::egui;
use bevy_inspector_egui::bevy_inspector::hierarchy::SelectedEntities;
use egui_phosphor_icons::{icons, Icon};
use shared::{
    AssetRegistryDocument, EditorAssetRef, MaterialAsset, MaterialDesc, MaterialInputSet,
    MaterialInstanceAsset, MaterialRef, MaterialTextureBinding, NormalMapConvention,
    TextureAssetSemantic, TextureChannel, TextureColorSpace, TextureCompression, TextureFilter,
    TextureImportSettings, TextureMipmapMode, TextureWrap,
};

use crate::assets::{
    AssetSelection, AssetSubAssetKind, EditorAsset, EditorAssetKind, EditorAssets,
    ExternalMoveRepairChoice, PendingExternalMoveRepair, ASSETS_ROOT, BUILTINS_FOLDER,
};
use crate::project::collaboration::{file_status_indicator_ui, CollaborationState};
use crate::scene_io::{SceneIo, SceneIoRequest};

use super::state::{
    AssetBrowserUiState, AssetBrowserView, AssetDeleteRequest, AssetKindFilter, AssetSort,
    ContentBrowserSnapshot, ContentClipboard, ContentPathEdit, ContentPathEditKind,
    ContentUndoAction, ContentUndoEntry, GltfMaterialExtractionDraft, GltfMaterialExtractionEntry,
    GltfMaterialExtractionTarget, MaterialExtractionWriteMode, PbrGroupingDraft, PbrTextureDraft,
    PendingContentTransaction, ASSET_DETAILS_DEFAULT_WIDTH,
};
use crate::asset_db::{
    find_asset_by_path, find_asset_mut_by_path, save_registry, AssetKind, AssetRegistry,
    ImportSettings, ModelHierarchyMode, ModelMaterialSelection, ModelMaterialSlotSelection,
    ModelPlacementMode,
};
use crate::assets::animation::load_animation_manifest;
use crate::assets::operators::{
    apply_material_operator, apply_texture_operator, assign_animation_clip_operator,
    place_asset_operator, place_subasset_operator,
};
use crate::assets::static_mesh::{
    load_static_mesh_manifest, material_id_from_label, part_id_from_label,
};
use crate::assets::{
    asset_cache_key, invalidate_on_catalog_refresh, kind_icon, prefetch_folder_thumbnails,
    refresh_model_artifacts, AssetThumbnailCache, ThumbnailCacheSnapshot, ThumbnailStudio,
};
use crate::ui::asset_card::{draw_asset_card, draw_asset_status_marker, AssetCardStatus};
use crate::ui::document_status::authored_document_status_ui;
use crate::ui::theme::{
    panel_heading, ACCENT, BORDER, ELEVATED_BG, ERROR, SUCCESS, TEXT, TEXT_DIM, TEXT_MUTED,
    WARNING, WIDGET_BG,
};

const FOOTER_HEIGHT: f32 = editor_ui::content_browser::STATUS_BAR_HEIGHT;
const TOOLBAR_HEIGHT: f32 = editor_ui::content_browser::TOOLBAR_HEIGHT;
const DETAILS_MIN_PANEL_WIDTH: f32 = 980.0;
const DETAILS_MIN_WIDTH: f32 = 200.0;
const DETAILS_RESIZE_HANDLE_WIDTH: f32 = 10.0;
const TREE_MIN_PANEL_WIDTH: f32 = editor_ui::content_browser::COMPACT_BREAKPOINT;
const TREE_MIN_WIDTH: f32 = 180.0;
const TREE_MAX_WIDTH: f32 = 216.0;
const MIN_CONTENT_WIDTH: f32 = 160.0;
const COMPACT_LIST_WIDTH: f32 = 560.0;
const PHOSPHOR: &str = "phosphor-regular";

#[derive(Clone)]
struct FolderSnapshot {
    path: String,
    name: String,
    parent: Option<String>,
}

#[derive(Clone)]
struct AssetRow {
    selection: AssetSelection,
    asset: EditorAsset,
    file_size: Option<u64>,
    modified: Option<SystemTime>,
}

#[derive(Clone)]
struct EmbeddedAsset {
    selection: AssetSelection,
    label: String,
    kind: AssetSubAssetKind,
    detail: String,
    texture_path: Option<String>,
    thumbnail_key: String,
    mesh_label: Option<String>,
    material_label: Option<String>,
    requires_skinned_hierarchy: bool,
}

#[derive(Clone)]
struct ExtractedMaterialBinding {
    source_index: usize,
    path: String,
}

fn fit_width(ui: &egui::Ui, min: f32, max: f32) -> f32 {
    let available = ui.available_width().max(1.0);
    available.min(max).max(min.min(available))
}

fn icon_text(icon: Icon, size: f32) -> egui::RichText {
    egui::RichText::new(icon.as_str()).font(egui::FontId::new(
        size,
        egui::FontFamily::Name(PHOSPHOR.into()),
    ))
}

fn exact_region<R>(
    ui: &mut egui::Ui,
    size: egui::Vec2,
    layout: egui::Layout,
    add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
    let (rect, _response) = ui.allocate_exact_size(size, egui::Sense::hover());
    let mut child = ui.new_child(egui::UiBuilder::new().max_rect(rect).layout(layout));
    add_contents(&mut child)
}

fn exact_region_with_pointer<R>(
    ui: &mut egui::Ui,
    size: egui::Vec2,
    layout: egui::Layout,
    add_contents: impl FnOnce(&mut egui::Ui, bool) -> R,
) -> (R, egui::Response) {
    let (rect, _response) = ui.allocate_exact_size(size, egui::Sense::hover());
    let contains_pointer = ui
        .ctx()
        .pointer_hover_pos()
        .is_some_and(|position| rect.contains(position));
    let mut child = ui.new_child(
        egui::UiBuilder::new()
            .max_rect(rect)
            .layout(layout)
            .sense(egui::Sense::click()),
    );
    let result = add_contents(&mut child, contains_pointer);
    let response = child.response();
    (result, response)
}

fn details_width_for_layout(available_width: f32, tree_width: f32, requested: f32) -> f32 {
    let separator_budget = if tree_width > 0.0 { 10.0 } else { 0.0 };
    let max_width = (available_width
        - tree_width
        - separator_budget
        - DETAILS_RESIZE_HANDLE_WIDTH
        - MIN_CONTENT_WIDTH)
        .max(DETAILS_MIN_WIDTH);
    requested.clamp(DETAILS_MIN_WIDTH, max_width)
}

fn details_resize_handle(ui: &mut egui::Ui, height: f32) -> egui::Response {
    let rect = egui::Rect::from_min_size(
        ui.next_widget_position(),
        egui::vec2(DETAILS_RESIZE_HANDLE_WIDTH, height),
    );
    let response = ui.interact(
        rect,
        egui::Id::new("asset_details_resize_handle"),
        egui::Sense::drag(),
    );
    ui.advance_cursor_after_rect(rect);
    let stroke_color = if response.hovered() || response.dragged() {
        ACCENT
    } else {
        BORDER
    };
    ui.painter().vline(
        rect.center().x,
        rect.y_range().shrink(4.0),
        egui::Stroke::new(2.0_f32, stroke_color),
    );
    response
        .on_hover_cursor(egui::CursorIcon::ResizeHorizontal)
        .on_hover_text("Drag to resize asset details; double-click to reset")
}

pub fn asset_browser_ui(
    world: &mut World,
    ui: &mut egui::Ui,
    selected_entities: &SelectedEntities,
) {
    let available_width = ui.available_width().max(1.0);
    let available_height = ui.available_height().max(1.0);
    let body_height = (available_height - TOOLBAR_HEIGHT - FOOTER_HEIGHT - 4.0).max(40.0);
    let browser_interactions_enabled = !content_browser_modal_active(world);

    exact_region(
        ui,
        egui::vec2(available_width, available_height),
        egui::Layout::top_down(egui::Align::Min),
        |ui| {
            if !browser_interactions_enabled {
                ui.disable();
            }
            exact_region(
                ui,
                egui::vec2(available_width, TOOLBAR_HEIGHT),
                egui::Layout::left_to_right(egui::Align::Center),
                |ui| {
                    asset_toolbar(world, ui);
                },
            );
            let (
                current_folder,
                selected,
                folders,
                assets,
                cache_snapshot,
                state_snapshot,
                browser_status,
                selection_count,
            ) = {
                let assets = world.resource::<EditorAssets>();
                let cache = world.resource::<AssetThumbnailCache>();
                let state = world.resource::<AssetBrowserUiState>();
                let folders: Vec<FolderSnapshot> = assets
                    .folders
                    .iter()
                    .map(|folder| FolderSnapshot {
                        path: folder.path.clone(),
                        name: folder.name.clone(),
                        parent: folder.parent.clone(),
                    })
                    .collect();
                let asset_rows: Vec<AssetRow> = assets
                    .assets
                    .iter()
                    .map(|asset| AssetRow {
                        selection: AssetSelection::from_asset(asset),
                        asset: asset.clone(),
                        file_size: asset.path.as_deref().and_then(file_size),
                        modified: asset.path.as_deref().and_then(modified_time),
                    })
                    .collect();
                (
                    assets.current_folder.clone(),
                    assets.selected.clone(),
                    folders,
                    asset_rows,
                    cache.snapshot(),
                    state_snapshot(state),
                    assets.status.clone(),
                    assets.selections.len(),
                )
            };

            prefetch_folder_thumbnails(world, &current_folder);

            let show_tree = available_width >= TREE_MIN_PANEL_WIDTH;
            let show_details =
                state_snapshot.show_details && available_width >= DETAILS_MIN_PANEL_WIDTH;
            let tree_width = if show_tree {
                (available_width * 0.18).clamp(TREE_MIN_WIDTH, TREE_MAX_WIDTH)
            } else {
                0.0
            };
            let details_width = if show_details {
                details_width_for_layout(available_width, tree_width, state_snapshot.details_width)
            } else {
                0.0
            };
            let separator_budget = (show_tree as u8 as f32) * 10.0
                + (show_details as u8 as f32) * DETAILS_RESIZE_HANDLE_WIDTH;
            let content_width = (available_width - tree_width - details_width - separator_budget)
                .max(MIN_CONTENT_WIDTH.min(available_width));

            exact_region(
                ui,
                egui::vec2(available_width, body_height),
                egui::Layout::left_to_right(egui::Align::Min),
                |ui| {
                    if show_tree {
                        exact_region(
                            ui,
                            egui::vec2(tree_width, body_height),
                            egui::Layout::top_down(egui::Align::Min),
                            |ui| {
                                ui.label(
                                    egui::RichText::new("SOURCES")
                                        .small()
                                        .strong()
                                        .color(TEXT_MUTED),
                                );
                                egui::ScrollArea::vertical()
                                    .id_salt("asset_folder_tree")
                                    .max_height(ui.available_height())
                                    .auto_shrink([false, false])
                                    .show(ui, |ui| {
                                        for root in [BUILTINS_FOLDER, ASSETS_ROOT] {
                                            folder_tree_branch(
                                                world,
                                                ui,
                                                &folders,
                                                root,
                                                0,
                                                &current_folder,
                                            );
                                        }
                                    });
                            },
                        );
                        ui.separator();
                    }

                    let (visible_order, content_response) = exact_region_with_pointer(
                        ui,
                        egui::vec2(content_width, body_height),
                        egui::Layout::top_down(egui::Align::Min),
                        |ui, content_contains_pointer| {
                            content_header(world, ui, &current_folder, &folders, &assets);
                            egui::ScrollArea::vertical()
                                .id_salt("asset_content_area")
                                .max_height(ui.available_height())
                                .auto_shrink([false, false])
                                .show(ui, |ui| {
                                    let child_folders = child_folders_for_content(
                                        &folders,
                                        &current_folder,
                                        &state_snapshot.search,
                                    );
                                    let visible_assets =
                                        visible_assets(&assets, &current_folder, &state_snapshot);
                                    let visible_order =
                                        visible_selection_order(&child_folders, &visible_assets);
                                    handle_content_shortcuts(
                                        world,
                                        ui.ctx(),
                                        &visible_order,
                                        content_contains_pointer,
                                    );
                                    prefetch_asset_row_thumbnails(world, &visible_assets);

                                    if child_folders.is_empty() && visible_assets.is_empty() {
                                        empty_content(ui, &state_snapshot.search);
                                    } else if state_snapshot.view == AssetBrowserView::Grid {
                                        let thumbnail_size = state_snapshot
                                            .thumbnail_size
                                            .min((content_width - 26.0).max(48.0));
                                        asset_grid(
                                            world,
                                            ui,
                                            selected_entities,
                                            &selected,
                                            &child_folders,
                                            &visible_assets,
                                            &cache_snapshot,
                                            thumbnail_size,
                                            &visible_order,
                                        );
                                    } else {
                                        asset_list(
                                            world,
                                            ui,
                                            selected_entities,
                                            &selected,
                                            &child_folders,
                                            &visible_assets,
                                            &cache_snapshot,
                                            &visible_order,
                                        );
                                    }
                                    visible_order
                                })
                                .inner
                        },
                    );
                    content_empty_space(world, content_response, &current_folder, &visible_order);

                    if show_details {
                        let resize_response = details_resize_handle(ui, body_height);
                        let requested_width = if resize_response.double_clicked() {
                            Some(ASSET_DETAILS_DEFAULT_WIDTH)
                        } else if resize_response.dragged() {
                            Some(details_width - resize_response.drag_delta().x)
                        } else {
                            None
                        };
                        if let Some(requested_width) = requested_width {
                            world.resource_mut::<AssetBrowserUiState>().details_width =
                                details_width_for_layout(
                                    available_width,
                                    tree_width,
                                    requested_width,
                                );
                        }
                        exact_region(
                            ui,
                            egui::vec2(details_width, body_height),
                            egui::Layout::top_down(egui::Align::Min),
                            |ui| {
                                egui::ScrollArea::vertical()
                                    .id_salt("asset_details_area")
                                    .max_height(ui.available_height())
                                    .auto_shrink([false, false])
                                    .show(ui, |ui| {
                                        details_panel(world, ui, selected_entities);
                                    });
                            },
                        );
                    }
                },
            );

            exact_region(
                ui,
                egui::vec2(available_width, FOOTER_HEIGHT),
                egui::Layout::top_down(egui::Align::Min),
                |ui| {
                    let direct_assets = assets
                        .iter()
                        .filter(|row| row.asset.folder_path == current_folder)
                        .count();
                    let direct_folders = folders
                        .iter()
                        .filter(|folder| folder.parent.as_deref() == Some(current_folder.as_str()))
                        .count();
                    let summary = if selection_count > 0 {
                        format!("{selection_count} selected")
                    } else {
                        format!("{direct_folders} folders · {direct_assets} assets")
                    };
                    editor_ui::content_browser::content_browser_status_bar(
                        ui,
                        editor_ui::content_browser::ContentBrowserStatusViewModel {
                            summary: &summary,
                            location: &current_folder,
                            status: &browser_status,
                        },
                    );
                },
            );
        },
    );

    draw_delete_modal(world, ui.ctx());
    draw_import_to_modal(world, ui.ctx());
    draw_path_edit_modal(world, ui.ctx());
    draw_content_transaction_modal(world, ui.ctx());
    draw_gltf_material_extraction_modal(world, ui.ctx());
    draw_pbr_grouping_modal(world, ui.ctx());
    draw_trash_modal(world, ui.ctx());
    draw_external_move_repair_modal(world, ui.ctx());
}

#[path = "panel/details.rs"]
mod details;
#[path = "panel/embedded.rs"]
mod embedded;
#[path = "panel/file_operations.rs"]
mod file_operations;
#[path = "panel/grid.rs"]
mod grid;
#[path = "panel/import_settings.rs"]
mod import_settings;
#[path = "panel/list.rs"]
mod list;
#[path = "panel/material_extraction.rs"]
mod material_extraction;
#[path = "panel/navigation.rs"]
mod navigation;
#[path = "panel/pbr_transactions.rs"]
mod pbr_transactions;
#[path = "panel/toolbar_import.rs"]
mod toolbar_import;
#[path = "panel/undo_trash.rs"]
mod undo_trash;
#[path = "panel/utilities.rs"]
mod utilities;
#[path = "panel/validation.rs"]
mod validation;

use crate::ui::materials::{
    create_material_instance_from_base, material_asset_editor, material_instance_asset_editor,
};
pub(crate) use details::top_level_asset_details_panel;
use details::*;
use embedded::*;
use file_operations::*;
use grid::*;
use import_settings::*;
use list::*;
pub(crate) use material_extraction::begin_actor_material_extraction;
use material_extraction::*;
use navigation::*;
use pbr_transactions::*;
pub(crate) use toolbar_import::draw_asset_import_review_modal;
use toolbar_import::*;
use undo_trash::*;
use utilities::*;
pub(crate) use validation::validate_material_conflict_destination;

#[cfg(test)]
mod tests {
    use super::*;
    include!("panel/tests/a.rs");
    include!("panel/tests/b.rs");
}
