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

use crate::dto::{CreateTemplateReq, MessageTemplateResp, UpdateTemplateReq};
use crate::entity::message_template;
use crate::error::{MessageError, MessageResult};

/// 消息模板服务。
pub struct TemplateService;

impl TemplateService {
    /// 查询模板列表。
    pub async fn list(
        tenant_id: Uuid,
        page: u64,
        page_size: u64,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<(Vec<MessageTemplateResp>, u64)> {
        let paginator = message_template::Entity::find()
            .filter(message_template::Column::TenantId.eq(tenant_id))
            .filter(message_template::Column::DeletedAt.is_null())
            .paginate(db, page_size);

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

        let page_index = page.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 create(
        tenant_id: Uuid,
        operator_id: Uuid,
        req: &CreateTemplateReq,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<MessageTemplateResp> {
        // 检查编码唯一性
        let existing = message_template::Entity::find()
            .filter(message_template::Column::TenantId.eq(tenant_id))
            .filter(message_template::Column::Code.eq(&req.code))
            .filter(message_template::Column::DeletedAt.is_null())
            .one(db)
            .await
            .map_err(|e| MessageError::Validation(e.to_string()))?;

        if existing.is_some() {
            return Err(MessageError::DuplicateTemplateCode(format!(
                "模板编码已存在: {}",
                req.code
            )));
        }

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

        let model = message_template::ActiveModel {
            id: Set(id),
            tenant_id: Set(tenant_id),
            name: Set(req.name.clone()),
            code: Set(req.code.clone()),
            channel: Set(req.channel.clone()),
            title_template: Set(req.title_template.clone()),
            body_template: Set(req.body_template.clone()),
            language: Set(req.language.clone()),
            created_at: Set(now),
            updated_at: Set(now),
            created_by: Set(operator_id),
            updated_by: Set(operator_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 async fn update(
        id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        req: &UpdateTemplateReq,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<MessageTemplateResp> {
        let model = message_template::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}")))?;

        let current_version = model.version;
        let next_ver = erp_core::error::check_version(req.version, current_version)
            .map_err(|_| MessageError::VersionMismatch)?;

        let mut active: message_template::ActiveModel = model.into();
        if let Some(name) = &req.name {
            active.name = Set(name.clone());
        }
        if let Some(title) = &req.title_template {
            active.title_template = Set(title.clone());
        }
        if let Some(body) = &req.body_template {
            active.body_template = Set(body.clone());
        }
        if let Some(lang) = &req.language {
            active.language = Set(lang.clone());
        }
        if let Some(channel) = &req.channel {
            active.channel = Set(channel.clone());
        }
        active.updated_at = Set(Utc::now());
        active.updated_by = Set(operator_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))
    }

    /// 软删除消息模板。
    pub async fn delete(
        id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> MessageResult<()> {
        let model = message_template::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}")))?;

        let current_version = model.version;
        let mut active: message_template::ActiveModel = model.into();
        active.deleted_at = Set(Some(Utc::now()));
        active.updated_at = Set(Utc::now());
        active.updated_by = Set(operator_id);
        active.version = Set(current_version + 1);

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

        Ok(())
    }

    /// 使用模板渲染消息内容。
    /// 支持 {{variable}} 格式的变量插值。
    pub fn render(template: &str, variables: &std::collections::HashMap<String, String>) -> String {
        let mut result = template.to_string();
        for (key, value) in variables {
            result = result.replace(&format!("{{{{{}}}}}", key), value);
        }
        result
    }

    pub(crate) fn model_to_resp(m: &message_template::Model) -> MessageTemplateResp {
        MessageTemplateResp {
            id: m.id,
            tenant_id: m.tenant_id,
            name: m.name.clone(),
            code: m.code.clone(),
            channel: m.channel.clone(),
            title_template: m.title_template.clone(),
            body_template: m.body_template.clone(),
            language: m.language.clone(),
            created_at: m.created_at,
            updated_at: m.updated_at,
            version: m.version,
        }
    }
}

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

    #[test]
    fn render_replaces_single_variable() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("name".to_string(), "张三".to_string());
        let result = TemplateService::render("您好，{{name}}", &vars);
        assert_eq!(result, "您好，张三");
    }

    #[test]
    fn render_replaces_multiple_variables() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("name".to_string(), "李四".to_string());
        vars.insert("code".to_string(), "ORD-001".to_string());
        let result = TemplateService::render("{{name}}，您的订单 {{code}} 已发货", &vars);
        assert_eq!(result, "李四，您的订单 ORD-001 已发货");
    }

    #[test]
    fn render_no_variables_returns_original() {
        let vars = std::collections::HashMap::new();
        let result = TemplateService::render("没有变量的模板", &vars);
        assert_eq!(result, "没有变量的模板");
    }

    #[test]
    fn render_missing_variable_leaves_placeholder() {
        let vars = std::collections::HashMap::new();
        let result = TemplateService::render("您好，{{name}}", &vars);
        assert_eq!(result, "您好，{{name}}");
    }

    #[test]
    fn render_same_variable_multiple_times() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("user".to_string(), "王五".to_string());
        let result = TemplateService::render("{{user}} 你好，{{user}} 的订单已确认", &vars);
        assert_eq!(result, "王五 你好，王五 的订单已确认");
    }

    #[test]
    fn render_empty_template() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("name".to_string(), "test".to_string());
        let result = TemplateService::render("", &vars);
        assert_eq!(result, "");
    }

    #[test]
    fn render_empty_variable_value() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("name".to_string(), "".to_string());
        let result = TemplateService::render("您好，{{name}}！", &vars);
        assert_eq!(result, "您好，！");
    }

    #[test]
    fn render_adjacent_variables() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("a".to_string(), "1".to_string());
        vars.insert("b".to_string(), "2".to_string());
        let result = TemplateService::render("{{a}}{{b}}", &vars);
        assert_eq!(result, "12");
    }

    #[test]
    fn render_extra_variables_not_in_template_are_ignored() {
        let mut vars = std::collections::HashMap::new();
        vars.insert("name".to_string(), "赵六".to_string());
        vars.insert("unused".to_string(), "ignore".to_string());
        let result = TemplateService::render("你好 {{name}}", &vars);
        assert_eq!(result, "你好 赵六");
    }

    #[test]
    fn model_to_resp_maps_all_fields() {
        let m = message_template::Model {
            id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
            tenant_id: uuid::Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(),
            name: "欢迎消息".to_string(),
            code: "WELCOME".to_string(),
            channel: "in_app".to_string(),
            title_template: "欢迎 {{name}}".to_string(),
            body_template: "{{name}}，欢迎使用".to_string(),
            language: "zh-CN".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::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: 2,
        };
        let resp = TemplateService::model_to_resp(&m);
        assert_eq!(resp.name, "欢迎消息");
        assert_eq!(resp.code, "WELCOME");
        assert_eq!(resp.channel, "in_app");
        assert_eq!(resp.language, "zh-CN");
        assert_eq!(resp.version, 2);
    }
}
