use chrono::Utc;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, DatabaseBackend, EntityTrait, FromQueryResult,
    PaginatorTrait, QueryFilter, Set, Statement,
};
use uuid::Uuid;

use crate::dto::{MessageQuery, MessageResp, SendMessageReq, UnreadCountResp};
use crate::entity::message;
use crate::error::{MessageError, MessageResult};
use erp_core::audit::AuditLog;
use erp_core::audit_service;
use erp_core::events::EventBus;

/// 原始 SQL 查询 user_id 的结果结构体。
#[derive(Debug, FromQueryResult)]
struct UserIdRow {
    user_id: Uuid,
}

/// 消息服务。
pub struct MessageService;

impl MessageService {
    /// 查询消息列表（分页）。
    pub async fn list(
        tenant_id: Uuid,
        recipient_id: Uuid,
        query: &MessageQuery,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<(Vec<MessageResp>, u64)> {
        let page_size = query.safe_page_size();
        let mut q = message::Entity::find()
            .filter(message::Column::TenantId.eq(tenant_id))
            .filter(message::Column::RecipientId.eq(recipient_id))
            .filter(message::Column::DeletedAt.is_null());

        if let Some(is_read) = query.is_read {
            q = q.filter(message::Column::IsRead.eq(is_read));
        }
        if let Some(ref priority) = query.priority {
            q = q.filter(message::Column::Priority.eq(priority.as_str()));
        }
        if let Some(ref business_type) = query.business_type {
            q = q.filter(message::Column::BusinessType.eq(business_type.as_str()));
        }
        if let Some(ref status) = query.status {
            q = q.filter(message::Column::Status.eq(status.as_str()));
        }

        let paginator = q.paginate(db, page_size);

        let total = paginator
            .num_items()
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        let page_index = query.page.unwrap_or(1).saturating_sub(1);
        let models = paginator
            .fetch_page(page_index)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        let resps = models.iter().map(Self::model_to_resp).collect();
        Ok((resps, total))
    }

    /// 获取未读消息数量。
    pub async fn unread_count(
        tenant_id: Uuid,
        recipient_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<UnreadCountResp> {
        let count = message::Entity::find()
            .filter(message::Column::TenantId.eq(tenant_id))
            .filter(message::Column::RecipientId.eq(recipient_id))
            .filter(message::Column::IsRead.eq(false))
            .filter(message::Column::DeletedAt.is_null())
            .count(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        Ok(UnreadCountResp {
            count: count as i64,
        })
    }

    /// 发送消息。
    ///
    /// 根据 `recipient_type` 执行不同的投递策略：
    /// - `"user"` — 单条消息，直接投递给 `recipient_id` 指定的用户。
    /// - `"role"` — 查询 `user_roles` 表，向该角色下的所有用户批量投递。
    /// - `"department"` — 查询 `user_departments` 表，向该部门下的所有用户批量投递。
    /// - `"all"` — 查询 `users` 表，向租户内所有活跃用户批量投递。
    pub async fn send(
        tenant_id: Uuid,
        sender_id: Uuid,
        req: &SendMessageReq,
        db: &sea_orm::DatabaseConnection,
        event_bus: &EventBus,
    ) -> MessageResult<MessageResp> {
        let now = Utc::now();

        // Resolve target user IDs based on recipient type
        let recipient_user_ids = match req.recipient_type.as_str() {
            "user" => vec![req.recipient_id],
            "role" => Self::resolve_user_ids_by_role(db, req.recipient_id, tenant_id).await?,
            "department" => {
                Self::resolve_user_ids_by_department(db, req.recipient_id, tenant_id).await?
            }
            "all" => Self::resolve_all_active_user_ids(db, tenant_id).await?,
            other => {
                return Err(MessageError::Validation(format!(
                    "不支持的收件人类型: {other}"
                )));
            }
        };

        if recipient_user_ids.is_empty() {
            return Err(MessageError::Validation(
                "没有找到符合条件的收件人".to_string(),
            ));
        }

        // Build message models for all recipients
        let models: Vec<message::ActiveModel> = recipient_user_ids
            .iter()
            .map(|uid| message::ActiveModel {
                id: Set(Uuid::now_v7()),
                tenant_id: Set(tenant_id),
                template_id: Set(req.template_id),
                sender_id: Set(Some(sender_id)),
                sender_type: Set("user".to_string()),
                recipient_id: Set(*uid),
                recipient_type: Set("user".to_string()),
                title: Set(req.title.clone()),
                body: Set(req.body.clone()),
                priority: Set(req.priority.clone()),
                business_type: Set(req.business_type.clone()),
                business_id: Set(req.business_id),
                is_read: Set(false),
                read_at: Set(None),
                is_archived: Set(false),
                archived_at: Set(None),
                sent_at: Set(Some(now)),
                status: Set("sent".to_string()),
                created_at: Set(now),
                updated_at: Set(now),
                created_by: Set(sender_id),
                updated_by: Set(sender_id),
                deleted_at: Set(None),
                version: Set(1),
            })
            .collect();

        // Batch insert all messages
        message::Entity::insert_many(models)
            .exec(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        // Publish one event per batch (summary event)
        event_bus
            .publish(
                erp_core::events::DomainEvent::new(
                    "message.sent",
                    tenant_id,
                    serde_json::json!({
                        "recipient_type": req.recipient_type,
                        "recipient_count": recipient_user_ids.len(),
                        "title": req.title,
                    }),
                ),
                db,
            )
            .await;

        audit_service::record(
            AuditLog::new(tenant_id, Some(sender_id), "message.send", "message").with_changes(
                None,
                Some(serde_json::json!({
                    "recipient_type": req.recipient_type,
                    "recipient_count": recipient_user_ids.len(),
                    "title": req.title,
                })),
            ),
            db,
        )
        .await;

        // Construct a representative response (no row returned from batch insert)
        Ok(MessageResp {
            id: Uuid::nil(),
            tenant_id,
            template_id: req.template_id,
            sender_id: Some(sender_id),
            sender_type: "user".to_string(),
            recipient_id: req.recipient_id,
            recipient_type: req.recipient_type.clone(),
            title: req.title.clone(),
            body: req.body.clone(),
            priority: req.priority.clone(),
            business_type: req.business_type.clone(),
            business_id: req.business_id,
            is_read: false,
            read_at: None,
            is_archived: false,
            status: "sent".to_string(),
            sent_at: Some(now),
            created_at: now,
            updated_at: now,
            version: 1,
        })
    }

    /// 根据角色 ID 查询关联的用户 ID 列表（跨模块 raw SQL）。
    async fn resolve_user_ids_by_role(
        db: &sea_orm::DatabaseConnection,
        role_id: Uuid,
        tenant_id: Uuid,
    ) -> MessageResult<Vec<Uuid>> {
        let rows = UserIdRow::find_by_statement(Statement::from_sql_and_values(
            DatabaseBackend::Postgres,
            "SELECT user_id FROM user_roles WHERE role_id = $1 AND tenant_id = $2 AND deleted_at IS NULL",
            [role_id.into(), tenant_id.into()],
        ))
        .all(db)
        .await
        .map_err(|e| MessageError::Validation(e.to_string()))?;

        Ok(rows.into_iter().map(|r| r.user_id).collect())
    }

    /// 根据部门 ID 查询关联的用户 ID 列表（跨模块 raw SQL）。
    async fn resolve_user_ids_by_department(
        db: &sea_orm::DatabaseConnection,
        department_id: Uuid,
        tenant_id: Uuid,
    ) -> MessageResult<Vec<Uuid>> {
        let rows = UserIdRow::find_by_statement(Statement::from_sql_and_values(
            DatabaseBackend::Postgres,
            "SELECT user_id FROM user_departments WHERE department_id = $1 AND tenant_id = $2 AND deleted_at IS NULL",
            [department_id.into(), tenant_id.into()],
        ))
        .all(db)
        .await
        .map_err(|e| MessageError::Validation(e.to_string()))?;

        Ok(rows.into_iter().map(|r| r.user_id).collect())
    }

    /// 查询租户内所有活跃用户的 ID 列表（跨模块 raw SQL）。
    async fn resolve_all_active_user_ids(
        db: &sea_orm::DatabaseConnection,
        tenant_id: Uuid,
    ) -> MessageResult<Vec<Uuid>> {
        let rows = UserIdRow::find_by_statement(Statement::from_sql_and_values(
            DatabaseBackend::Postgres,
            "SELECT id AS user_id FROM users WHERE tenant_id = $1 AND deleted_at IS NULL AND status = 'active'",
            [tenant_id.into()],
        ))
        .all(db)
        .await
        .map_err(|e| MessageError::Validation(e.to_string()))?;

        Ok(rows.into_iter().map(|r| r.user_id).collect())
    }

    /// 系统发送消息（由事件处理器调用）。
    ///
    /// 幂等保证：当 `business_id` 存在时，若同 tenant + recipient + business_id 的消息已存在，
    /// 直接返回已有消息，避免 outbox relay 重放导致重复通知。
    #[allow(clippy::too_many_arguments)]
    pub async fn send_system(
        tenant_id: Uuid,
        recipient_id: Uuid,
        title: String,
        body: String,
        priority: &str,
        business_type: Option<String>,
        business_id: Option<Uuid>,
        db: &sea_orm::DatabaseConnection,
        event_bus: &EventBus,
    ) -> MessageResult<MessageResp> {
        // 幂等检查：防止 outbox relay 重放导致重复消息
        if let Some(bid) = business_id {
            let existing = message::Entity::find()
                .filter(message::Column::TenantId.eq(tenant_id))
                .filter(message::Column::RecipientId.eq(recipient_id))
                .filter(message::Column::BusinessId.eq(bid))
                .filter(message::Column::DeletedAt.is_null())
                .one(db)
                .await
                .map_err(|e| MessageError::Validation(e.to_string()))?;

            if let Some(m) = existing {
                tracing::debug!(
                    message_id = %m.id,
                    business_id = %bid,
                    "消息已存在，跳过重复创建（幂等保护）"
                );
                return Ok(Self::model_to_resp(&m));
            }
        }

        let id = Uuid::now_v7();
        let now = Utc::now();
        let system_user = Uuid::nil();

        let model = message::ActiveModel {
            id: Set(id),
            tenant_id: Set(tenant_id),
            template_id: Set(None),
            sender_id: Set(None),
            sender_type: Set("system".to_string()),
            recipient_id: Set(recipient_id),
            recipient_type: Set("user".to_string()),
            title: Set(title),
            body: Set(body),
            priority: Set(priority.to_string()),
            business_type: Set(business_type),
            business_id: Set(business_id),
            is_read: Set(false),
            read_at: Set(None),
            is_archived: Set(false),
            archived_at: Set(None),
            sent_at: Set(Some(now)),
            status: Set("sent".to_string()),
            created_at: Set(now),
            updated_at: Set(now),
            created_by: Set(system_user),
            updated_by: Set(system_user),
            deleted_at: Set(None),
            version: Set(1),
        };

        let inserted = model
            .insert(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        event_bus
            .publish(
                erp_core::events::DomainEvent::new(
                    "message.sent",
                    tenant_id,
                    serde_json::json!({
                        "message_id": id,
                        "recipient_id": recipient_id,
                    }),
                ),
                db,
            )
            .await;

        audit_service::record(
            AuditLog::new(
                tenant_id,
                Some(system_user),
                "message.send_system",
                "message",
            )
            .with_resource_id(id),
            db,
        )
        .await;

        Ok(Self::model_to_resp(&inserted))
    }

    /// 标记消息已读。
    pub async fn mark_read(
        id: Uuid,
        tenant_id: Uuid,
        user_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<()> {
        let model = message::Entity::find_by_id(id)
            .one(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?
            .filter(|m| m.tenant_id == tenant_id && m.deleted_at.is_none())
            .ok_or_else(|| MessageError::NotFound(format!("消息不存在: {id}")))?;

        if model.recipient_id != user_id {
            return Err(MessageError::Validation(
                "只能标记自己的消息为已读".to_string(),
            ));
        }

        if model.is_read {
            return Ok(());
        }

        let current_version = model.version;
        let mut active: message::ActiveModel = model.into();
        active.is_read = Set(true);
        active.read_at = Set(Some(Utc::now()));
        active.version = Set(current_version + 1);
        active.updated_at = Set(Utc::now());
        active.updated_by = Set(user_id);
        active
            .update(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        audit_service::record(
            AuditLog::new(tenant_id, Some(user_id), "message.mark_read", "message")
                .with_resource_id(id),
            db,
        )
        .await;

        Ok(())
    }

    /// 标记所有消息已读（批量 UPDATE，避免 N+1）。
    pub async fn mark_all_read(
        tenant_id: Uuid,
        user_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<()> {
        let now = Utc::now();
        db.execute(Statement::from_sql_and_values(
            DatabaseBackend::Postgres,
            "UPDATE messages SET is_read = true, read_at = $1, updated_at = $2, updated_by = $3, version = version + 1 WHERE tenant_id = $4 AND recipient_id = $5 AND is_read = false AND deleted_at IS NULL",
            [
                now.into(),
                now.into(),
                user_id.into(),
                tenant_id.into(),
                user_id.into(),
            ],
        ))
        .await
        .map_err(|e| MessageError::Validation(e.to_string()))?;

        audit_service::record(
            AuditLog::new(tenant_id, Some(user_id), "message.mark_all_read", "message"),
            db,
        )
        .await;

        Ok(())
    }

    /// 删除消息（软删除）。
    pub async fn delete(
        id: Uuid,
        tenant_id: Uuid,
        user_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<()> {
        let model = message::Entity::find_by_id(id)
            .one(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?
            .filter(|m| m.tenant_id == tenant_id && m.deleted_at.is_none())
            .ok_or_else(|| MessageError::NotFound(format!("消息不存在: {id}")))?;

        if model.recipient_id != user_id {
            return Err(MessageError::Validation("只能删除自己的消息".to_string()));
        }

        let current_version = model.version;
        let mut active: message::ActiveModel = model.into();
        active.version = Set(current_version + 1);
        active.deleted_at = Set(Some(Utc::now()));
        active.updated_at = Set(Utc::now());
        active.updated_by = Set(user_id);
        active
            .update(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        audit_service::record(
            AuditLog::new(tenant_id, Some(user_id), "message.delete", "message")
                .with_resource_id(id),
            db,
        )
        .await;

        Ok(())
    }

    pub(crate) fn model_to_resp(m: &message::Model) -> MessageResp {
        MessageResp {
            id: m.id,
            tenant_id: m.tenant_id,
            template_id: m.template_id,
            sender_id: m.sender_id,
            sender_type: m.sender_type.clone(),
            recipient_id: m.recipient_id,
            recipient_type: m.recipient_type.clone(),
            title: m.title.clone(),
            body: m.body.clone(),
            priority: m.priority.clone(),
            business_type: m.business_type.clone(),
            business_id: m.business_id,
            is_read: m.is_read,
            read_at: m.read_at,
            is_archived: m.is_archived,
            status: m.status.clone(),
            sent_at: m.sent_at,
            created_at: m.created_at,
            updated_at: m.updated_at,
            version: m.version,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::Utc;

    fn sample_model() -> message::Model {
        message::Model {
            id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
            tenant_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(),
            template_id: None,
            sender_id: None,
            sender_type: "system".to_string(),
            recipient_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
            recipient_type: "user".to_string(),
            title: "测试消息".to_string(),
            body: "消息内容".to_string(),
            priority: "normal".to_string(),
            business_type: None,
            business_id: None,
            is_read: false,
            read_at: None,
            is_archived: false,
            archived_at: None,
            sent_at: None,
            status: "sent".to_string(),
            created_at: Utc::now(),
            updated_at: Utc::now(),
            created_by: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000004").unwrap(),
            updated_by: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000004").unwrap(),
            deleted_at: None,
            version: 1,
        }
    }

    #[test]
    fn model_to_resp_maps_all_fields() {
        let m = sample_model();
        let resp = MessageService::model_to_resp(&m);
        assert_eq!(resp.id, m.id);
        assert_eq!(resp.tenant_id, m.tenant_id);
        assert_eq!(resp.title, "测试消息");
        assert_eq!(resp.body, "消息内容");
        assert_eq!(resp.priority, "normal");
        assert_eq!(resp.is_read, false);
        assert_eq!(resp.status, "sent");
        assert_eq!(resp.version, 1);
    }

    #[test]
    fn model_to_resp_preserves_optional_fields() {
        let m = sample_model();
        let resp = MessageService::model_to_resp(&m);
        assert_eq!(resp.template_id, None);
        assert_eq!(resp.sender_id, None);
        assert_eq!(resp.business_type, None);
        assert_eq!(resp.read_at, None);
        assert_eq!(resp.sent_at, None);
    }
}
