use chrono::Utc;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, EntityTrait, PaginatorTrait, QueryFilter, Set,
};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use uuid::Uuid;

use erp_core::sea_orm_ext::bump_version;

use erp_core::error::AppResult;

use crate::dto::{PluginEntityResp, PluginHealthResp, PluginPermissionResp, PluginResp};
use crate::dynamic_table::DynamicTableManager;
use crate::engine::PluginEngine;
use crate::entity::{plugin, plugin_entity, plugin_event_subscription};
use crate::error::PluginError;
use crate::manifest::{PluginManifest, parse_manifest};

pub struct PluginService;

impl PluginService {
    /// 上传插件: 解析 manifest + 存储 wasm_binary + status=uploaded
    pub async fn upload(
        tenant_id: Uuid,
        operator_id: Uuid,
        wasm_binary: Vec<u8>,
        manifest_toml: &str,
        db: &sea_orm::DatabaseConnection,
    ) -> AppResult<PluginResp> {
        // 解析 manifest
        let manifest = parse_manifest(manifest_toml)?;

        // 安全扫描
        let validation =
            crate::plugin_validator::validate_plugin_security(&manifest, wasm_binary.len())?;
        if !validation.valid {
            return Err(PluginError::ValidationError(format!(
                "插件安全校验失败: {}",
                validation.errors.join("; ")
            ))
            .into());
        }

        // 计算 WASM hash
        let mut hasher = Sha256::new();
        hasher.update(&wasm_binary);
        let wasm_hash = format!("{:x}", hasher.finalize());

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

        // 序列化 manifest 为 JSON
        let manifest_json = serde_json::to_value(&manifest)
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        let model = plugin::ActiveModel {
            id: Set(plugin_id),
            tenant_id: Set(tenant_id),
            name: Set(manifest.metadata.name.clone()),
            plugin_version: Set(manifest.metadata.version.clone()),
            description: Set(if manifest.metadata.description.is_empty() {
                None
            } else {
                Some(manifest.metadata.description.clone())
            }),
            author: Set(if manifest.metadata.author.is_empty() {
                None
            } else {
                Some(manifest.metadata.author.clone())
            }),
            status: Set("uploaded".to_string()),
            manifest_json: Set(manifest_json),
            wasm_binary: Set(wasm_binary),
            wasm_hash: Set(wasm_hash),
            config_json: Set(serde_json::json!({})),
            error_message: Set(None),
            installed_at: Set(None),
            enabled_at: Set(None),
            created_at: Set(now),
            updated_at: Set(now),
            created_by: Set(Some(operator_id)),
            updated_by: Set(Some(operator_id)),
            deleted_at: Set(None),
            version: Set(1),
        };

        let model = model.insert(db).await?;

        Ok(plugin_model_to_resp(&model, &manifest, vec![]))
    }

    /// 安装插件: 创建动态表 + 注册 entity 记录 + 注册事件订阅 + 注册权限 + status=installed
    pub async fn install(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        validate_status(&model.status, "uploaded")?;

        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        let now = Utc::now();

        // 创建动态表 + 注册 entity 记录
        let mut entity_resps = Vec::new();
        if let Some(schema) = &manifest.schema {
            for (i, entity_def) in schema.entities.iter().enumerate() {
                let table_name =
                    DynamicTableManager::table_name(&manifest.metadata.id, &entity_def.name);
                tracing::info!(step = i, entity = %entity_def.name, table = %table_name, "Creating dynamic table");

                // 创建动态表
                DynamicTableManager::create_table(db, &manifest.metadata.id, entity_def).await
                    .map_err(|e| {
                        tracing::error!(entity = %entity_def.name, table = %table_name, error = %e, "Failed to create dynamic table");
                        e
                    })?;

                // 注册 entity 记录
                let entity_id = Uuid::now_v7();
                let entity_model = plugin_entity::ActiveModel {
                    id: Set(entity_id),
                    tenant_id: Set(tenant_id),
                    plugin_id: Set(plugin_id),
                    entity_name: Set(entity_def.name.clone()),
                    table_name: Set(table_name.clone()),
                    schema_json: Set(serde_json::to_value(entity_def).unwrap_or_default()),
                    manifest_id: Set(manifest.metadata.id.clone()),
                    is_public: Set(entity_def.is_public.unwrap_or(false)),
                    created_at: Set(now),
                    updated_at: Set(now),
                    created_by: Set(Some(operator_id)),
                    updated_by: Set(Some(operator_id)),
                    deleted_at: Set(None),
                    version: Set(1),
                };
                entity_model.insert(db).await?;

                entity_resps.push(PluginEntityResp {
                    name: entity_def.name.clone(),
                    display_name: entity_def.display_name.clone(),
                    table_name,
                });
            }
        }

        // 注册事件订阅
        if let Some(events) = &manifest.events {
            for pattern in &events.subscribe {
                let sub_id = Uuid::now_v7();
                let sub_model = plugin_event_subscription::ActiveModel {
                    id: Set(sub_id),
                    plugin_id: Set(plugin_id),
                    event_pattern: Set(pattern.clone()),
                    created_at: Set(now),
                };
                sub_model.insert(db).await?;
            }
        }

        // 注册插件声明的权限到 permissions 表
        tracing::info!("Registering plugin permissions");
        if let Some(perms) = &manifest.permissions {
            register_plugin_permissions(
                db,
                tenant_id,
                operator_id,
                &manifest.metadata.id,
                perms,
                &now,
            )
            .await
            .map_err(|e| {
                tracing::error!(error = %e, "Failed to register permissions");
                e
            })?;
        }

        // 将插件权限自动分配给 admin 角色
        tracing::info!("Granting plugin permissions to admin role");
        grant_permissions_to_admin(db, tenant_id, &manifest.metadata.id).await?;

        // 加载到内存
        tracing::info!(manifest_id = %manifest.metadata.id, "Loading plugin into engine");
        engine
            .load(&manifest.metadata.id, &model.wasm_binary, manifest.clone())
            .await?;

        // 更新状态
        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.status = Set("installed".to_string());
        active.installed_at = Set(Some(now));
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        let model = active.update(db).await?;

        Ok(plugin_model_to_resp(&model, &manifest, entity_resps))
    }

    /// 启用插件: engine.initialize + start_event_listener + status=running
    pub async fn enable(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        validate_status_any(&model.status, &["installed", "disabled"])?;

        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        let plugin_manifest_id = &manifest.metadata.id;

        // 确保插件权限已分配给 admin 角色（幂等操作）
        grant_permissions_to_admin(db, tenant_id, plugin_manifest_id).await?;

        // 如果之前是 disabled 状态，需要先卸载再重新加载到内存
        // （disable 只改内存状态但不从 DashMap 移除）
        if model.status == "disabled" {
            engine.unload(plugin_manifest_id).await.ok();
            engine
                .load(plugin_manifest_id, &model.wasm_binary, manifest.clone())
                .await?;
        }

        // 初始化（非致命：WASM 插件可能不包含 initialize 逻辑，失败不阻塞启用）
        let init_error = match engine.initialize(plugin_manifest_id).await {
            Ok(()) => None,
            Err(e) => {
                tracing::warn!(plugin = %plugin_manifest_id, error = %e, "插件初始化失败（非致命，继续启用）");
                Some(format!("初始化警告: {}", e))
            }
        };

        // 启动事件监听（非致命）
        if let Err(e) = engine.start_event_listener(plugin_manifest_id).await {
            tracing::warn!(plugin = %plugin_manifest_id, error = %e, "事件监听启动失败（非致命，继续启用）");
        }

        let now = Utc::now();
        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.status = Set("running".to_string());
        active.enabled_at = Set(Some(now));
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        active.error_message = Set(init_error);
        let model = active.update(db).await?;

        let entity_resps = find_plugin_entities(plugin_id, tenant_id, db).await?;
        Ok(plugin_model_to_resp(&model, &manifest, entity_resps))
    }

    /// 禁用插件: engine.disable + cancel 事件订阅 + status=disabled
    pub async fn disable(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        validate_status_any(&model.status, &["running", "enabled"])?;

        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        // 禁用引擎
        engine.disable(&manifest.metadata.id).await?;

        let now = Utc::now();
        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.status = Set("disabled".to_string());
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        let model = active.update(db).await?;

        let entity_resps = find_plugin_entities(plugin_id, tenant_id, db).await?;
        Ok(plugin_model_to_resp(&model, &manifest, entity_resps))
    }

    /// 卸载插件: unload + 有条件地 drop 动态表 + 清理权限 + status=uninstalled
    pub async fn uninstall(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        validate_status_any(&model.status, &["installed", "disabled"])?;

        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        // 卸载（如果 disabled 状态，engine 可能仍在内存中）
        engine.unload(&manifest.metadata.id).await.ok();

        // 软删除当前租户的 entity 记录
        let now = Utc::now();
        let tenant_entities = plugin_entity::Entity::find()
            .filter(plugin_entity::Column::PluginId.eq(plugin_id))
            .filter(plugin_entity::Column::TenantId.eq(tenant_id))
            .filter(plugin_entity::Column::DeletedAt.is_null())
            .all(db)
            .await?;

        for entity in &tenant_entities {
            let mut active: plugin_entity::ActiveModel = entity.clone().into();
            active.version = Set(bump_version(&active.version));
            active.deleted_at = Set(Some(now));
            active.updated_at = Set(now);
            active.updated_by = Set(Some(operator_id));
            active.update(db).await?;
        }

        // 仅当没有其他租户的活跃 entity 记录引用相同的 table_name 时才 drop 表
        if let Some(schema) = &manifest.schema {
            for entity_def in &schema.entities {
                let table_name =
                    DynamicTableManager::table_name(&manifest.metadata.id, &entity_def.name);

                // 检查是否还有其他租户的活跃 entity 记录引用此表
                let other_tenants_count = plugin_entity::Entity::find()
                    .filter(plugin_entity::Column::TableName.eq(&table_name))
                    .filter(plugin_entity::Column::TenantId.ne(tenant_id))
                    .filter(plugin_entity::Column::DeletedAt.is_null())
                    .count(db)
                    .await?;

                if other_tenants_count == 0 {
                    // 没有其他租户使用，安全删除
                    DynamicTableManager::drop_table(db, &manifest.metadata.id, &entity_def.name)
                        .await
                        .ok();
                }
            }
        }

        // 清理此插件注册的权限
        unregister_plugin_permissions(db, tenant_id, &manifest.metadata.id).await?;

        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.status = Set("uninstalled".to_string());
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        let model = active.update(db).await?;

        Ok(plugin_model_to_resp(&model, &manifest, vec![]))
    }

    /// 列表查询
    pub async fn list(
        tenant_id: Uuid,
        page: u64,
        page_size: u64,
        status: Option<&str>,
        search: Option<&str>,
        db: &sea_orm::DatabaseConnection,
    ) -> AppResult<(Vec<PluginResp>, u64)> {
        let mut query = plugin::Entity::find()
            .filter(plugin::Column::TenantId.eq(tenant_id))
            .filter(plugin::Column::DeletedAt.is_null());

        if let Some(s) = status {
            query = query.filter(plugin::Column::Status.eq(s));
        }
        if let Some(q) = search {
            query = query.filter(
                plugin::Column::Name
                    .contains(q)
                    .or(plugin::Column::Description.contains(q)),
            );
        }

        let paginator = query.clone().paginate(db, page_size);

        let total = paginator.num_items().await?;
        let models = paginator.fetch_page(page.saturating_sub(1)).await?;

        let mut resps = Vec::with_capacity(models.len());

        // 批量查询所有插件的 entities（N+1 → 2 固定查询）
        let plugin_ids: Vec<Uuid> = models.iter().map(|m| m.id).collect();
        let entities_map = find_batch_plugin_entities(&plugin_ids, tenant_id, db).await;

        for model in models {
            let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
                .unwrap_or_else(|_| PluginManifest {
                    metadata: crate::manifest::PluginMetadata {
                        id: String::new(),
                        name: String::new(),
                        version: String::new(),
                        description: String::new(),
                        author: String::new(),
                        min_platform_version: None,
                        dependencies: vec![],
                    },
                    schema: None,
                    events: None,
                    ui: None,
                    permissions: None,
                    settings: None,
                    numbering: None,
                    templates: None,
                    trigger_events: None,
                });
            let entities = entities_map.get(&model.id).cloned().unwrap_or_default();
            resps.push(plugin_model_to_resp(&model, &manifest, entities));
        }

        Ok((resps, total))
    }

    /// 按 ID 获取详情
    pub async fn get_by_id(
        plugin_id: Uuid,
        tenant_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;
        let entities = find_plugin_entities(plugin_id, tenant_id, db).await?;
        Ok(plugin_model_to_resp(&model, &manifest, entities))
    }

    /// 更新配置
    pub async fn update_config(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        config: serde_json::Value,
        expected_version: i32,
        db: &sea_orm::DatabaseConnection,
        event_bus: Option<&erp_core::events::EventBus>,
    ) -> AppResult<PluginResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;

        erp_core::error::check_version(expected_version, model.version)?;

        // 校验配置值是否符合 manifest settings 声明
        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;
        if let Some(settings) = &manifest.settings {
            validate_plugin_settings(
                config.as_object().ok_or_else(|| {
                    PluginError::ValidationError("config 必须是 JSON 对象".to_string())
                })?,
                &settings.fields,
            )?;
        }

        let now = Utc::now();
        let manifest_id = manifest.metadata.id.clone();
        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.config_json = Set(config);
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        let model = active.update(db).await?;

        // 发布配置变更事件
        if let Some(bus) = event_bus {
            let event = erp_core::events::DomainEvent::new(
                "plugin.config.updated",
                tenant_id,
                serde_json::json!({
                    "plugin_id": manifest_id,
                    "updated_by": operator_id,
                }),
            );
            bus.publish(event, db).await;
        }

        let entities = find_plugin_entities(plugin_id, tenant_id, db)
            .await
            .unwrap_or_default();
        Ok(plugin_model_to_resp(&model, &manifest, entities))
    }

    /// 健康检查
    pub async fn health_check(
        plugin_id: Uuid,
        tenant_id: Uuid,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginHealthResp> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        let details = engine.health_check(&manifest.metadata.id).await?;

        Ok(PluginHealthResp {
            plugin_id,
            status: details
                .get("status")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown")
                .to_string(),
            details,
        })
    }

    /// 获取插件 Schema
    pub async fn get_schema(
        plugin_id: Uuid,
        tenant_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> AppResult<serde_json::Value> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        let manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        // 构建 schema 响应：entities + ui 页面配置 + settings + numbering + trigger_events
        let mut result = serde_json::Map::new();
        if let Some(schema) = &manifest.schema {
            result.insert(
                "entities".to_string(),
                serde_json::to_value(&schema.entities).unwrap_or_default(),
            );
        }
        if let Some(ui) = &manifest.ui {
            result.insert(
                "ui".to_string(),
                serde_json::to_value(ui).unwrap_or_default(),
            );
        }
        if let Some(settings) = &manifest.settings {
            result.insert(
                "settings".to_string(),
                serde_json::to_value(settings).unwrap_or_default(),
            );
        }
        if let Some(numbering) = &manifest.numbering {
            result.insert(
                "numbering".to_string(),
                serde_json::to_value(numbering).unwrap_or_default(),
            );
        }
        if let Some(triggers) = &manifest.trigger_events {
            result.insert(
                "trigger_events".to_string(),
                serde_json::to_value(triggers).unwrap_or_default(),
            );
        }
        Ok(serde_json::Value::Object(result))
    }

    /// 清除插件记录（软删除，仅限已卸载状态）
    pub async fn purge(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        db: &sea_orm::DatabaseConnection,
    ) -> AppResult<()> {
        let model = find_plugin(plugin_id, tenant_id, db).await?;
        validate_status_any(&model.status, &["uninstalled", "uploaded"])?;
        let now = Utc::now();
        let mut active: plugin::ActiveModel = model.into();
        active.version = Set(bump_version(&active.version));
        active.deleted_at = Set(Some(now));
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        active.update(db).await?;
        Ok(())
    }

    /// 热更新插件 — 上传新版本 WASM + manifest，对比 schema 变更，执行增量 DDL
    ///
    /// 流程：
    /// 1. 解析新 manifest
    /// 2. 获取当前插件信息
    /// 3. 对比 schema 变更，为新增实体创建表
    /// 4. 卸载旧 WASM，加载新 WASM
    /// 5. 更新数据库记录
    /// 6. 失败时保持旧版本继续运行（回滚）
    pub async fn upgrade(
        plugin_id: Uuid,
        tenant_id: Uuid,
        operator_id: Uuid,
        new_wasm: Vec<u8>,
        new_manifest_toml: &str,
        db: &sea_orm::DatabaseConnection,
        engine: &PluginEngine,
    ) -> AppResult<PluginResp> {
        let new_manifest = parse_manifest(new_manifest_toml)?;

        let model = find_plugin(plugin_id, tenant_id, db).await?;
        let old_manifest: PluginManifest = serde_json::from_value(model.manifest_json.clone())
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

        let old_version = old_manifest.metadata.version.clone();
        let new_version = new_manifest.metadata.version.clone();

        if old_manifest.metadata.id != new_manifest.metadata.id {
            return Err(PluginError::InvalidManifest(format!(
                "插件 ID 不匹配: 旧={}, 新={}",
                old_manifest.metadata.id, new_manifest.metadata.id
            ))
            .into());
        }

        let plugin_manifest_id = &new_manifest.metadata.id;

        // 对比 schema — 为新增实体创建动态表 + 已有实体字段演进
        if let Some(new_schema) = &new_manifest.schema {
            let old_schema = old_manifest.schema.as_ref();

            for entity in &new_schema.entities {
                let old_entity =
                    old_schema.and_then(|s| s.entities.iter().find(|e| e.name == entity.name));

                match old_entity {
                    None => {
                        tracing::info!(entity = %entity.name, "创建新增实体表");
                        DynamicTableManager::create_table(db, plugin_manifest_id, entity).await?;
                    }
                    Some(old) => {
                        let diff = DynamicTableManager::diff_entity_fields(old, entity);
                        if !diff.new_filterable.is_empty() || !diff.new_searchable.is_empty() {
                            tracing::info!(
                                entity = %entity.name,
                                new_cols = diff.new_filterable.len(),
                                new_search = diff.new_searchable.len(),
                                "Schema 演进：新增 Generated Column"
                            );
                            DynamicTableManager::alter_add_generated_columns(
                                db,
                                plugin_manifest_id,
                                entity,
                                &diff,
                            )
                            .await?;
                        }
                    }
                }
            }
        }

        // 先加载新版本到临时 key，确保成功后再替换旧版本（原子回滚）
        let temp_id = format!("{}__upgrade_{}", plugin_manifest_id, Uuid::now_v7());
        engine
            .load(&temp_id, &new_wasm, new_manifest.clone())
            .await
            .map_err(|e| {
                tracing::error!(error = %e, "新版本 WASM 加载失败，旧版本仍在运行");
                e
            })?;

        // 新版本加载成功，卸载旧版本并重命名新版本为正式 key
        engine.unload(plugin_manifest_id).await.ok();
        engine.rename_plugin(&temp_id, plugin_manifest_id).await?;

        // 更新数据库记录
        let wasm_hash = {
            let mut hasher = Sha256::new();
            hasher.update(&new_wasm);
            format!("{:x}", hasher.finalize())
        };

        let now = Utc::now();
        let mut active: plugin::ActiveModel = model.into();
        active.wasm_binary = Set(new_wasm);
        active.wasm_hash = Set(wasm_hash);
        active.manifest_json = Set(serde_json::to_value(&new_manifest)
            .map_err(|e| PluginError::InvalidManifest(e.to_string()))?);
        active.plugin_version = Set(new_version.clone());
        active.updated_at = Set(now);
        active.updated_by = Set(Some(operator_id));
        active.version = Set(bump_version(&active.version));

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

        // 更新 plugin_entities 表中的 schema_json
        if let Some(schema) = &new_manifest.schema {
            for entity in &schema.entities {
                let entity_model = plugin_entity::Entity::find()
                    .filter(plugin_entity::Column::PluginId.eq(plugin_id))
                    .filter(plugin_entity::Column::TenantId.eq(tenant_id))
                    .filter(plugin_entity::Column::EntityName.eq(&entity.name))
                    .filter(plugin_entity::Column::DeletedAt.is_null())
                    .one(db)
                    .await
                    .map_err(|e| PluginError::DatabaseError(e.to_string()))?;

                if let Some(em) = entity_model {
                    let mut active: plugin_entity::ActiveModel = em.into();
                    active.version = Set(bump_version(&active.version));
                    active.schema_json = Set(serde_json::to_value(entity)
                        .map_err(|e| PluginError::InvalidManifest(e.to_string()))?);
                    active.updated_at = Set(now);
                    active.updated_by = Set(Some(operator_id));
                    active
                        .update(db)
                        .await
                        .map_err(|e| PluginError::DatabaseError(e.to_string()))?;
                }
            }
        }

        tracing::info!(
            plugin_id = %plugin_id,
            old_version = %old_version,
            new_version = %new_version,
            "插件热更新成功"
        );

        let entities = find_plugin_entities(plugin_id, tenant_id, db).await?;
        Ok(plugin_model_to_resp(&updated, &new_manifest, entities))
    }
}

// ---- 内部辅助 ----

async fn find_plugin(
    plugin_id: Uuid,
    tenant_id: Uuid,
    db: &sea_orm::DatabaseConnection,
) -> AppResult<plugin::Model> {
    plugin::Entity::find_by_id(plugin_id)
        .one(db)
        .await?
        .filter(|m| m.tenant_id == tenant_id && m.deleted_at.is_none())
        .ok_or_else(|| erp_core::error::AppError::NotFound(format!("插件 {} 不存在", plugin_id)))
}

/// 公开的插件查询 — 供 handler 使用
pub async fn find_plugin_model(
    plugin_id: Uuid,
    tenant_id: Uuid,
    db: &sea_orm::DatabaseConnection,
) -> AppResult<plugin::Model> {
    find_plugin(plugin_id, tenant_id, db).await
}

/// 批量查询多插件的 entities，返回 plugin_id → Vec<PluginEntityResp> 映射。
async fn find_batch_plugin_entities(
    plugin_ids: &[Uuid],
    tenant_id: Uuid,
    db: &sea_orm::DatabaseConnection,
) -> HashMap<Uuid, Vec<PluginEntityResp>> {
    if plugin_ids.is_empty() {
        return HashMap::new();
    }

    let entities = plugin_entity::Entity::find()
        .filter(plugin_entity::Column::PluginId.is_in(plugin_ids.iter().copied()))
        .filter(plugin_entity::Column::TenantId.eq(tenant_id))
        .filter(plugin_entity::Column::DeletedAt.is_null())
        .all(db)
        .await
        .unwrap_or_default();

    let mut result: HashMap<Uuid, Vec<PluginEntityResp>> = HashMap::new();
    for e in entities {
        result
            .entry(e.plugin_id)
            .or_default()
            .push(PluginEntityResp {
                name: e.entity_name.clone(),
                display_name: e.entity_name,
                table_name: e.table_name,
            });
    }
    result
}

async fn find_plugin_entities(
    plugin_id: Uuid,
    tenant_id: Uuid,
    db: &sea_orm::DatabaseConnection,
) -> AppResult<Vec<PluginEntityResp>> {
    let entities = plugin_entity::Entity::find()
        .filter(plugin_entity::Column::PluginId.eq(plugin_id))
        .filter(plugin_entity::Column::TenantId.eq(tenant_id))
        .filter(plugin_entity::Column::DeletedAt.is_null())
        .all(db)
        .await?;

    Ok(entities
        .into_iter()
        .map(|e| PluginEntityResp {
            name: e.entity_name.clone(),
            display_name: e.entity_name,
            table_name: e.table_name,
        })
        .collect())
}

fn validate_status(actual: &str, expected: &str) -> AppResult<()> {
    if actual != expected {
        return Err(PluginError::InvalidState {
            expected: expected.to_string(),
            actual: actual.to_string(),
        }
        .into());
    }
    Ok(())
}

fn validate_status_any(actual: &str, expected: &[&str]) -> AppResult<()> {
    if !expected.contains(&actual) {
        return Err(PluginError::InvalidState {
            expected: expected.join(" 或 "),
            actual: actual.to_string(),
        }
        .into());
    }
    Ok(())
}

/// 校验配置值是否符合 manifest settings 声明
fn validate_plugin_settings(
    config: &serde_json::Map<String, serde_json::Value>,
    fields: &[crate::manifest::PluginSettingField],
) -> AppResult<()> {
    use crate::manifest::PluginSettingType;

    for field in fields {
        let value = config.get(&field.name);

        // 必填校验
        if field.required {
            match value {
                None | Some(serde_json::Value::Null) => {
                    return Err(PluginError::ValidationError(format!(
                        "配置项 '{}' ({}) 为必填",
                        field.name, field.display_name
                    ))
                    .into());
                }
                Some(serde_json::Value::String(s)) if s.is_empty() => {
                    return Err(PluginError::ValidationError(format!(
                        "配置项 '{}' ({}) 不能为空",
                        field.name, field.display_name
                    ))
                    .into());
                }
                _ => {}
            }
        }

        // 类型校验
        if let Some(val) = value
            && !val.is_null()
        {
            let type_ok = match field.field_type {
                PluginSettingType::Text => val.is_string(),
                PluginSettingType::Number => val.is_number(),
                PluginSettingType::Boolean => val.is_boolean(),
                PluginSettingType::Select => val.is_string(),
                PluginSettingType::Multiselect => val.is_array(),
                PluginSettingType::Color => val.is_string(),
                PluginSettingType::Date => val.is_string(),
                PluginSettingType::Datetime => val.is_string(),
                PluginSettingType::Json => true,
            };
            if !type_ok {
                return Err(PluginError::ValidationError(format!(
                    "配置项 '{}' 类型错误，期望 {:?}",
                    field.name, field.field_type
                ))
                .into());
            }

            // 数值范围校验
            if let Some((min, max)) = field.range
                && let Some(n) = val.as_f64()
                && (n < min || n > max)
            {
                return Err(PluginError::ValidationError(format!(
                    "配置项 '{}' ({}) 的值 {} 超出范围 [{}, {}]",
                    field.name, field.display_name, n, min, max
                ))
                .into());
            }
        }
    }
    Ok(())
}

fn plugin_model_to_resp(
    model: &plugin::Model,
    manifest: &PluginManifest,
    entities: Vec<PluginEntityResp>,
) -> PluginResp {
    let permissions = manifest.permissions.as_ref().map(|perms| {
        perms
            .iter()
            .map(|p| PluginPermissionResp {
                code: p.code.clone(),
                name: p.name.clone(),
                description: p.description.clone(),
            })
            .collect()
    });

    PluginResp {
        id: model.id,
        name: model.name.clone(),
        version: model.plugin_version.clone(),
        description: model.description.clone(),
        author: model.author.clone(),
        status: model.status.clone(),
        config: model.config_json.clone(),
        installed_at: model.installed_at,
        enabled_at: model.enabled_at,
        entities,
        permissions,
        record_version: model.version,
    }
}

/// 将插件声明的权限注册到 permissions 表。
///
/// 使用 raw SQL 避免依赖 erp-auth 的 entity 类型。
/// 权限码格式：`{plugin_manifest_id}.{code}`（如 `erp-crm.customer.list`）。
/// 使用 `ON CONFLICT DO NOTHING` 保证幂等。
async fn register_plugin_permissions(
    db: &sea_orm::DatabaseConnection,
    tenant_id: Uuid,
    operator_id: Uuid,
    plugin_manifest_id: &str,
    perms: &[crate::manifest::PluginPermission],
    now: &chrono::DateTime<chrono::Utc>,
) -> AppResult<()> {
    for perm in perms {
        let full_code = format!("{}.{}", plugin_manifest_id, perm.code);
        let resource = plugin_manifest_id.to_string();
        let action = perm.code.clone();
        let description: Option<String> = if perm.description.is_empty() {
            None
        } else {
            Some(perm.description.clone())
        };

        let sql = r#"
            INSERT INTO permissions (id, tenant_id, code, name, resource, action, description, created_at, updated_at, created_by, updated_by, deleted_at, version)
            VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $7, $8, $8, NULL, 1)
            ON CONFLICT (tenant_id, code) WHERE deleted_at IS NULL DO NOTHING
        "#;

        db.execute(sea_orm::Statement::from_sql_and_values(
            sea_orm::DatabaseBackend::Postgres,
            sql,
            vec![
                sea_orm::Value::from(tenant_id),
                sea_orm::Value::from(full_code.clone()),
                sea_orm::Value::from(perm.name.clone()),
                sea_orm::Value::from(resource),
                sea_orm::Value::from(action),
                sea_orm::Value::from(description),
                sea_orm::Value::from(*now),
                sea_orm::Value::from(operator_id),
            ],
        ))
        .await
        .map_err(|e| {
            tracing::error!(
                plugin = plugin_manifest_id,
                permission = %full_code,
                error = %e,
                "注册插件权限失败"
            );
            PluginError::DatabaseError(format!("注册插件权限 {} 失败: {}", full_code, e))
        })?;
    }

    tracing::info!(
        plugin = plugin_manifest_id,
        count = perms.len(),
        tenant_id = %tenant_id,
        "插件权限注册完成"
    );
    Ok(())
}

/// 将插件的所有权限分配给 admin 角色。
///
/// 使用 raw SQL 按 manifest_id 前缀匹配权限，INSERT 到 role_permissions。
/// ON CONFLICT DO NOTHING 保证幂等。
pub async fn grant_permissions_to_admin(
    db: &sea_orm::DatabaseConnection,
    tenant_id: Uuid,
    plugin_manifest_id: &str,
) -> AppResult<()> {
    let prefix = format!("{}.%", plugin_manifest_id);

    let sql = r#"
        INSERT INTO role_permissions (tenant_id, role_id, permission_id, data_scope, created_at, updated_at, created_by, updated_by, deleted_at, version)
        SELECT
            p.tenant_id,
            r.id,
            p.id,
            'all',
            NOW(), NOW(),
            r.id, r.id,
            NULL, 1
        FROM permissions p
        CROSS JOIN roles r
        WHERE p.tenant_id = $1
          AND r.tenant_id = $1
          AND r.code = 'admin'
          AND r.deleted_at IS NULL
          AND p.code LIKE $2
          AND p.deleted_at IS NULL
          AND NOT EXISTS (
            SELECT 1 FROM role_permissions rp
            WHERE rp.permission_id = p.id
              AND rp.role_id = r.id
              AND rp.deleted_at IS NULL
          )
        ON CONFLICT (role_id, permission_id) DO NOTHING
    "#;

    let result = db
        .execute(sea_orm::Statement::from_sql_and_values(
            sea_orm::DatabaseBackend::Postgres,
            sql,
            vec![
                sea_orm::Value::from(tenant_id),
                sea_orm::Value::from(prefix.clone()),
            ],
        ))
        .await
        .map_err(|e| {
            tracing::error!(
                plugin = plugin_manifest_id,
                error = %e,
                "分配插件权限给 admin 角色失败"
            );
            PluginError::DatabaseError(format!("分配插件权限给 admin 角色失败: {}", e))
        })?;

    let rows = result.rows_affected();
    tracing::info!(
        plugin = plugin_manifest_id,
        rows_affected = rows,
        tenant_id = %tenant_id,
        "插件权限已分配给 admin 角色"
    );
    Ok(())
}

/// 清理插件注册的权限（软删除）。
///
/// 使用 raw SQL 按前缀匹配清理：`{plugin_manifest_id}.%`。
/// 同时清理 role_permissions 中对这些权限的关联。
async fn unregister_plugin_permissions(
    db: &sea_orm::DatabaseConnection,
    tenant_id: Uuid,
    plugin_manifest_id: &str,
) -> AppResult<()> {
    let prefix = format!("{}.%", plugin_manifest_id);
    let now = chrono::Utc::now();

    // 先软删除 role_permissions 中的关联
    let rp_sql = r#"
        UPDATE role_permissions
        SET deleted_at = $1, updated_at = $1
        WHERE permission_id IN (
            SELECT id FROM permissions
            WHERE tenant_id = $2
              AND code LIKE $3
              AND deleted_at IS NULL
        )
        AND deleted_at IS NULL
    "#;
    db.execute(sea_orm::Statement::from_sql_and_values(
        sea_orm::DatabaseBackend::Postgres,
        rp_sql,
        vec![
            sea_orm::Value::from(now),
            sea_orm::Value::from(tenant_id),
            sea_orm::Value::from(prefix.clone()),
        ],
    ))
    .await
    .map_err(|e| {
        tracing::error!(
            plugin = plugin_manifest_id,
            error = %e,
            "清理插件权限角色关联失败"
        );
        PluginError::DatabaseError(format!("清理插件权限角色关联失败: {}", e))
    })?;

    // 再软删除 permissions
    let perm_sql = r#"
        UPDATE permissions
        SET deleted_at = $1, updated_at = $1
        WHERE tenant_id = $2
          AND code LIKE $3
          AND deleted_at IS NULL
    "#;
    db.execute(sea_orm::Statement::from_sql_and_values(
        sea_orm::DatabaseBackend::Postgres,
        perm_sql,
        vec![
            sea_orm::Value::from(now),
            sea_orm::Value::from(tenant_id),
            sea_orm::Value::from(prefix),
        ],
    ))
    .await
    .map_err(|e| {
        tracing::error!(
            plugin = plugin_manifest_id,
            error = %e,
            "清理插件权限失败"
        );
        PluginError::DatabaseError(format!("清理插件权限失败: {}", e))
    })?;

    tracing::info!(
        plugin = plugin_manifest_id,
        tenant_id = %tenant_id,
        "插件权限清理完成"
    );
    Ok(())
}
