use std::ops::Not;

use crate::{
    assist_context::{AssistContext, Assists},
    utils::convert_param_list_to_arg_list,
};
use either::Either;
use hir::{db::HirDatabase, HasVisibility};
use ide_db::{
    assists::{AssistId, GroupLabel},
    path_transform::PathTransform,
};
use syntax::{
    ast::{
        self,
        edit::{self, AstNodeEdit},
        make, AssocItem, HasGenericParams, HasName, HasVisibility as astHasVisibility, Path,
    },
    ted::{self, Position},
    AstNode, NodeOrToken, SyntaxKind,
};

// Assist: generate_delegate_trait
//
// Generate delegate trait implementation for `StructField`s.
//
// ```
// trait SomeTrait {
//     type T;
//     fn fn_(arg: u32) -> u32;
//     fn method_(&mut self) -> bool;
// }
// struct A;
// impl SomeTrait for A {
//     type T = u32;
//
//     fn fn_(arg: u32) -> u32 {
//         42
//     }
//
//     fn method_(&mut self) -> bool {
//         false
//     }
// }
// struct B {
//     a$0: A,
// }
// ```
// ->
// ```
// trait SomeTrait {
//     type T;
//     fn fn_(arg: u32) -> u32;
//     fn method_(&mut self) -> bool;
// }
// struct A;
// impl SomeTrait for A {
//     type T = u32;
//
//     fn fn_(arg: u32) -> u32 {
//         42
//     }
//
//     fn method_(&mut self) -> bool {
//         false
//     }
// }
// struct B {
//     a: A,
// }
//
// impl SomeTrait for B {
//     type T = <A as SomeTrait>::T;
//
//     fn fn_(arg: u32) -> u32 {
//         <A as SomeTrait>::fn_(arg)
//     }
//
//     fn method_(&mut self) -> bool {
//         <A as SomeTrait>::method_( &mut self.a )
//     }
// }
// ```
pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let strukt = Struct::new(ctx.find_node_at_offset::<ast::Struct>()?)?;

    let field: Field = match ctx.find_node_at_offset::<ast::RecordField>() {
        Some(field) => Field::new(&ctx, Either::Left(field))?,
        None => {
            let field = ctx.find_node_at_offset::<ast::TupleField>()?;
            let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
            Field::new(&ctx, either::Right((field, field_list)))?
        }
    };

    strukt.delegate(field, acc, ctx);
    Some(())
}

/// A utility object that represents a struct's field.
struct Field {
    name: String,
    ty: ast::Type,
    range: syntax::TextRange,
    impls: Vec<Delegee>,
}

impl Field {
    pub(crate) fn new(
        ctx: &AssistContext<'_>,
        f: Either<ast::RecordField, (ast::TupleField, ast::TupleFieldList)>,
    ) -> Option<Field> {
        let db = ctx.sema.db;
        let name: String;
        let range: syntax::TextRange;
        let ty: ast::Type;

        let module = ctx.sema.to_module_def(ctx.file_id())?;

        match f {
            Either::Left(f) => {
                name = f.name()?.to_string();
                ty = f.ty()?;
                range = f.syntax().text_range();
            }
            Either::Right((f, l)) => {
                name = l.fields().position(|it| it == f)?.to_string();
                ty = f.ty()?;
                range = f.syntax().text_range();
            }
        };

        let hir_ty = ctx.sema.resolve_type(&ty)?;
        let type_impls = hir::Impl::all_for_type(db, hir_ty.clone());
        let mut impls = Vec::with_capacity(type_impls.len());
        let type_param = hir_ty.as_type_param(db);

        if let Some(tp) = type_param {
            for tb in tp.trait_bounds(db) {
                impls.push(Delegee::Bound(BoundCase(tb)));
            }
        };

        for imp in type_impls {
            match imp.trait_(db) {
                Some(tr) => {
                    if tr.is_visible_from(db, module) {
                        impls.push(Delegee::Impls(ImplCase(tr, imp)))
                    }
                }
                None => (),
            }
        }

        Some(Field { name, ty, range, impls })
    }
}

/// A field that we want to delegate can offer the enclosing struct
/// trait to implement in two ways. The first way is when the field
/// actually implements the trait and the second way is when the field
/// has a bound type parameter. We handle these cases in different ways
/// hence the enum.
enum Delegee {
    Bound(BoundCase),
    Impls(ImplCase),
}

struct BoundCase(hir::Trait);
struct ImplCase(hir::Trait, hir::Impl);

impl Delegee {
    fn signature(&self, db: &dyn HirDatabase) -> String {
        let mut s = String::new();

        let (Delegee::Bound(BoundCase(it)) | Delegee::Impls(ImplCase(it, _))) = self;

        for m in it.module(db).path_to_root(db).iter().rev() {
            if let Some(name) = m.name(db) {
                s.push_str(&format!("{}::", name.to_smol_str()));
            }
        }

        s.push_str(&it.name(db).to_smol_str());
        s
    }
}

/// A utility struct that is used for the enclosing struct.
struct Struct {
    strukt: ast::Struct,
    name: ast::Name,
}

impl Struct {
    pub(crate) fn new(s: ast::Struct) -> Option<Self> {
        let name = s.name()?;
        Some(Struct { name, strukt: s })
    }

    pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) {
        let db = ctx.db();
        for delegee in &field.impls {
            // FIXME :  We can omit already implemented impl_traits
            // But we don't know what the &[hir::Type] argument should look like.

            // let trait_ = match delegee {
            //     Delegee::Bound(b) => b.0,
            //     Delegee::Impls(i) => i.1,
            // };

            // if self.hir_ty.impls_trait(db, trait_, &[]) {
            //     continue;
            // }
            let signature = delegee.signature(db);
            let Some(delegate) = generate_impl(ctx, self, &field.ty, &field.name, delegee) else {
                continue;
            };

            acc.add_group(
                &GroupLabel("Delegate trait impl for field...".to_owned()),
                AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate),
                format!("Generate delegate impl `{}` for `{}`", signature, field.name),
                field.range,
                |builder| {
                    builder.insert(
                        self.strukt.syntax().text_range().end(),
                        format!("\n\n{}", delegate.syntax()),
                    );
                },
            );
        }
    }
}

fn generate_impl(
    ctx: &AssistContext<'_>,
    strukt: &Struct,
    field_ty: &ast::Type,
    field_name: &String,
    delegee: &Delegee,
) -> Option<ast::Impl> {
    let delegate: ast::Impl;
    let source: ast::Impl;
    let genpar: Option<ast::GenericParamList>;
    let db = ctx.db();
    let base_path = make::path_from_text(&field_ty.to_string().as_str());
    let s_path = make::ext::ident_path(&strukt.name.to_string());

    match delegee {
        Delegee::Bound(delegee) => {
            let in_file = ctx.sema.source(delegee.0.to_owned())?;
            let source: ast::Trait = in_file.value;

            delegate = make::impl_trait(
                delegee.0.is_unsafe(db),
                None,
                None,
                strukt.strukt.generic_param_list(),
                None,
                delegee.0.is_auto(db),
                make::ty(&delegee.0.name(db).to_smol_str()),
                make::ty_path(s_path),
                source.where_clause(),
                strukt.strukt.where_clause(),
                None,
            )
            .clone_for_update();

            genpar = source.generic_param_list();
            let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
            let gen_args: String =
                genpar.map_or_else(String::new, |params| params.to_generic_args().to_string());

            // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
            let qualified_path_type = make::path_from_text(&format!(
                "<{} as {}{}>",
                base_path.to_string(),
                delegee.0.name(db).to_smol_str(),
                gen_args.to_string()
            ));

            match source.assoc_item_list() {
                Some(ai) => {
                    ai.assoc_items()
                        .filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
                        .for_each(|item| {
                            let assoc =
                                process_assoc_item(item, qualified_path_type.clone(), &field_name);
                            if let Some(assoc) = assoc {
                                delegate_assoc_items.add_item(assoc);
                            }
                        });
                }
                None => {}
            };

            let target = ctx.sema.scope(strukt.strukt.syntax())?;
            let source = ctx.sema.scope(source.syntax())?;

            let transform =
                PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone());
            transform.apply(&delegate.syntax());
        }
        Delegee::Impls(delegee) => {
            let in_file = ctx.sema.source(delegee.1.to_owned())?;
            source = in_file.value;
            delegate = make::impl_trait(
                delegee.0.is_unsafe(db),
                source.generic_param_list(),
                None,
                None,
                None,
                delegee.0.is_auto(db),
                make::ty(&delegee.0.name(db).to_smol_str()),
                make::ty_path(s_path),
                source.where_clause(),
                strukt.strukt.where_clause(),
                None,
            )
            .clone_for_update();
            genpar = source.generic_param_list();
            let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
            let gen_args: String =
                genpar.map_or_else(String::new, |params| params.to_generic_args().to_string());

            // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
            let qualified_path_type = make::path_from_text(&format!(
                "<{} as {}{}>",
                base_path.to_string().as_str(),
                delegee.0.name(db).to_smol_str(),
                gen_args.to_string().as_str()
            ));

            source
                .get_or_create_assoc_item_list()
                .assoc_items()
                .filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
                .for_each(|item| {
                    let assoc = process_assoc_item(item, qualified_path_type.clone(), &field_name);
                    if let Some(assoc) = assoc {
                        delegate_assoc_items.add_item(assoc);
                    }
                });

            let target = ctx.sema.scope(strukt.strukt.syntax())?;
            let source = ctx.sema.scope(source.syntax())?;

            let transform =
                PathTransform::trait_impl(&target, &source, delegee.0, delegate.clone());
            transform.apply(&delegate.syntax());
        }
    }

    Some(delegate)
}

fn process_assoc_item(
    item: syntax::ast::AssocItem,
    qual_path_ty: ast::Path,
    base_name: &str,
) -> Option<ast::AssocItem> {
    match item {
        AssocItem::Const(c) => const_assoc_item(c, qual_path_ty),
        AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name),
        AssocItem::MacroCall(_) => {
            // FIXME : Handle MacroCall case.
            // macro_assoc_item(mac, qual_path_ty)
            None
        }
        AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty),
    }
}

fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> {
    let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());

    // We want rhs of the const assignment to be a qualified path
    // The general case for const assigment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`)
    // The qualified will have the following generic syntax :
    // <Base as Trait<GenArgs>>::ConstName;
    // FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it.
    // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap());
    let qualpath = qualpath(qual_path_ty, path_expr_segment);
    let inner =
        make::item_const(item.visibility(), item.name()?, item.ty()?, make::expr_path(qualpath))
            .clone_for_update();

    Some(AssocItem::Const(inner))
}

fn func_assoc_item(
    item: syntax::ast::Fn,
    qual_path_ty: Path,
    base_name: &str,
) -> Option<AssocItem> {
    let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
    let qualpath = qualpath(qual_path_ty, path_expr_segment);

    let call = match item.param_list() {
        // Methods and funcs should be handled separately.
        // We ask if the func has a `self` param.
        Some(l) => match l.self_param() {
            Some(slf) => {
                let mut self_kw = make::expr_path(make::path_from_text("self"));
                self_kw = make::expr_field(self_kw, base_name);

                let tail_expr_self = match slf.kind() {
                    ast::SelfParamKind::Owned => self_kw,
                    ast::SelfParamKind::Ref => make::expr_ref(self_kw, false),
                    ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true),
                };

                let param_count = l.params().count();
                let args = convert_param_list_to_arg_list(l).clone_for_update();

                if param_count > 0 {
                    // Add SelfParam and a TOKEN::COMMA
                    ted::insert_all(
                        Position::after(args.l_paren_token()?),
                        vec![
                            NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
                            NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
                            NodeOrToken::Token(make::token(SyntaxKind::COMMA)),
                        ],
                    );
                } else {
                    // Add SelfParam only
                    ted::insert(
                        Position::after(args.l_paren_token()?),
                        NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
                    );
                }

                make::expr_call(make::expr_path(qualpath), args)
            }
            None => make::expr_call(make::expr_path(qualpath), convert_param_list_to_arg_list(l)),
        },
        None => make::expr_call(
            make::expr_path(qualpath),
            convert_param_list_to_arg_list(make::param_list(None, Vec::new())),
        ),
    }
    .clone_for_update();

    let body = make::block_expr(vec![], Some(call)).clone_for_update();
    let func = make::fn_(
        item.visibility(),
        item.name()?,
        item.generic_param_list(),
        item.where_clause(),
        item.param_list()?,
        body,
        item.ret_type(),
        item.async_token().is_some(),
        item.const_token().is_some(),
        item.unsafe_token().is_some(),
    )
    .clone_for_update();

    Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update()))
}

fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<AssocItem> {
    let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
    let qualpath = qualpath(qual_path_ty, path_expr_segment);
    let ty = make::ty_path(qualpath);
    let ident = item.name()?.to_string();

    let alias = make::ty_alias(
        ident.as_str(),
        item.generic_param_list(),
        None,
        item.where_clause(),
        Some((ty, None)),
    )
    .clone_for_update();

    Some(AssocItem::TypeAlias(alias))
}

fn qualpath(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path {
    make::path_from_text(&format!("{}::{}", qual_path_ty.to_string(), path_expr_seg.to_string()))
}

#[cfg(test)]
mod test {

    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable};

    #[test]
    fn test_tuple_struct_basic() {
        check_assist(
            generate_delegate_trait,
            r#"
struct Base;
struct S(B$0ase);
trait Trait {}
impl Trait for Base {}
"#,
            r#"
struct Base;
struct S(Base);

impl Trait for S {}
trait Trait {}
impl Trait for Base {}
"#,
        );
    }

    #[test]
    fn test_struct_struct_basic() {
        check_assist(
            generate_delegate_trait,
            r#"
struct Base;
struct S {
    ba$0se : Base
}
trait Trait {}
impl Trait for Base {}
"#,
            r#"
struct Base;
struct S {
    base : Base
}

impl Trait for S {}
trait Trait {}
impl Trait for Base {}
"#,
        )
    }

    // Structs need to be by def populated with fields
    // However user can invoke this assist while still editing
    // We therefore assert its non-applicability
    #[test]
    fn test_yet_empty_struct() {
        check_assist_not_applicable(
            generate_delegate_trait,
            r#"
struct Base;
struct S {
    $0
}

impl Trait for S {}
trait Trait {}
impl Trait for Base {}
"#,
        )
    }

    #[test]
    fn test_yet_unspecified_field_type() {
        check_assist_not_applicable(
            generate_delegate_trait,
            r#"
struct Base;
struct S {
    ab$0c
}

impl Trait for S {}
trait Trait {}
impl Trait for Base {}
"#,
        );
    }

    #[test]
    fn test_unsafe_trait() {
        check_assist(
            generate_delegate_trait,
            r#"
struct Base;
struct S {
    ba$0se : Base
}
unsafe trait Trait {}
unsafe impl Trait for Base {}
"#,
            r#"
struct Base;
struct S {
    base : Base
}

unsafe impl Trait for S {}
unsafe trait Trait {}
unsafe impl Trait for Base {}
"#,
        );
    }

    #[test]
    fn test_unsafe_trait_with_unsafe_fn() {
        check_assist(
            generate_delegate_trait,
            r#"
struct Base;
struct S {
    ba$0se: Base,
}

unsafe trait Trait {
    unsafe fn a_func();
    unsafe fn a_method(&self);
}
unsafe impl Trait for Base {
    unsafe fn a_func() {}
    unsafe fn a_method(&self) {}
}
"#,
            r#"
struct Base;
struct S {
    base: Base,
}

unsafe impl Trait for S {
    unsafe fn a_func() {
        <Base as Trait>::a_func()
    }

    unsafe fn a_method(&self) {
        <Base as Trait>::a_method( &self.base )
    }
}

unsafe trait Trait {
    unsafe fn a_func();
    unsafe fn a_method(&self);
}
unsafe impl Trait for Base {
    unsafe fn a_func() {}
    unsafe fn a_method(&self) {}
}
"#,
        );
    }

    #[test]
    fn test_struct_with_where_clause() {
        check_assist(
            generate_delegate_trait,
            r#"
trait AnotherTrait {}
struct S<T>
where
    T: AnotherTrait,
{
    b$0 : T,
}"#,
            r#"
trait AnotherTrait {}
struct S<T>
where
    T: AnotherTrait,
{
    b : T,
}

impl<T> AnotherTrait for S<T>
where
    T: AnotherTrait,
{}"#,
        );
    }

    #[test]
    fn test_complex_without_where() {
        check_assist(
            generate_delegate_trait,
            r#"
trait Trait<'a, T, const C: usize> {
    type AssocType;
    const AssocConst: usize;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    field$0: Base
}

impl<'a, T, const C: usize> Trait<'a, T, C> for Base {
    type AssocType = ();
    const AssocConst: usize = 0;
    fn assoc_fn(p: ()) {}
    fn assoc_method(&self, p: ()) {}
}
"#,
            r#"
trait Trait<'a, T, const C: usize> {
    type AssocType;
    const AssocConst: usize;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    field: Base
}

impl<'a, T, const C: usize> Trait<'a, T, C> for S {
    type AssocType = <Base as Trait<'a, T, C>>::AssocType;

    const AssocConst: usize = <Base as Trait<'a, T, C>>::AssocConst;

    fn assoc_fn(p: ()) {
        <Base as Trait<'a, T, C>>::assoc_fn(p)
    }

    fn assoc_method(&self, p: ()) {
        <Base as Trait<'a, T, C>>::assoc_method( &self.field , p)
    }
}

impl<'a, T, const C: usize> Trait<'a, T, C> for Base {
    type AssocType = ();
    const AssocConst: usize = 0;
    fn assoc_fn(p: ()) {}
    fn assoc_method(&self, p: ()) {}
}
"#,
        );
    }

    #[test]
    fn test_complex_two() {
        check_assist(
            generate_delegate_trait,
            r"
trait AnotherTrait {}

trait Trait<'a, T, const C: usize> {
    type AssocType;
    const AssocConst: usize;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    fi$0eld: Base,
}

impl<'b, C, const D: usize> Trait<'b, C, D> for Base
where
    C: AnotherTrait,
{
    type AssocType = ();
    const AssocConst: usize = 0;
    fn assoc_fn(p: ()) {}
    fn assoc_method(&self, p: ()) {}
}",
            r#"
trait AnotherTrait {}

trait Trait<'a, T, const C: usize> {
    type AssocType;
    const AssocConst: usize;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    field: Base,
}

impl<'b, C, const D: usize> Trait<'b, C, D> for S
where
    C: AnotherTrait,
{
    type AssocType = <Base as Trait<'b, C, D>>::AssocType;

    const AssocConst: usize = <Base as Trait<'b, C, D>>::AssocConst;

    fn assoc_fn(p: ()) {
        <Base as Trait<'b, C, D>>::assoc_fn(p)
    }

    fn assoc_method(&self, p: ()) {
        <Base as Trait<'b, C, D>>::assoc_method( &self.field , p)
    }
}

impl<'b, C, const D: usize> Trait<'b, C, D> for Base
where
    C: AnotherTrait,
{
    type AssocType = ();
    const AssocConst: usize = 0;
    fn assoc_fn(p: ()) {}
    fn assoc_method(&self, p: ()) {}
}"#,
        )
    }

    #[test]
    fn test_complex_three() {
        check_assist(
            generate_delegate_trait,
            r#"
trait AnotherTrait {}
trait YetAnotherTrait {}

struct StructImplsAll();
impl AnotherTrait for StructImplsAll {}
impl YetAnotherTrait for StructImplsAll {}

trait Trait<'a, T, const C: usize> {
    type A;
    const ASSOC_CONST: usize = C;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    fi$0eld: Base,
}

impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base
where
    A: AnotherTrait,
{
    type A = i32;

    const ASSOC_CONST: usize = B;

    fn assoc_fn(p: ()) {}

    fn assoc_method(&self, p: ()) {}
}
"#,
            r#"
trait AnotherTrait {}
trait YetAnotherTrait {}

struct StructImplsAll();
impl AnotherTrait for StructImplsAll {}
impl YetAnotherTrait for StructImplsAll {}

trait Trait<'a, T, const C: usize> {
    type A;
    const ASSOC_CONST: usize = C;
    fn assoc_fn(p: ());
    fn assoc_method(&self, p: ());
}

struct Base;
struct S {
    field: Base,
}

impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for S
where
    A: AnotherTrait,
{
    type A = <Base as Trait<'b, A, B>>::A;

    const ASSOC_CONST: usize = <Base as Trait<'b, A, B>>::ASSOC_CONST;

    fn assoc_fn(p: ()) {
        <Base as Trait<'b, A, B>>::assoc_fn(p)
    }

    fn assoc_method(&self, p: ()) {
        <Base as Trait<'b, A, B>>::assoc_method( &self.field , p)
    }
}

impl<'b, A: AnotherTrait + YetAnotherTrait, const B: usize> Trait<'b, A, B> for Base
where
    A: AnotherTrait,
{
    type A = i32;

    const ASSOC_CONST: usize = B;

    fn assoc_fn(p: ()) {}

    fn assoc_method(&self, p: ()) {}
}
"#,
        )
    }

    #[test]
    fn test_type_bound() {
        check_assist(
            generate_delegate_trait,
            r#"
trait AnotherTrait {}
struct S<T>
where
    T: AnotherTrait,
{
    b$0: T,
}"#,
            r#"
trait AnotherTrait {}
struct S<T>
where
    T: AnotherTrait,
{
    b: T,
}

impl<T> AnotherTrait for S<T>
where
    T: AnotherTrait,
{}"#,
        );
    }

    #[test]
    fn test_docstring_example() {
        check_assist(
            generate_delegate_trait,
            r#"
trait SomeTrait {
    type T;
    fn fn_(arg: u32) -> u32;
    fn method_(&mut self) -> bool;
}
struct A;
impl SomeTrait for A {
    type T = u32;
    fn fn_(arg: u32) -> u32 {
        42
    }
    fn method_(&mut self) -> bool {
        false
    }
}
struct B {
    a$0: A,
}
"#,
            r#"
trait SomeTrait {
    type T;
    fn fn_(arg: u32) -> u32;
    fn method_(&mut self) -> bool;
}
struct A;
impl SomeTrait for A {
    type T = u32;
    fn fn_(arg: u32) -> u32 {
        42
    }
    fn method_(&mut self) -> bool {
        false
    }
}
struct B {
    a: A,
}

impl SomeTrait for B {
    type T = <A as SomeTrait>::T;

    fn fn_(arg: u32) -> u32 {
        <A as SomeTrait>::fn_(arg)
    }

    fn method_(&mut self) -> bool {
        <A as SomeTrait>::method_( &mut self.a )
    }
}
"#,
        );
    }

    #[test]
    fn import_from_other_mod() {
        check_assist(
            generate_delegate_trait,
            r#"
mod some_module {
    pub trait SomeTrait {
        type T;
        fn fn_(arg: u32) -> u32;
        fn method_(&mut self) -> bool;
    }
    pub struct A;
    impl SomeTrait for A {
        type T = u32;

        fn fn_(arg: u32) -> u32 {
            42
        }

        fn method_(&mut self) -> bool {
            false
        }
    }
}

struct B {
    a$0: some_module::A,
}"#,
            r#"
mod some_module {
    pub trait SomeTrait {
        type T;
        fn fn_(arg: u32) -> u32;
        fn method_(&mut self) -> bool;
    }
    pub struct A;
    impl SomeTrait for A {
        type T = u32;

        fn fn_(arg: u32) -> u32 {
            42
        }

        fn method_(&mut self) -> bool {
            false
        }
    }
}

struct B {
    a: some_module::A,
}

impl some_module::SomeTrait for B {
    type T = <some_module::A as some_module::SomeTrait>::T;

    fn fn_(arg: u32) -> u32 {
        <some_module::A as some_module::SomeTrait>::fn_(arg)
    }

    fn method_(&mut self) -> bool {
        <some_module::A as some_module::SomeTrait>::method_( &mut self.a )
    }
}"#,
        )
    }
}
