pub mod channel;
pub mod dm;
pub mod emoji;
pub mod guild;
pub mod message;
pub mod user;

use reqwest::{Client, Method};

pub use channel::Channel;
pub use dm::DM;
pub use emoji::Emoji;
pub use guild::Guild;
pub use message::Message;
use serde::de::DeserializeOwned;
pub use user::User;

use crate::{
    Error,
    api::{
        channel::{PermissionContext, Role},
        guild::GuildMember,
    },
};

#[derive(Debug, Clone)]
pub struct ApiClient {
    pub http_client: Client,
    pub auth_token: String,
    pub base_url: String,
}

impl ApiClient {
    pub fn new(http_client: Client, auth_token: String, base_url: String) -> Self {
        Self {
            http_client,
            auth_token,
            base_url,
        }
    }

    async fn api_request<T: DeserializeOwned>(
        &self,
        endpoint: &str,
        method: Method,
        body: Option<serde_json::Value>,
    ) -> Result<T, Error> {
        let url = format!("{}/{}", self.base_url, endpoint);
        let mut request = self
            .http_client
            .request(method, &url)
            .header("Authorization", self.auth_token.as_str());

        if let Some(data) = body {
            request = request.json(&data);
        }

        let response = request.send().await?;
        let status = response.status();

        if status.is_success() {
            Ok(response.json::<T>().await?)
        } else {
            let body = response
                .text()
                .await
                .unwrap_or("Failed to read error body".to_string());
            Err(format!("API Error: Status {status}. Details: {body}").into())
        }
    }

    pub async fn get_current_user(&self) -> Result<User, Error> {
        self.api_request("users/@me", Method::GET, None).await
    }

    pub async fn get_channel(&self, channel_id: &str) -> Result<Channel, Error> {
        self.api_request(format!("channels/{channel_id}").as_str(), Method::GET, None)
            .await
    }

    pub async fn get_dms(&self) -> Result<Vec<DM>, Error> {
        self.api_request("users/@me/channels", Method::GET, None)
            .await
    }

    pub async fn get_guild_emojis(&self, guild_id: &str) -> Result<Vec<Emoji>, Error> {
        self.api_request(
            format!("guilds/{guild_id}/emojis").as_str(),
            Method::GET,
            None,
        )
        .await
    }

    pub async fn get_guild_channels(&self, guild_id: &str) -> Result<Vec<Channel>, Error> {
        self.api_request(
            format!("guilds/{guild_id}/channels").as_str(),
            Method::GET,
            None,
        )
        .await
    }

    pub async fn get_guild_roles(&self, guild_id: &str) -> Result<Vec<Role>, Error> {
        self.api_request(
            format!("guilds/{guild_id}/roles").as_str(),
            Method::GET,
            None,
        )
        .await
    }

    pub async fn get_guild_member(&self, guild_id: &str) -> Result<GuildMember, Error> {
        let user = self.get_current_user().await?;
        self.api_request(
            format!("guilds/{guild_id}/members/{}", user.id).as_str(),
            Method::GET,
            None,
        )
        .await
    }

    pub async fn get_permission_context(&self, guild_id: &str) -> Result<PermissionContext, Error> {
        let all_guild_roles: Vec<Role> = self.get_guild_roles(guild_id).await?;
        let member_info: GuildMember = self.get_guild_member(guild_id).await?;

        Ok(PermissionContext {
            user_id: member_info.user.id,
            user_role_ids: {
                let everyone_role_id = guild_id.to_string();
                let mut roles = member_info.roles;
                if !roles.contains(&everyone_role_id) {
                    roles.push(everyone_role_id.clone());
                }
                roles
            },
            all_guild_roles,
            everyone_role_id: guild_id.to_string(),
        })
    }

    pub async fn create_message(
        &self,
        channel_id: &str,
        content: Option<String>,
        tts: bool,
    ) -> Result<Message, Error> {
        self.api_request(
            format!("channels/{channel_id}/messages").as_str(),
            Method::POST,
            Some(serde_json::json!({ "content": content, "tts": tts })),
        )
        .await
    }

    pub async fn get_channel_messages(
        &self,
        channel_id: &str,
        around: Option<String>,
        before: Option<String>,
        after: Option<String>,
        limit: Option<usize>,
    ) -> Result<Vec<Message>, Error> {
        let mut endpoint = format!("channels/{channel_id}/messages");
        let mut query = Vec::new();

        if let Some(a) = around {
            query.push(format!("around={a}"));
        }
        if let Some(b) = before {
            query.push(format!("before={b}"));
        }
        if let Some(a) = after {
            query.push(format!("after={a}"));
        }
        if let Some(l) = limit {
            query.push(format!("limit={l}"));
        }

        if !query.is_empty() {
            endpoint.push('?');
            endpoint.push_str(&query.join("&"));
        }

        self.api_request(&endpoint, Method::GET, None).await
    }

    pub async fn get_current_user_guilds(&self) -> Result<Vec<Guild>, Error> {
        self.api_request("/users/@me/guilds", Method::GET, None)
            .await
    }
}
