//! Shared native launch configuration for the game and in-process editor.

use std::path::{Path, PathBuf};

use bevy::app::PluginGroupBuilder;
use bevy::asset::AssetPlugin;
use bevy::log::{LogPlugin, DEFAULT_FILTER};
use bevy::prelude::*;
use bevy::window::{CompositeAlphaMode, PresentMode, WindowMode, WindowPlugin};

/// Runtime switch for the HDR camera pass.
///
/// Set to `0`, `false`, `off`, or `no` to force the game/editor cameras through
/// the SDR path when debugging mixed HDR/SDR compositor issues.
pub const HDR_ENV_VAR: &str = "BEVY_FPS_HDR";

/// Locates the workspace `assets/` folder when the binary lives under `target/`.
pub fn resolve_assets_directory() -> String {
    if let Some(path) = find_assets_directory(std::env::current_dir().ok()) {
        return path;
    }
    if let Ok(exe) = std::env::current_exe() {
        let mut dir = exe.parent().map(Path::to_path_buf);
        while let Some(current) = dir {
            if let Some(path) = find_assets_directory(Some(current.clone())) {
                return path;
            }
            dir = current.parent().map(Path::to_path_buf);
        }
    }
    "assets".into()
}

fn find_assets_directory(start: Option<PathBuf>) -> Option<String> {
    let start = start?;
    let assets = start.join("assets");
    assets
        .is_dir()
        .then(|| assets.to_string_lossy().replace('\\', "/"))
}

pub fn default_plugins(title: impl Into<String>) -> PluginGroupBuilder {
    DefaultPlugins
        .set(LogPlugin {
            // Blacksite intentionally replaces Bevy's `.scn.ron` loader with a
            // schema-aware loader. Keep the expected registration warning out
            // of normal startup logs; RUST_LOG still overrides this filter.
            filter: format!("{DEFAULT_FILTER}bevy_asset::server::loaders=error"),
            ..default()
        })
        .set(WindowPlugin {
            primary_window: Some(primary_window(title)),
            ..default()
        })
        .set(AssetPlugin {
            file_path: resolve_assets_directory(),
            ..default()
        })
}

pub fn primary_window(title: impl Into<String>) -> Window {
    Window {
        title: title.into(),
        name: Some("bevy-fps-foundation".into()),
        present_mode: PresentMode::AutoVsync,
        mode: WindowMode::Windowed,
        transparent: false,
        // Force an opaque surface. On Wayland/Hyprland the default `Auto` mode
        // can select an alpha-respecting surface, which may composite the whole
        // window as transparent on SDR outputs in mixed HDR/SDR setups.
        composite_alpha_mode: CompositeAlphaMode::Opaque,
        ..default()
    }
}

pub fn hdr_enabled_from_env() -> bool {
    std::env::var(HDR_ENV_VAR)
        .map(|value| {
            !matches!(
                value.trim().to_ascii_lowercase().as_str(),
                "0" | "false" | "off" | "no"
            )
        })
        .unwrap_or(true)
}

#[cfg(test)]
mod tests {
    use super::{primary_window, resolve_assets_directory};
    use bevy::window::CompositeAlphaMode;

    #[test]
    fn primary_window_is_opaque() {
        let window = primary_window("test");

        assert!(!window.transparent);
        assert_eq!(window.composite_alpha_mode, CompositeAlphaMode::Opaque);
    }

    #[test]
    fn resolve_assets_directory_finds_workspace_assets() {
        let cwd = std::env::current_dir().expect("cwd");
        let expected = cwd.join("assets");
        if !expected.is_dir() {
            return;
        }
        assert_eq!(resolve_assets_directory(), expected.to_string_lossy());
    }
}
