use std::path::PathBuf;

#[derive(Debug, Clone)]
pub struct AttachmentMetadata {
    pub attribution_info: Option<AttributionInfo>,
}

#[derive(Debug, Clone)]
pub struct AttributionInfo {
    pub width: Option<u32>,
    pub height: Option<u32>,
}

#[derive(Debug, Clone)]
pub struct Attachment {
    pub guid: String,
    pub base_path: PathBuf,
    pub metadata: Option<AttachmentMetadata>,
    pub mime_type: Option<String>,
    pub cached_full_path: Option<PathBuf>,
    pub cached_preview_path: Option<PathBuf>,
}

impl Attachment {
    pub(crate) fn preferred_extension_for_mime(mime: &str) -> Option<&'static str> {
        let normalized = mime.split(';').next().unwrap_or(mime).trim();
        // Prefer common, user-friendly extensions over obscure ones
        match normalized {
            "image/jpeg" | "image/pjpeg" => Some("jpg"),
            _ => mime_guess::get_mime_extensions_str(normalized).and_then(|list| {
                // If jpg is one of the candidates, prefer it
                if list.iter().any(|e| *e == "jpg") {
                    Some("jpg")
                } else {
                    list.first().copied()
                }
            }),
        }
    }
    pub fn get_path(&self) -> PathBuf {
        self.get_path_for_preview_scratch(false, false)
    }

    pub fn get_path_for_preview(&self, preview: bool) -> PathBuf {
        self.get_path_for_preview_scratch(preview, false)
    }

    pub fn get_path_for_preview_scratch(&self, preview: bool, scratch: bool) -> PathBuf {
        if !scratch {
            let cached = if preview {
                self.cached_preview_path.as_ref()
            } else {
                self.cached_full_path.as_ref()
            };

            if let Some(path) = cached {
                return path.clone();
            }
        }

        // Determine whether this is a preview or full attachment.
        let kind = if preview { "preview" } else { "full" };

        // Fall back to constructing a path using known info. If we know the MIME type,
        // prefer an extension guessed from it; otherwise keep legacy naming.
        let ext_from_mime = self
            .mime_type
            .as_ref()
            .and_then(|m| Self::preferred_extension_for_mime(m));

        let base_ext = match ext_from_mime {
            Some(ext) => format!("{}.{}", kind, ext),
            None => kind.to_string(),
        };

        if scratch {
            self.base_path
                .with_extension(format!("{}.download", base_ext))
        } else {
            self.base_path.with_extension(base_ext)
        }
    }

    pub fn is_downloaded(&self, preview: bool) -> bool {
        let path = self.get_path_for_preview(preview);
        std::fs::exists(&path).expect(
            format!(
                "Wasn't able to check for the existence of an attachment file path at {}",
                path.display()
            )
            .as_str(),
        )
    }
}

impl From<kordophone::model::message::AttachmentMetadata> for AttachmentMetadata {
    fn from(metadata: kordophone::model::message::AttachmentMetadata) -> Self {
        Self {
            attribution_info: metadata.attribution_info.map(|info| info.into()),
        }
    }
}

impl From<kordophone::model::message::AttributionInfo> for AttributionInfo {
    fn from(info: kordophone::model::message::AttributionInfo) -> Self {
        Self {
            width: info.width,
            height: info.height,
        }
    }
}

impl From<AttachmentMetadata> for kordophone::model::message::AttachmentMetadata {
    fn from(metadata: AttachmentMetadata) -> Self {
        Self {
            attribution_info: metadata.attribution_info.map(|info| info.into()),
        }
    }
}

impl From<AttributionInfo> for kordophone::model::message::AttributionInfo {
    fn from(info: AttributionInfo) -> Self {
        Self {
            width: info.width,
            height: info.height,
        }
    }
}
