use model::{
    users::UserModel,
    workspace::{WkGroupModel, WorkspaceModel},
};
use serde::{Deserialize, Serialize};

use crate::{error::AppError, non_empty};
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceResponse {
    pub name: String,
    pub description: String,
    pub avatar_url: String,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub owner: bool,
    pub admin: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceMemberResponse {
    pub username: String,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
    pub owner: bool,
    pub admin: bool,
    #[schema(value_type = String)]
    pub join_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceGroupResponse {
    pub name: String,
    pub avatar_url: Option<String>,
    pub is_deleted: bool,
    #[schema(value_type = String)]
    pub created_at: chrono::DateTime<chrono::Utc>,
}

pub fn normalize_name(name: &str) -> Result<String, AppError> {
    let name = name.trim();
    if name.is_empty() {
        return Err(AppError::BadRequest(
            "workspace name is required".to_string(),
        ));
    }
    if name.len() > 64 {
        return Err(AppError::BadRequest(
            "workspace name is too long".to_string(),
        ));
    }
    if !name
        .chars()
        .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
    {
        return Err(AppError::BadRequest(
            "workspace name can only contain letters, numbers, '-' and '_'"
                .to_string(),
        ));
    }
    Ok(name.to_string())
}

pub fn workspace_response(
    wk: WorkspaceModel,
    owner: bool,
    admin: bool,
) -> WorkspaceResponse {
    WorkspaceResponse {
        name: wk.name,
        description: wk.description,
        avatar_url: wk.avatar_url,
        created_at: wk.created_at,
        owner,
        admin,
    }
}

pub fn member_response(
    user: UserModel,
    owner: bool,
    admin: bool,
    join_at: chrono::DateTime<chrono::Utc>,
) -> WorkspaceMemberResponse {
    WorkspaceMemberResponse {
        username: user.username,
        display_name: non_empty(user.display_name),
        avatar_url: non_empty(user.avatar_url),
        owner,
        admin,
        join_at,
    }
}

pub fn group_response(group: WkGroupModel) -> WorkspaceGroupResponse {
    WorkspaceGroupResponse {
        name: group.name,
        avatar_url: group.avatar_url,
        is_deleted: group.is_deleted,
        created_at: group.created_at,
    }
}
impl From<WorkspaceListRow> for WorkspaceModel {
    fn from(value: WorkspaceListRow) -> Self {
        Self {
            id: value.id,
            name: value.name,
            description: value.description,
            avatar_url: value.avatar_url,
            created_at: value.created_at,
        }
    }
}

#[derive(db::sqlx::FromRow)]
pub struct WorkspaceListRow {
    id: uuid::Uuid,
    pub name: String,
    pub description: String,
    pub avatar_url: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub owner: bool,
    pub admin: bool,
}

impl From<WorkspaceMemberRow> for WorkspaceMemberResponse {
    fn from(value: WorkspaceMemberRow) -> Self {
        Self {
            username: value.username,
            display_name: non_empty(value.display_name),
            avatar_url: non_empty(value.avatar_url),
            owner: value.owner,
            admin: value.admin,
            join_at: value.join_at,
        }
    }
}

#[derive(db::sqlx::FromRow)]
pub struct WorkspaceMemberRow {
    pub username: String,
    pub display_name: String,
    pub avatar_url: String,
    pub owner: bool,
    pub admin: bool,
    pub join_at: chrono::DateTime<chrono::Utc>,
}

impl From<WorkspaceGroupMemberRow> for WorkspaceMemberResponse {
    fn from(value: WorkspaceGroupMemberRow) -> Self {
        Self {
            username: value.username,
            display_name: non_empty(value.display_name),
            avatar_url: non_empty(value.avatar_url),
            owner: false,
            admin: false,
            join_at: value.join_at,
        }
    }
}

#[derive(db::sqlx::FromRow)]
pub struct WorkspaceGroupMemberRow {
    pub username: String,
    pub display_name: String,
    pub avatar_url: String,
    pub join_at: chrono::DateTime<chrono::Utc>,
}
