use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use uuid::Uuid;
use validator::Validate;

use erp_core::sanitize::{sanitize_option, sanitize_string};

// --- Auth DTOs ---

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct LoginReq {
    #[validate(length(min = 1, message = "用户名不能为空"))]
    pub username: String,
    #[validate(length(min = 1, max = 128, message = "密码长度需在1-128之间"))]
    pub password: String,
    /// 客户端类型: "miniprogram" 允许患者角色登录
    #[serde(default)]
    pub client_type: Option<String>,
}

#[derive(Debug, Serialize, ToSchema)]
pub struct LoginResp {
    pub access_token: String,
    pub refresh_token: String,
    pub expires_in: u64,
    pub user: UserResp,
}

#[derive(Debug, Deserialize, ToSchema)]
pub struct RefreshReq {
    pub refresh_token: String,
}

/// 修改密码请求
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct ChangePasswordReq {
    #[validate(length(min = 1, message = "当前密码不能为空"))]
    pub current_password: String,
    #[validate(length(min = 6, max = 128, message = "新密码长度需在6-128之间"))]
    pub new_password: String,
}

/// 管理员重置用户密码请求
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct ResetPasswordReq {
    #[validate(length(min = 6, max = 128, message = "新密码长度需在6-128之间"))]
    pub new_password: String,
    #[validate(range(min = 0))]
    pub version: i32,
}

// --- User DTOs ---

#[derive(Debug, Serialize, ToSchema)]
pub struct UserResp {
    pub id: Uuid,
    pub username: String,
    pub email: Option<String>,
    pub phone: Option<String>,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
    pub status: String,
    pub roles: Vec<RoleResp>,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateUserReq {
    #[validate(length(min = 1, max = 50))]
    pub username: String,
    #[validate(length(min = 6, max = 128))]
    pub password: String,
    #[validate(email)]
    pub email: Option<String>,
    #[validate(length(max = 20))]
    pub phone: Option<String>,
    #[validate(length(max = 100))]
    pub display_name: Option<String>,
}

impl CreateUserReq {
    /// 清理所有用户输入字段中的 HTML 标签，防止存储型 XSS。
    pub fn sanitize(&mut self) {
        self.username = sanitize_string(&self.username);
        self.email = sanitize_option(self.email.take());
        self.phone = sanitize_option(self.phone.take());
        self.display_name = sanitize_option(self.display_name.take());
    }
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct UpdateUserReq {
    #[validate(email)]
    pub email: Option<String>,
    #[validate(length(max = 20))]
    pub phone: Option<String>,
    #[validate(length(max = 100))]
    pub display_name: Option<String>,
    #[validate(length(min = 1, max = 20))]
    pub status: Option<String>,
    pub version: i32,
}

impl UpdateUserReq {
    /// 清理所有用户输入字段中的 HTML 标签，防止存储型 XSS。
    pub fn sanitize(&mut self) {
        self.email = sanitize_option(self.email.take());
        self.phone = sanitize_option(self.phone.take());
        self.display_name = sanitize_option(self.display_name.take());
    }
}

// --- Role DTOs ---

#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct RoleResp {
    pub id: Uuid,
    pub name: String,
    pub code: String,
    pub description: Option<String>,
    pub is_system: bool,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateRoleReq {
    #[validate(length(min = 1, max = 50))]
    pub name: String,
    #[validate(length(min = 1, max = 50))]
    pub code: String,
    pub description: Option<String>,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct UpdateRoleReq {
    #[validate(length(min = 1, max = 50))]
    pub name: Option<String>,
    pub description: Option<String>,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct AssignRolesReq {
    #[validate(length(min = 1, message = "至少需要分配一个角色"))]
    pub role_ids: Vec<Uuid>,
}

// --- Permission DTOs ---

#[derive(Debug, Serialize, ToSchema)]
pub struct PermissionResp {
    pub id: Uuid,
    pub code: String,
    pub name: String,
    pub resource: String,
    pub action: String,
    pub description: Option<String>,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct AssignPermissionsReq {
    #[validate(length(min = 1, message = "至少需要分配一个权限"))]
    pub permission_ids: Vec<Uuid>,
}

// --- Organization DTOs ---

#[derive(Debug, Serialize, ToSchema)]
pub struct OrganizationResp {
    pub id: Uuid,
    pub name: String,
    pub code: Option<String>,
    pub parent_id: Option<Uuid>,
    pub path: Option<String>,
    pub level: i32,
    pub sort_order: i32,
    pub children: Vec<OrganizationResp>,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateOrganizationReq {
    #[validate(length(min = 1))]
    pub name: String,
    pub code: Option<String>,
    pub parent_id: Option<Uuid>,
    pub sort_order: Option<i32>,
}

#[derive(Debug, Deserialize, ToSchema)]
pub struct UpdateOrganizationReq {
    pub name: Option<String>,
    pub code: Option<String>,
    pub sort_order: Option<i32>,
    pub version: i32,
}

// --- Department DTOs ---

#[derive(Debug, Serialize, ToSchema)]
pub struct DepartmentResp {
    pub id: Uuid,
    pub org_id: Uuid,
    pub name: String,
    pub code: Option<String>,
    pub parent_id: Option<Uuid>,
    pub manager_id: Option<Uuid>,
    pub path: Option<String>,
    pub sort_order: i32,
    pub children: Vec<DepartmentResp>,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateDepartmentReq {
    #[validate(length(min = 1))]
    pub name: String,
    pub code: Option<String>,
    pub parent_id: Option<Uuid>,
    pub manager_id: Option<Uuid>,
    pub sort_order: Option<i32>,
}

#[derive(Debug, Deserialize, ToSchema)]
pub struct UpdateDepartmentReq {
    pub name: Option<String>,
    pub code: Option<String>,
    pub manager_id: Option<Uuid>,
    pub sort_order: Option<i32>,
    pub version: i32,
}

// --- Position DTOs ---

#[derive(Debug, Serialize, ToSchema)]
pub struct PositionResp {
    pub id: Uuid,
    pub dept_id: Uuid,
    pub name: String,
    pub code: Option<String>,
    pub level: i32,
    pub sort_order: i32,
    pub version: i32,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreatePositionReq {
    #[validate(length(min = 1))]
    pub name: String,
    pub code: Option<String>,
    pub level: Option<i32>,
    pub sort_order: Option<i32>,
}

#[derive(Debug, Deserialize, ToSchema)]
pub struct UpdatePositionReq {
    pub name: Option<String>,
    pub code: Option<String>,
    pub level: Option<i32>,
    pub sort_order: Option<i32>,
    pub version: i32,
}

#[cfg(test)]
mod tests {
    use super::*;
    use validator::Validate;

    #[test]
    fn login_req_valid() {
        let req = LoginReq {
            username: "admin".to_string(),
            password: "password123".to_string(),
            client_type: None,
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn login_req_empty_username_fails() {
        let req = LoginReq {
            username: "".to_string(),
            password: "password123".to_string(),
            client_type: None,
        };
        let result = req.validate();
        assert!(result.is_err());
    }

    #[test]
    fn change_password_req_valid() {
        let req = ChangePasswordReq {
            current_password: "oldPassword123".to_string(),
            new_password: "newPassword456".to_string(),
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn change_password_req_empty_current_fails() {
        let req = ChangePasswordReq {
            current_password: "".to_string(),
            new_password: "newPassword456".to_string(),
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn change_password_req_short_new_fails() {
        let req = ChangePasswordReq {
            current_password: "oldPassword123".to_string(),
            new_password: "12345".to_string(), // min 6
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn change_password_req_long_new_fails() {
        let req = ChangePasswordReq {
            current_password: "oldPassword123".to_string(),
            new_password: "a".repeat(129), // max 128
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn login_req_empty_password_fails() {
        let req = LoginReq {
            username: "admin".to_string(),
            password: "".to_string(),
            client_type: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_user_req_valid() {
        let req = CreateUserReq {
            username: "alice".to_string(),
            password: "secret123".to_string(),
            email: Some("alice@example.com".to_string()),
            phone: None,
            display_name: Some("Alice".to_string()),
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn create_user_req_short_password_fails() {
        let req = CreateUserReq {
            username: "bob".to_string(),
            password: "12345".to_string(), // min 6
            email: None,
            phone: None,
            display_name: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_user_req_empty_username_fails() {
        let req = CreateUserReq {
            username: "".to_string(),
            password: "secret123".to_string(),
            email: None,
            phone: None,
            display_name: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_user_req_invalid_email_fails() {
        let req = CreateUserReq {
            username: "charlie".to_string(),
            password: "secret123".to_string(),
            email: Some("not-an-email".to_string()),
            phone: None,
            display_name: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_user_req_long_username_fails() {
        let req = CreateUserReq {
            username: "a".repeat(51), // max 50
            password: "secret123".to_string(),
            email: None,
            phone: None,
            display_name: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_role_req_valid() {
        let req = CreateRoleReq {
            name: "管理员".to_string(),
            code: "admin".to_string(),
            description: Some("系统管理员".to_string()),
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn create_role_req_empty_name_fails() {
        let req = CreateRoleReq {
            name: "".to_string(),
            code: "admin".to_string(),
            description: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_role_req_empty_code_fails() {
        let req = CreateRoleReq {
            name: "管理员".to_string(),
            code: "".to_string(),
            description: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_org_req_valid() {
        let req = CreateOrganizationReq {
            name: "总部".to_string(),
            code: Some("HQ".to_string()),
            parent_id: None,
            sort_order: Some(0),
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn create_org_req_empty_name_fails() {
        let req = CreateOrganizationReq {
            name: "".to_string(),
            code: None,
            parent_id: None,
            sort_order: None,
        };
        assert!(req.validate().is_err());
    }

    #[test]
    fn create_dept_req_valid() {
        let req = CreateDepartmentReq {
            name: "技术部".to_string(),
            code: Some("TECH".to_string()),
            parent_id: None,
            manager_id: None,
            sort_order: Some(1),
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn create_position_req_valid() {
        let req = CreatePositionReq {
            name: "高级工程师".to_string(),
            code: Some("SENIOR".to_string()),
            level: Some(3),
            sort_order: None,
        };
        assert!(req.validate().is_ok());
    }

    #[test]
    fn create_position_req_empty_name_fails() {
        let req = CreatePositionReq {
            name: "".to_string(),
            code: None,
            level: None,
            sort_order: None,
        };
        assert!(req.validate().is_err());
    }
}
