use super::commands::{SceneError, load_scene_binary_from_bytes};
use super::components::Scene;
use std::sync::{Arc, Mutex};

#[derive(Default)]
pub enum SceneLoadStatus {
    #[default]
    Idle,
    Loading,
    Ready(Box<Scene>),
    Failed(String),
}

fn load_scene_from_bytes(bytes: &[u8]) -> Result<Scene, SceneError> {
    if bytes.first() == Some(&b'{') {
        let mut scene: Scene = serde_json::from_slice(bytes)?;
        scene.rebuild_uuid_index();
        Ok(scene)
    } else {
        load_scene_binary_from_bytes(bytes)
    }
}

#[derive(Default)]
pub struct SceneLoadState {
    status: Arc<Mutex<SceneLoadStatus>>,
}

impl SceneLoadState {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn is_loading(&self) -> bool {
        matches!(*self.status.lock().unwrap(), SceneLoadStatus::Loading)
    }

    pub fn take_result(&self) -> SceneLoadStatus {
        std::mem::take(&mut *self.status.lock().unwrap())
    }

    #[cfg(target_arch = "wasm32")]
    pub fn load_from_url(&self, url: &str) {
        *self.status.lock().unwrap() = SceneLoadStatus::Loading;

        let status = Arc::clone(&self.status);
        let url = url.to_string();

        ehttp::fetch(
            ehttp::Request::get(&url),
            move |result: ehttp::Result<ehttp::Response>| {
                let mut state = status.lock().unwrap();
                match result {
                    Ok(response) => {
                        if response.ok {
                            match load_scene_from_bytes(&response.bytes) {
                                Ok(scene) => *state = SceneLoadStatus::Ready(Box::new(scene)),
                                Err(error) => {
                                    *state =
                                        SceneLoadStatus::Failed(format!("Parse error: {}", error))
                                }
                            }
                        } else {
                            *state = SceneLoadStatus::Failed(format!(
                                "HTTP {}: {}",
                                response.status, response.status_text
                            ));
                        }
                    }
                    Err(error) => *state = SceneLoadStatus::Failed(error),
                }
            },
        );
    }

    #[cfg(not(target_arch = "wasm32"))]
    pub fn load_from_url(&self, url: &str) {
        *self.status.lock().unwrap() = SceneLoadStatus::Loading;

        let path = std::path::Path::new(url);
        match std::fs::read(path) {
            Ok(bytes) => match load_scene_from_bytes(&bytes) {
                Ok(scene) => *self.status.lock().unwrap() = SceneLoadStatus::Ready(Box::new(scene)),
                Err(error) => {
                    *self.status.lock().unwrap() =
                        SceneLoadStatus::Failed(format!("Parse error: {}", error))
                }
            },
            Err(error) => {
                *self.status.lock().unwrap() =
                    SceneLoadStatus::Failed(format!("Read error: {}", error))
            }
        }
    }

    pub fn load_from_bytes(&self, bytes: &[u8]) {
        match load_scene_from_bytes(bytes) {
            Ok(scene) => *self.status.lock().unwrap() = SceneLoadStatus::Ready(Box::new(scene)),
            Err(error) => {
                *self.status.lock().unwrap() =
                    SceneLoadStatus::Failed(format!("Parse error: {}", error))
            }
        }
    }
}
