use crate::{
    kw,
    utils::{DebugPunctuated, ParseNested},
    Expr, SolIdent, Spanned,
};
use proc_macro2::Span;
use std::fmt;
use syn::{
    braced, parenthesized,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    token::{Brace, Paren},
    Result, Token,
};
#[derive(Clone, Debug)]
pub struct ExprCall {
    pub expr: Box<Expr>,
    pub args: ArgList,
}
impl ParseNested for ExprCall {
    fn parse_nested(expr: Box<Expr>, input: ParseStream<'_>) -> Result<Self> {
        Ok(Self { expr, args: input.parse()? })
    }
}
derive_parse!(ExprCall);
impl Spanned for ExprCall {
    fn span(&self) -> Span {
        let span = self.expr.span();
        span.join(self.args.span()).unwrap_or(span)
    }
    fn set_span(&mut self, span: Span) {
        self.expr.set_span(span);
        self.args.set_span(span);
    }
}
#[derive(Clone)]
pub struct ExprPayable {
    pub payable_token: kw::payable,
    pub args: ArgList,
}
impl fmt::Debug for ExprPayable {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ExprPayable").field("args", &self.args).finish()
    }
}
impl From<ExprPayable> for ExprCall {
    fn from(value: ExprPayable) -> Self {
        Self {
            expr: Box::new(Expr::Ident(SolIdent::new_spanned("payable", value.payable_token.span))),
            args: value.args,
        }
    }
}
impl Parse for ExprPayable {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        Ok(Self { payable_token: input.parse()?, args: input.parse()? })
    }
}
impl Spanned for ExprPayable {
    fn span(&self) -> Span {
        let span = self.payable_token.span;
        span.join(self.args.span()).unwrap_or(span)
    }
    fn set_span(&mut self, span: Span) {
        self.payable_token.span = span;
        self.args.set_span(span);
    }
}
#[derive(Clone)]
pub struct ArgList {
    pub paren_token: Paren,
    pub list: ArgListImpl,
}
impl fmt::Debug for ArgList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ArgList").field("list", &self.list).finish()
    }
}
impl Parse for ArgList {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let content;
        Ok(Self { paren_token: parenthesized!(content in input), list: content.parse()? })
    }
}
impl Spanned for ArgList {
    fn span(&self) -> Span {
        self.paren_token.span.join()
    }
    fn set_span(&mut self, span: Span) {
        self.paren_token = Paren(span);
    }
}
#[derive(Clone)]
pub enum ArgListImpl {
    Unnamed(Punctuated<Expr, Token![,]>),
    Named(NamedArgList),
}
impl fmt::Debug for ArgListImpl {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Unnamed(list) => {
                f.debug_tuple("Unnamed").field(DebugPunctuated::new(list)).finish()
            }
            Self::Named(list) => f.debug_tuple("Named").field(list).finish(),
        }
    }
}
impl Parse for ArgListImpl {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        if input.peek(Brace) {
            input.parse().map(Self::Named)
        } else {
            input.parse_terminated(Expr::parse, Token![,]).map(Self::Unnamed)
        }
    }
}
#[derive(Clone, Debug)]
pub struct ExprCallOptions {
    pub expr: Box<Expr>,
    pub args: NamedArgList,
}
impl ParseNested for ExprCallOptions {
    fn parse_nested(expr: Box<Expr>, input: ParseStream<'_>) -> Result<Self> {
        Ok(Self { expr, args: input.parse()? })
    }
}
derive_parse!(ExprCallOptions);
impl Spanned for ExprCallOptions {
    fn span(&self) -> Span {
        let span = self.expr.span();
        span.join(self.args.span()).unwrap_or(span)
    }
    fn set_span(&mut self, span: Span) {
        self.expr.set_span(span);
        self.args.set_span(span);
    }
}
#[derive(Clone)]
pub struct NamedArgList {
    pub brace_token: Brace,
    pub list: Punctuated<NamedArg, Token![,]>,
}
impl fmt::Debug for NamedArgList {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("NamedArgList").field("list", DebugPunctuated::new(&self.list)).finish()
    }
}
impl Parse for NamedArgList {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        let content;
        Ok(Self {
            brace_token: braced!(content in input),
            list: content.parse_terminated(NamedArg::parse, Token![,])?,
        })
    }
}
impl Spanned for NamedArgList {
    fn span(&self) -> Span {
        self.brace_token.span.join()
    }
    fn set_span(&mut self, span: Span) {
        self.brace_token = Brace(span);
    }
}
#[derive(Clone)]
pub struct NamedArg {
    pub name: SolIdent,
    pub colon_token: Token![:],
    pub arg: Expr,
}
impl fmt::Debug for NamedArg {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("NamedArg").field("name", &self.name).field("arg", &self.arg).finish()
    }
}
impl Parse for NamedArg {
    fn parse(input: ParseStream<'_>) -> Result<Self> {
        Ok(Self { name: input.parse()?, colon_token: input.parse()?, arg: input.parse()? })
    }
}
impl Spanned for NamedArg {
    fn span(&self) -> Span {
        let span = self.name.span();
        span.join(self.arg.span()).unwrap_or(span)
    }
    fn set_span(&mut self, span: Span) {
        self.name.set_span(span);
        self.arg.set_span(span);
    }
}