use serde::{Deserialize, Serialize};

use crate::config::Config;
use crate::constants::AccessTokenType;
use crate::error::LarkError;
use crate::req::RequestOption;
use crate::service::common::{PageQuery, RestRequest};

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AppRoleData {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub role: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AppRoleListData {
    #[serde(default)]
    pub items: Vec<serde_json::Value>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub page_token: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub has_more: Option<bool>,
}

impl_resp_v2!(CreateAppRoleV2Resp, AppRoleData);
impl_resp_v2!(ListAppRoleV2Resp, AppRoleListData);
impl_resp_v2!(UpdateAppRoleV2Resp, AppRoleData);

#[derive(Debug, Clone, Copy)]
pub struct CreateAppRoleV2Query<'a> {
    pub app_token: &'a str,
    pub body: &'a serde_json::Value,
}

impl<'a> CreateAppRoleV2Query<'a> {
    pub fn new(app_token: &'a str, body: &'a serde_json::Value) -> Self {
        Self { app_token, body }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct ListAppRoleV2Query<'a> {
    pub app_token: &'a str,
    pub page: PageQuery<'a>,
}

impl<'a> ListAppRoleV2Query<'a> {
    pub fn new(app_token: &'a str) -> Self {
        Self {
            app_token,
            page: PageQuery::default(),
        }
    }

    pub fn page(mut self, page: PageQuery<'a>) -> Self {
        self.page = page;
        self
    }
}

#[derive(Debug, Clone, Copy)]
pub struct UpdateAppRoleV2Query<'a> {
    pub app_token: &'a str,
    pub role_id: &'a str,
    pub body: &'a serde_json::Value,
}

impl<'a> UpdateAppRoleV2Query<'a> {
    pub fn new(app_token: &'a str, role_id: &'a str, body: &'a serde_json::Value) -> Self {
        Self {
            app_token,
            role_id,
            body,
        }
    }
}

pub struct V2<'a> {
    pub app_role: AppRoleV2Resource<'a>,
}

impl<'a> V2<'a> {
    pub fn new(config: &'a Config) -> Self {
        Self {
            app_role: AppRoleV2Resource { config },
        }
    }
}

pub struct AppRoleV2Resource<'a> {
    config: &'a Config,
}

impl AppRoleV2Resource<'_> {
    pub async fn create(
        &self,
        app_token: &str,
        body: serde_json::Value,
        option: &RequestOption,
    ) -> Result<CreateAppRoleV2Resp, LarkError> {
        self.create_by_query(&CreateAppRoleV2Query::new(app_token, &body), option)
            .await
    }

    pub async fn create_by_query(
        &self,
        query: &CreateAppRoleV2Query<'_>,
        option: &RequestOption,
    ) -> Result<CreateAppRoleV2Resp, LarkError> {
        let path = format!("/open-apis/base/v2/apps/{}/roles", query.app_token);
        RestRequest::new(
            self.config,
            http::Method::POST,
            path,
            vec![AccessTokenType::User, AccessTokenType::Tenant],
            option,
        )
        .json_body(query.body)?
        .send_v2_response::<AppRoleData, CreateAppRoleV2Resp>()
        .await
    }

    pub async fn list(
        &self,
        app_token: &str,
        page_size: Option<i32>,
        page_token: Option<&str>,
        option: &RequestOption,
    ) -> Result<ListAppRoleV2Resp, LarkError> {
        let query =
            ListAppRoleV2Query::new(app_token).page(PageQuery::from_parts(page_size, page_token));
        self.list_by_query(&query, option).await
    }

    pub async fn list_by_query(
        &self,
        query: &ListAppRoleV2Query<'_>,
        option: &RequestOption,
    ) -> Result<ListAppRoleV2Resp, LarkError> {
        let path = format!("/open-apis/base/v2/apps/{}/roles", query.app_token);
        RestRequest::new(
            self.config,
            http::Method::GET,
            path,
            vec![AccessTokenType::User, AccessTokenType::Tenant],
            option,
        )
        .page_query(query.page)
        .send_v2_response::<AppRoleListData, ListAppRoleV2Resp>()
        .await
    }

    pub async fn update(
        &self,
        app_token: &str,
        role_id: &str,
        body: serde_json::Value,
        option: &RequestOption,
    ) -> Result<UpdateAppRoleV2Resp, LarkError> {
        self.update_by_query(
            &UpdateAppRoleV2Query::new(app_token, role_id, &body),
            option,
        )
        .await
    }

    pub async fn update_by_query(
        &self,
        query: &UpdateAppRoleV2Query<'_>,
        option: &RequestOption,
    ) -> Result<UpdateAppRoleV2Resp, LarkError> {
        let path = format!(
            "/open-apis/base/v2/apps/{}/roles/{}",
            query.app_token, query.role_id
        );
        RestRequest::new(
            self.config,
            http::Method::PUT,
            path,
            vec![AccessTokenType::User, AccessTokenType::Tenant],
            option,
        )
        .json_body(query.body)?
        .send_v2_response::<AppRoleData, UpdateAppRoleV2Resp>()
        .await
    }
}
