use chrono::Utc;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use uuid::Uuid;

use crate::dto::{MessageSubscriptionResp, UpdateSubscriptionReq};
use crate::entity::message_subscription;
use crate::error::{MessageError, MessageResult};
use erp_core::error::check_version;

/// 消息订阅偏好服务。
pub struct SubscriptionService;

impl SubscriptionService {
    /// 获取用户订阅偏好。
    pub async fn get(
        tenant_id: Uuid,
        user_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<MessageSubscriptionResp> {
        let model = message_subscription::Entity::find()
            .filter(message_subscription::Column::TenantId.eq(tenant_id))
            .filter(message_subscription::Column::UserId.eq(user_id))
            .filter(message_subscription::Column::DeletedAt.is_null())
            .one(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?
            .ok_or_else(|| MessageError::NotFound("订阅偏好不存在".to_string()))?;

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

    /// 创建或更新用户订阅偏好（upsert）。
    pub async fn upsert(
        tenant_id: Uuid,
        user_id: Uuid,
        req: &UpdateSubscriptionReq,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<MessageSubscriptionResp> {
        let existing = message_subscription::Entity::find()
            .filter(message_subscription::Column::TenantId.eq(tenant_id))
            .filter(message_subscription::Column::UserId.eq(user_id))
            .filter(message_subscription::Column::DeletedAt.is_null())
            .one(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        let now = Utc::now();

        if let Some(model) = existing {
            let current_version = model.version;
            let next_ver = check_version(req.version, current_version)
                .map_err(|_| MessageError::VersionMismatch)?;
            let mut active: message_subscription::ActiveModel = model.into();
            if let Some(types) = &req.notification_types {
                active.notification_types = Set(Some(types.clone()));
            }
            if let Some(prefs) = &req.channel_preferences {
                active.channel_preferences = Set(Some(prefs.clone()));
            }
            if let Some(dnd) = req.dnd_enabled {
                active.dnd_enabled = Set(dnd);
            }
            if let Some(ref start) = req.dnd_start {
                active.dnd_start = Set(Some(start.clone()));
            }
            if let Some(ref end) = req.dnd_end {
                active.dnd_end = Set(Some(end.clone()));
            }
            active.updated_at = Set(now);
            active.updated_by = Set(user_id);
            active.version = Set(next_ver);

            let updated = active
                .update(db)
                .await
                .map_err(|e| MessageError::Validation(e.to_string()))?;

            Ok(Self::model_to_resp(&updated))
        } else {
            let id = Uuid::now_v7();

            let model = message_subscription::ActiveModel {
                id: Set(id),
                tenant_id: Set(tenant_id),
                user_id: Set(user_id),
                notification_types: Set(req.notification_types.clone()),
                channel_preferences: Set(req.channel_preferences.clone()),
                dnd_enabled: Set(req.dnd_enabled.unwrap_or(false)),
                dnd_start: Set(req.dnd_start.clone()),
                dnd_end: Set(req.dnd_end.clone()),
                created_at: Set(now),
                updated_at: Set(now),
                created_by: Set(user_id),
                updated_by: Set(user_id),
                deleted_at: Set(None),
                version: Set(1),
            };

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

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

    pub(crate) fn model_to_resp(m: &message_subscription::Model) -> MessageSubscriptionResp {
        MessageSubscriptionResp {
            id: m.id,
            tenant_id: m.tenant_id,
            user_id: m.user_id,
            notification_types: m.notification_types.clone(),
            channel_preferences: m.channel_preferences.clone(),
            dnd_enabled: m.dnd_enabled,
            dnd_start: m.dnd_start.clone(),
            dnd_end: m.dnd_end.clone(),
            created_at: m.created_at,
            updated_at: m.updated_at,
            version: m.version,
        }
    }
}

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

    #[test]
    fn model_to_resp_maps_all_fields() {
        let m = message_subscription::Model {
            id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
            tenant_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(),
            user_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
            notification_types: Some(serde_json::json!(["appointment"])),
            channel_preferences: Some(serde_json::json!(["in_app"])),
            dnd_enabled: true,
            dnd_start: Some("22:00".to_string()),
            dnd_end: Some("08:00".to_string()),
            created_at: Utc::now(),
            updated_at: Utc::now(),
            created_by: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
            updated_by: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
            deleted_at: None,
            version: 1,
        };
        let resp = SubscriptionService::model_to_resp(&m);
        assert_eq!(resp.user_id, m.user_id);
        assert_eq!(resp.dnd_enabled, true);
        assert_eq!(resp.dnd_start, Some("22:00".to_string()));
        assert_eq!(resp.dnd_end, Some("08:00".to_string()));
        assert_eq!(resp.version, 1);
    }
}
