Documentation
use dashmap::DashMap;
use std::fmt::{Display, Formatter};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use ricq::structs::{GroupInfo, MessageReceipt};
use tracing::error;

use crate::contact::member::NamedMember;
use crate::error::{AtriError, AtriResult};
use crate::message::image::Image;
use crate::message::meta::{MessageMetadata, MetaMessage};
use crate::message::MessageChain;
use crate::Client;

#[derive(Clone)]
pub struct Group(Arc<imp::Group>);

impl Group {
    pub fn from(bot: Client, info: GroupInfo) -> Self {
        let imp = imp::Group {
            client: bot,
            info,
            member_list_refreshed: AtomicBool::new(false),
            members: DashMap::new(),
        };

        Self(Arc::new(imp))
    }

    pub fn id(&self) -> i64 {
        self.0.info.code
    }

    pub fn client(&self) -> &Client {
        &self.0.client
    }

    pub fn name(&self) -> &str {
        &self.0.info.name
    }

    pub async fn members(&self) -> Vec<NamedMember> {
        if self.0.member_list_refreshed.load(Ordering::Relaxed) {
            self.0.members.iter().map(|named| named.clone()).collect()
        } else {
            let owner = self.0.info.owner_uin;
            self.client()
                .request_client()
                .get_group_member_list(self.id(), owner)
                .await
                .map(|r| {
                    self.0.member_list_refreshed.swap(true, Ordering::Release);
                    r
                })
                .unwrap_or_else(|e| {
                    error!("刷新群聊成员信息时出现错误: {:?}", e);
                    vec![]
                })
                .into_iter()
                .map(|info| {
                    let named = NamedMember::from(self.clone(), info);
                    self.0.members.insert(named.id(), named.clone());
                    named
                })
                .collect()
        }
    }

    pub fn find_member(&self, id: i64) -> Option<NamedMember> {
        if let Some(member) = self.0.members.get(&id) {
            return Some(member.clone());
        }

        None
    }

    pub async fn try_refresh_member(&self, id: i64) -> AtriResult<Option<NamedMember>> {
        let named = self
            .client()
            .request_client()
            .get_group_member_info(self.id(), id)
            .await
            .map(|info| {
                if info.join_time == 0 {
                    return None;
                }

                let named = NamedMember::from(self.clone(), info);
                self.cache_member(named.clone());
                Some(named)
            })?;

        Ok(named)
    }

    pub async fn refresh_member(&self, id: i64) -> Option<NamedMember> {
        self.try_refresh_member(id).await.unwrap_or(None)
    }

    pub async fn find_or_refresh_member(&self, id: i64) -> Option<NamedMember> {
        if let Some(named) = self.find_member(id) {
            return Some(named);
        }

        self.refresh_member(id).await
    }

    async fn _send_message(&self, chain: MessageChain) -> AtriResult<MessageReceipt> {
        self.client()
            .request_client()
            .send_group_message(self.id(), chain.into())
            .await
            .map_err(|err| {
                error!(
                    "{}发送信息失败, 目标群: {}({}), {:?}",
                    self.client(),
                    self.name(),
                    self.id(),
                    err
                );

                AtriError::from(err)
            })
    }

    pub async fn send_message<M: Into<MessageChain>>(&self, msg: M) -> AtriResult<MessageReceipt> {
        self._send_message(msg.into()).await
    }

    async fn _upload_image(&self, image: Vec<u8>) -> AtriResult<Image> {
        self.client()
            .request_client()
            .upload_group_image(self.id(), image)
            .await
            .map(Image::Group)
            .map_err(|err| {
                error!(
                    "{}上传图片失败, 目标群: {}({}), {:?}",
                    self.client(),
                    self.name(),
                    self.id(),
                    err
                );

                AtriError::from(err)
            })
    }

    pub async fn upload_image<I: Into<Vec<u8>>>(&self, image: I) -> AtriResult<Image> {
        self._upload_image(image.into()).await
    }

    async fn _recall_message(&self, meta: &MessageMetadata) -> AtriResult<()> {
        self.client()
            .request_client()
            .recall_group_message(self.id(), meta.seqs.clone(), meta.rands.clone())
            .await
            .map_err(AtriError::from)
    }

    pub async fn recall_message<M: MetaMessage>(&self, msg: &M) -> AtriResult<()> {
        self._recall_message(msg.metadata()).await
    }

    async fn _change_name(&self, name: String) -> AtriResult<()> {
        self.client()
            .request_client()
            .update_group_name(self.id(), name)
            .await
            .map_err(AtriError::from)
    }

    pub async fn change_name<S: Into<String>>(&self, name: S) -> AtriResult<()> {
        self._change_name(name.into()).await
    }

    pub async fn kick<M: ToKickMember, S: AsRef<str>>(
        &self,
        member: M,
        msg: Option<S>,
        block: bool,
    ) -> AtriResult<()> {
        let members = member.to_member_vec();
        let msg = msg.as_ref().map(AsRef::<str>::as_ref).unwrap_or("");

        self.client()
            .request_client()
            .group_kick(self.id(), members, msg, block)
            .await
            .map_err(AtriError::from)
    }

    pub async fn quit(&self) -> bool {
        let result = self.client().request_client().group_quit(self.id()).await;
        if let Err(e) = result {
            error!("尝试退出群 {}({}) 时失败: {:?}", self.name(), self.id(), e);
            return false;
        }

        let map = self.client().remove_group_cache(self.id());

        map.is_some()
    }

    pub(crate) fn cache_member(&self, member: NamedMember) {
        self.0.members.insert(member.id(), member);
    }
}

impl Display for Group {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Group({})", self.id(),)
    }
}

mod imp {
    use dashmap::DashMap;
    use ricq::structs::GroupInfo;
    use std::sync::atomic::AtomicBool;

    use crate::contact::member::NamedMember;
    use crate::Client;

    pub struct Group {
        pub client: Client,
        pub info: GroupInfo,
        pub member_list_refreshed: AtomicBool,
        pub members: DashMap<i64, NamedMember>,
    }
}

pub trait ToKickMember {
    fn to_member_vec(self) -> Vec<i64>;
}

impl ToKickMember for i64 {
    fn to_member_vec(self) -> Vec<i64> {
        vec![self]
    }
}

impl ToKickMember for Vec<i64> {
    fn to_member_vec(self) -> Vec<i64> {
        self
    }
}

impl<const N: usize> ToKickMember for [i64; N] {
    fn to_member_vec(self) -> Vec<i64> {
        self.to_vec()
    }
}

impl<const N: usize> ToKickMember for &[i64; N] {
    fn to_member_vec(self) -> Vec<i64> {
        self.to_vec()
    }
}