use crate::{kw, Modifier, Mutability, Override, SolPath, Spanned, VariableAttribute, Visibility};
use proc_macro2::Span;
use std::{
    fmt,
    ops::{Deref, DerefMut},
};
use syn::{
    ext::IdentExt,
    parse::{Parse, ParseStream},
    token::Brace,
    Error, Ident, Result, Token,
};
#[derive(Clone, Default, PartialEq, Eq)]
pub struct FunctionAttributes(pub Vec<FunctionAttribute>);
impl fmt::Display for FunctionAttributes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (i, attr) in self.0.iter().enumerate() {
            if i > 0 {
                f.write_str(" ")?;
            }
            write!(f, "{attr}")?;
        }
        Ok(())
    }
}
impl fmt::Debug for FunctionAttributes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}
impl Deref for FunctionAttributes {
    type Target = Vec<FunctionAttribute>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl DerefMut for FunctionAttributes {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
impl Parse for FunctionAttributes {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let mut attributes = Vec::<FunctionAttribute>::new();
        while !input.is_empty() && !input.peek(kw::returns) && input.peek(Ident::peek_any) {
            let attr: FunctionAttribute = input.parse()?;
            if matches!(
                attr,
                FunctionAttribute::Visibility(_)
                    | FunctionAttribute::Mutability(_)
                    | FunctionAttribute::Virtual(_)
            ) {
                if let Some(prev) = attributes.iter().find(|a| **a == attr) {
                    let mut e = Error::new(attr.span(), "duplicate attribute");
                    e.combine(Error::new(prev.span(), "previous declaration is here"));
                    return Err(e);
                }
            }
            attributes.push(attr);
        }
        Ok(Self(attributes))
    }
}
impl Spanned for FunctionAttributes {
    fn span(&self) -> Span {
        self.0.span()
    }
    fn set_span(&mut self, span: Span) {
        self.0.set_span(span);
    }
}
impl FunctionAttributes {
    #[inline]
    pub fn new() -> Self {
        Self(Vec::new())
    }
    pub fn visibility(&self) -> Option<Visibility> {
        self.0.iter().find_map(FunctionAttribute::visibility)
    }
    pub fn mutability(&self) -> Option<Mutability> {
        self.0.iter().find_map(FunctionAttribute::mutability)
    }
    pub fn r#override(&self) -> Option<&Override> {
        self.0.iter().find_map(FunctionAttribute::r#override)
    }
    pub fn modifier(&self) -> Option<&Modifier> {
        self.0.iter().find_map(FunctionAttribute::modifier)
    }
    pub fn has_external(&self) -> bool {
        self.0.iter().any(FunctionAttribute::is_external)
    }
    pub fn has_internal(&self) -> bool {
        self.0.iter().any(FunctionAttribute::is_internal)
    }
    pub fn has_private(&self) -> bool {
        self.0.iter().any(FunctionAttribute::is_private)
    }
    pub fn has_public(&self) -> bool {
        self.0.iter().any(FunctionAttribute::is_public)
    }
    pub fn has_virtual(&self) -> bool {
        self.0.iter().any(FunctionAttribute::is_virtual)
    }
    pub fn has_override(&self, path: Option<&SolPath>) -> bool {
        self.0.iter().any(|attr| attr.is_override(path))
    }
    pub fn has_modifier(&self, path: Option<&SolPath>) -> bool {
        self.0.iter().any(|attr| attr.is_modifier(path))
    }
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum FunctionAttribute {
    Visibility(Visibility),
    Mutability(Mutability),
    Modifier(Modifier),
    Virtual(Token![virtual]),
    Override(Override),
}
impl fmt::Display for FunctionAttribute {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Visibility(visibility) => visibility.fmt(f),
            Self::Mutability(mutability) => mutability.fmt(f),
            Self::Virtual(_) => f.write_str("virtual"),
            Self::Override(o) => o.fmt(f),
            Self::Modifier(modifier) => modifier.fmt(f),
        }
    }
}
impl fmt::Debug for FunctionAttribute {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Visibility(visibility) => f.debug_tuple("Visibility").field(visibility).finish(),
            Self::Mutability(mutability) => f.debug_tuple("Mutability").field(mutability).finish(),
            Self::Virtual(_) => f.write_str("Virtual"),
            Self::Override(o) => o.fmt(f),
            Self::Modifier(modifier) => modifier.fmt(f),
        }
    }
}
impl Parse for FunctionAttribute {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let lookahead = input.lookahead1();
        if Visibility::peek(&lookahead) {
            input.parse().map(Self::Visibility)
        } else if Mutability::peek(&lookahead) {
            input.parse().map(Self::Mutability)
        } else if lookahead.peek(Token![virtual]) {
            input.parse().map(Self::Virtual)
        } else if lookahead.peek(Token![override]) {
            input.parse().map(Self::Override)
        } else if !input.peek(kw::returns) && lookahead.peek(Ident::peek_any) {
            input.parse().map(Self::Modifier)
        } else if input.peek(Brace) {
            Err(input.error("functions cannot have an implementation"))
        } else {
            Err(lookahead.error())
        }
    }
}
impl From<VariableAttribute> for FunctionAttribute {
    fn from(value: VariableAttribute) -> Self {
        match value {
            VariableAttribute::Visibility(v) => Self::Visibility(v),
            VariableAttribute::Constant(c) => Self::Mutability(Mutability::new_pure(c.span)),
            VariableAttribute::Immutable(i) => Self::Mutability(Mutability::new_view(i.span)),
            VariableAttribute::Override(o) => Self::Override(o),
        }
    }
}
impl Spanned for FunctionAttribute {
    fn span(&self) -> Span {
        match self {
            Self::Visibility(v) => v.span(),
            Self::Mutability(m) => m.span(),
            Self::Virtual(v) => v.span,
            Self::Override(o) => o.span(),
            Self::Modifier(m) => m.span(),
        }
    }
    fn set_span(&mut self, span: Span) {
        match self {
            Self::Visibility(v) => v.set_span(span),
            Self::Mutability(m) => m.set_span(span),
            Self::Virtual(v) => v.span = span,
            Self::Override(o) => o.set_span(span),
            Self::Modifier(m) => m.set_span(span),
        }
    }
}
impl FunctionAttribute {
    #[inline]
    pub const fn visibility(&self) -> Option<Visibility> {
        match self {
            Self::Visibility(v) => Some(*v),
            _ => None,
        }
    }
    #[inline]
    pub const fn mutability(&self) -> Option<Mutability> {
        match self {
            Self::Mutability(m) => Some(*m),
            _ => None,
        }
    }
    #[inline]
    pub const fn r#override(&self) -> Option<&Override> {
        match self {
            Self::Override(o) => Some(o),
            _ => None,
        }
    }
    #[inline]
    pub const fn modifier(&self) -> Option<&Modifier> {
        match self {
            Self::Modifier(m) => Some(m),
            _ => None,
        }
    }
    #[inline]
    pub const fn is_external(&self) -> bool {
        matches!(self, Self::Visibility(Visibility::External(_)))
    }
    #[inline]
    pub const fn is_public(&self) -> bool {
        matches!(self, Self::Visibility(Visibility::Public(_)))
    }
    #[inline]
    pub const fn is_internal(&self) -> bool {
        matches!(self, Self::Visibility(Visibility::Internal(_)))
    }
    #[inline]
    pub const fn is_private(&self) -> bool {
        matches!(self, Self::Visibility(Visibility::Private(_)))
    }
    #[inline]
    pub const fn is_virtual(&self) -> bool {
        matches!(self, Self::Virtual(_))
    }
    #[inline]
    pub fn is_override(&self, path: Option<&SolPath>) -> bool {
        self.r#override().map_or(false, |o| match path {
            Some(path) => o.paths.iter().any(|p| p == path),
            None => true,
        })
    }
    #[inline]
    pub fn is_modifier(&self, path: Option<&SolPath>) -> bool {
        self.modifier().map_or(false, |m| match path {
            Some(path) => m.name == *path,
            None => true,
        })
    }
}