use crate::{algorithms::Properties, standards::micropub::paging};
use serde::{Deserialize, Serialize};
use std::str::FromStr;

/// The default name for a channel.
///
/// [Channels](https://github.com/indieweb/micropub-extensions/issues/40) are an experimental
/// feature.
pub const DEFAULT_NAME: &str = "default";

/// Provides a uniform way to represent a channel.
///
/// [Channels](https://github.com/indieweb/micropub-extensions/issues/40) are an experimental
/// feature being proposed to provide a means of organizing content stored in a Micropub server.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum Form {
    Expanded {
        uid: String,
        name: String,

        #[serde(flatten, default)]
        properties: Properties,
    },
    Simple(String),
}

#[allow(clippy::derived_hash_with_manual_eq)]
impl std::hash::Hash for Form {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        std::mem::discriminant(self).hash(state);
    }
}

impl Default for Form {
    fn default() -> Self {
        Self::Simple(DEFAULT_NAME.to_owned())
    }
}

impl Form {
    pub fn name(&self) -> String {
        match self {
            Self::Simple(name) => name.to_owned(),
            Self::Expanded { name, .. } => name.to_owned(),
        }
    }

    pub fn uid(&self) -> String {
        match self {
            Self::Simple(name) => name.to_owned(),
            Self::Expanded { uid, .. } => uid.to_owned(),
        }
    }
}

impl std::fmt::Display for Form {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.uid())
    }
}

impl FromStr for Form {
    type Err = crate::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self::Simple(s.to_string()))
    }
}

/// A representation of what a Micropub server would response to a query of channels.
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct QueryResponse {
    pub channels: Vec<Form>,

    #[serde(default, flatten, rename = "paging")]
    pub paging: paging::Fields,
}
