use indent::indent_by;
use indoc::formatdoc;
use itertools::Itertools;

use super::PluginTypeInfo;
use crate::plugins::utils::TypeVariant;

/// Adds derive result for the `PartialEq` trait.
pub fn handle_partial_eq(info: &PluginTypeInfo<'_>, member_access_desnaps: bool) -> String {
    const PARTIAL_EQ_TRAIT: &str = "core::traits::PartialEq";
    let header = info.impl_header(PARTIAL_EQ_TRAIT, &[PARTIAL_EQ_TRAIT]);
    let full_typename = info.full_typename();
    let body = indent_by(
        8,
        match &info.type_variant {
            TypeVariant::Enum => {
                let ty = &info.name;
                formatdoc! {"
                        match lhs {{
                            {}
                        }}",
                    info.members_info.iter().map(|lhs_variant| {
                    indent_by(4, formatdoc! {"
                        {ty}::{lhs_variant}(x) => match rhs {{
                            {}
                        }},",
                        info.members_info.iter().map(|rhs_variant|{
                            if lhs_variant.name == rhs_variant.name {
                                format!("{ty}::{}(y) => {}::eq(x, y),", rhs_variant.name,
                                rhs_variant.impl_name(PARTIAL_EQ_TRAIT))
                            } else {
                                format!("{ty}::{}(_y) => false,", rhs_variant.name)
                            }
                        }).join("\n    "),
                    lhs_variant=lhs_variant.name,
                    })
                }).join("\n    ")}
            }
            TypeVariant::Struct => {
                if info.members_info.is_empty() {
                    "true".to_string()
                } else {
                    info.members_info
                        .iter()
                        .map(|member| {
                            let imp = member.impl_name(PARTIAL_EQ_TRAIT);
                            let member = &member.name;
                            if member_access_desnaps {
                                format!("{imp}::eq(@lhs.{member}, @rhs.{member})")
                            } else {
                                format!("{imp}::eq(lhs.{member}, rhs.{member})")
                            }
                        })
                        .join(" && ")
                }
            }
        },
    );
    formatdoc! {"
        {header} {{
            fn eq(lhs: @{full_typename}, rhs: @{full_typename}) -> bool {{
                {body}
            }}
        }}
    "}
}
