#[cfg(not(target_arch = "wasm32"))]
use std::path::{Path, PathBuf};
use std::{borrow::Cow, sync::Arc};

#[cfg(feature = "http")]
use myth_core::AssetError;
use myth_core::{Error, Result};

/// Asset reader trait.
/// Supports asynchronous reading of local files and network resources.
pub trait AssetReader: Send + Sync {
    /// Asynchronously read resource bytes.
    #[cfg(not(target_arch = "wasm32"))]
    fn read_bytes(&self, uri: &str) -> impl std::future::Future<Output = Result<Vec<u8>>> + Send;

    /// On WASM, Futures do not need to be `Send`.
    #[cfg(target_arch = "wasm32")]
    fn read_bytes(&self, uri: &str) -> impl std::future::Future<Output = Result<Vec<u8>>>;
}

/// Local file reader (only available on non-WASM platforms).
#[cfg(not(target_arch = "wasm32"))]
pub struct FileAssetReader {
    root_path: PathBuf,
}

#[cfg(not(target_arch = "wasm32"))]
impl FileAssetReader {
    pub fn new(path: impl AsRef<Path>) -> Self {
        let path = path.as_ref();
        let root_path = if path.is_file() {
            path.parent().unwrap_or(Path::new(".")).to_path_buf()
        } else {
            path.to_path_buf()
        };
        Self { root_path }
    }

    #[inline]
    #[must_use]
    pub fn root_path(&self) -> &Path {
        &self.root_path
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl AssetReader for FileAssetReader {
    async fn read_bytes(&self, uri: &str) -> Result<Vec<u8>> {
        let path = self.root_path.join(uri);
        let data = tokio::fs::read(&path).await?;
        Ok(data)
    }
}

/// HTTP network asset reader.
///
/// Uses [`ehttp`] for lightweight cross-platform HTTP fetching (ureq on
/// native, browser Fetch API on WASM). URL resolution is handled via the
/// [`url`] crate.
#[cfg(feature = "http")]
pub struct HttpAssetReader {
    root_url: url::Url,
}

#[cfg(feature = "http")]
impl HttpAssetReader {
    /// Creates a new reader rooted at the given URL.
    ///
    /// If `url_str` points to a file (no trailing `/`), the parent directory
    /// is used as the root so that relative URIs resolve correctly.
    pub fn new(url_str: &str) -> Result<Self> {
        let url = url::Url::parse(url_str)
            .map_err(|e| Error::Asset(AssetError::Format(format!("URL parse error: {e}"))))?;
        let root_url = if url.path().ends_with('/') {
            url
        } else {
            let mut u = url.clone();
            if let Ok(mut segments) = u.path_segments_mut() {
                segments.pop();
                segments.push("");
            }
            u
        };

        Ok(Self { root_url })
    }

    /// Returns the resolved root URL.
    #[inline]
    #[must_use]
    pub fn root_url(&self) -> &url::Url {
        &self.root_url
    }
}

#[cfg(feature = "http")]
impl AssetReader for HttpAssetReader {
    async fn read_bytes(&self, uri: &str) -> Result<Vec<u8>> {
        let url = self
            .root_url
            .join(uri)
            .map_err(|e| Error::Asset(AssetError::Format(format!("URL join error: {e}"))))?;
        let request = ehttp::Request::get(url.as_str());
        let resp = ehttp::fetch_async(request)
            .await
            .map_err(|e| Error::Asset(AssetError::Network(e)))?;
        if !resp.ok {
            return Err(Error::Asset(AssetError::HttpResponse {
                status: resp.status,
            }));
        }
        Ok(resp.bytes)
    }
}

/// Asset reader variant enum.
#[derive(Clone)]
pub enum AssetReaderVariant {
    #[cfg(not(target_arch = "wasm32"))]
    File(Arc<FileAssetReader>),
    #[cfg(feature = "http")]
    Http(Arc<HttpAssetReader>),
}

impl AssetReaderVariant {
    /// Automatically creates the appropriate reader from a path or URL.
    pub fn new(source: &impl AssetSource) -> Result<Self> {
        let uri = source.uri();

        #[cfg(not(target_arch = "wasm32"))]
        {
            if source.is_http() {
                #[cfg(feature = "http")]
                {
                    Ok(Self::Http(Arc::new(HttpAssetReader::new(&uri)?)))
                }
                #[cfg(not(feature = "http"))]
                {
                    Err(Error::Platform(
                        myth_core::PlatformError::FeatureNotEnabled("http".to_string()),
                    ))
                }
            } else {
                // If not HTTP, default to filesystem reader
                Ok(Self::File(Arc::new(FileAssetReader::new(uri.as_ref()))))
            }
        }

        #[cfg(target_arch = "wasm32")]
        {
            // WASM always uses HTTP reader
            #[cfg(not(feature = "http"))]
            {
                return Err(Error::Platform(
                    myth_core::PlatformError::FeatureNotEnabled("http".to_string()),
                ));
            }

            let full_uri = if !source.is_http() {
                let window = web_sys::window().ok_or_else(|| {
                    Error::Platform(myth_core::PlatformError::Wasm(
                        "No window found".to_string(),
                    ))
                })?;

                let location = window.location();
                let href = location.href().map_err(|e| {
                    Error::Platform(myth_core::PlatformError::Wasm(format!(
                        "Failed to get href: {:?}",
                        e
                    )))
                })?;

                let base = url::Url::parse(&href).map_err(|e| {
                    Error::Platform(myth_core::PlatformError::Wasm(format!(
                        "Invalid base URL: {}",
                        e
                    )))
                })?;

                base.join(&uri)
                    .map_err(|e| {
                        Error::Platform(myth_core::PlatformError::Wasm(format!(
                            "Failed to join URL: {}",
                            e
                        )))
                    })?
                    .to_string()
            } else {
                uri.to_string()
            };

            Ok(Self::Http(Arc::new(HttpAssetReader::new(&full_uri)?)))
        }
    }

    /// Asynchronously reads byte data.
    pub async fn read_bytes(&self, uri: &str) -> Result<Vec<u8>> {
        match self {
            #[cfg(not(target_arch = "wasm32"))]
            Self::File(r) => r.read_bytes(uri).await,
            #[cfg(feature = "http")]
            Self::Http(r) => r.read_bytes(uri).await,
        }
    }
}

pub trait AssetSource: std::fmt::Debug + Send + Sync {
    fn uri(&self) -> Cow<'_, str>;

    fn filename(&self) -> Option<Cow<'_, str>>;

    fn is_http(&self) -> bool;
}

impl AssetSource for str {
    fn uri(&self) -> Cow<'_, str> {
        Cow::Borrowed(self)
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        // Simple URL/path splitting logic
        // For URL "http://example.com/foo/bar.png" -> "bar.png"
        // For path "assets/bar.png" -> "bar.png"
        let name = self.rsplit('/').next().unwrap_or(self);
        // Handle possible URL query parameters "bar.png?v=1" -> "bar.png"
        let name = name.split('?').next().unwrap_or(name);
        if name.is_empty() {
            None
        } else {
            Some(Cow::Borrowed(name))
        }
    }

    fn is_http(&self) -> bool {
        self.starts_with("http://") || self.starts_with("https://")
    }
}

impl AssetSource for String {
    fn uri(&self) -> Cow<'_, str> {
        Cow::Borrowed(self)
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        self.as_str().filename()
    }

    fn is_http(&self) -> bool {
        self.as_str().is_http()
    }
}

impl AssetSource for &str {
    fn uri(&self) -> Cow<'_, str> {
        Cow::Borrowed(*self)
    }
    fn filename(&self) -> Option<Cow<'_, str>> {
        (**self).filename()
    }
    fn is_http(&self) -> bool {
        (**self).is_http()
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl AssetSource for Path {
    fn uri(&self) -> Cow<'_, str> {
        // Convert system path to generic URI format (forward slashes)
        Cow::Owned(self.to_string_lossy().replace('\\', "/"))
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        self.file_name().map(|s| s.to_string_lossy())
    }

    fn is_http(&self) -> bool {
        false
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl AssetSource for &Path {
    fn uri(&self) -> Cow<'_, str> {
        // Convert system path to generic URI format (forward slashes)
        Cow::Owned(self.to_string_lossy().replace('\\', "/"))
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        self.file_name().map(|s| s.to_string_lossy())
    }

    fn is_http(&self) -> bool {
        false
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl AssetSource for PathBuf {
    fn uri(&self) -> Cow<'_, str> {
        Cow::Owned(self.to_string_lossy().replace('\\', "/"))
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        self.file_name().map(|s| s.to_string_lossy())
    }

    fn is_http(&self) -> bool {
        false
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl AssetSource for &PathBuf {
    fn uri(&self) -> Cow<'_, str> {
        Cow::Owned(self.to_string_lossy().replace('\\', "/"))
    }

    fn filename(&self) -> Option<Cow<'_, str>> {
        self.file_name().map(|s| s.to_string_lossy())
    }

    fn is_http(&self) -> bool {
        false
    }
}
