use std::marker::PhantomData;
use crate::*;
use indexmap::IndexMap;
use http::Method;
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct PathItem {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub summary: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub get: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub put: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub post: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub options: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub head: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub patch: Option<Operation>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trace: Option<Operation>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub servers: Vec<Server>,
    #[serde(default)]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub parameters: Vec<ReferenceOr<Parameter>>,
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}
impl PathItem {
    pub fn iter(&self) -> impl Iterator<Item=(&str, &'_ Operation)> {
        vec![
            ("get", &self.get),
            ("put", &self.put),
            ("post", &self.post),
            ("delete", &self.delete),
            ("options", &self.options),
            ("head", &self.head),
            ("patch", &self.patch),
            ("trace", &self.trace),
        ]
            .into_iter()
            .filter_map(|(method, maybe_op)| maybe_op.as_ref().map(|op| (method, op)))
    }
    pub fn iter_mut(&mut self) -> impl Iterator<Item=(&str, &'_ mut Operation)> {
        vec![
            ("get", &mut self.get),
            ("put", &mut self.put),
            ("post", &mut self.post),
            ("delete", &mut self.delete),
            ("options", &mut self.options),
            ("head", &mut self.head),
            ("patch", &mut self.patch),
            ("trace", &mut self.trace),
        ]
            .into_iter()
            .filter_map(|(method, maybe_op)| maybe_op.as_mut().map(|op| (method, op)))
    }
    pub fn get(operation: Operation) -> Self {
        Self {
            get: Some(operation),
            ..PathItem::default()
        }
    }
    pub fn post(operation: Operation) -> Self {
        Self {
            post: Some(operation),
            ..PathItem::default()
        }
    }
}
impl IntoIterator for PathItem {
    type Item = (&'static str, Operation);
    type IntoIter = std::vec::IntoIter<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        vec![
            ("get", self.get),
            ("put", self.put),
            ("post", self.post),
            ("delete", self.delete),
            ("options", self.options),
            ("head", self.head),
            ("patch", self.patch),
            ("trace", self.trace),
        ]
            .into_iter()
            .filter_map(|(method, maybe_op)| maybe_op.map(|op| (method, op)))
            .collect::<Vec<_>>()
            .into_iter()
    }
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct Paths {
    #[serde(flatten, deserialize_with = "deserialize_paths")]
    pub paths: IndexMap<String, ReferenceOr<PathItem>>,
    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
    pub extensions: IndexMap<String, serde_json::Value>,
}
impl Paths {
    pub fn iter(&self) -> indexmap::map::Iter<String, ReferenceOr<PathItem>> {
        self.paths.iter()
    }
    pub fn iter_mut(&mut self) -> indexmap::map::IterMut<String, ReferenceOr<PathItem>> {
        self.paths.iter_mut()
    }
    pub fn insert(&mut self, key: String, path_item: PathItem) -> Option<ReferenceOr<PathItem>> {
        self.paths.insert(key, ReferenceOr::Item(path_item))
    }
    pub fn insert_operation(&mut self, path: String, method: Method, operation: Operation) -> Option<Operation> {
        let item = self.paths.entry(path).or_default();
        let item = item.as_mut().expect("Currently don't support references for PathItem");
        match method {
            Method::GET => item.get.replace(operation),
            Method::PUT => item.put.replace(operation),
            Method::POST => item.post.replace(operation),
            Method::DELETE => item.delete.replace(operation),
            Method::PATCH => item.patch.replace(operation),
            Method::HEAD => item.head.replace(operation),
            Method::OPTIONS => item.options.replace(operation),
            Method::TRACE => item.trace.replace(operation),
            _ => panic!("Unsupported method: {:?}", method),
        }
    }
}
impl IntoIterator for Paths {
    type Item = (String, ReferenceOr<PathItem>);
    type IntoIter = indexmap::map::IntoIter<String, ReferenceOr<PathItem>>;
    fn into_iter(self) -> Self::IntoIter {
        self.paths.into_iter()
    }
}
fn deserialize_paths<'de, D>(
    deserializer: D,
) -> Result<IndexMap<String, ReferenceOr<PathItem>>, D::Error>
    where
        D: Deserializer<'de>,
{
    deserializer.deserialize_map(PredicateVisitor(
        |key: &String| key.starts_with('/'),
        PhantomData,
    ))
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_path_item_iterators() {
        let operation = Operation::default();
        let path_item = PathItem {
            get: Some(operation.clone()),
            post: Some(operation.clone()),
            delete: Some(operation.clone()),
            ..Default::default()
        };
        let expected = vec![
            ("get", &operation),
            ("post", &operation),
            ("delete", &operation),
        ];
        assert_eq!(path_item.iter().collect::<Vec<_>>(), expected);
        let expected = vec![
            ("get", operation.clone()),
            ("post", operation.clone()),
            ("delete", operation.clone()),
        ];
        assert_eq!(path_item.into_iter().collect::<Vec<_>>(), expected);
    }
}