use serde::{Deserialize, Serialize};

use crate::error::{PluginError, PluginResult};

/// 插件清单 — 从 TOML 文件解析
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginManifest {
    pub metadata: PluginMetadata,
    pub schema: Option<PluginSchema>,
    pub events: Option<PluginEvents>,
    pub ui: Option<PluginUi>,
    pub permissions: Option<Vec<PluginPermission>>,
    /// 插件配置项声明 — 平台自动生成配置页面
    #[serde(default)]
    pub settings: Option<PluginSettings>,
    /// 编号规则声明 — 绑定实体字段到自动编号
    #[serde(default)]
    pub numbering: Option<Vec<PluginNumbering>>,
    /// 打印模板声明
    #[serde(default)]
    pub templates: Option<Vec<PluginTemplate>>,
    /// 触发事件声明 — 数据 CRUD 时自动发布域事件
    #[serde(default)]
    pub trigger_events: Option<Vec<PluginTriggerEvent>>,
}

/// 插件元数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginMetadata {
    pub id: String,
    pub name: String,
    pub version: String,
    #[serde(default)]
    pub description: String,
    #[serde(default)]
    pub author: String,
    #[serde(default)]
    pub min_platform_version: Option<String>,
    #[serde(default)]
    pub dependencies: Vec<String>,
}

/// 插件 Schema — 定义动态实体
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSchema {
    pub entities: Vec<PluginEntity>,
}

/// 插件实体定义
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginEntity {
    pub name: String,
    pub display_name: String,
    #[serde(default)]
    pub fields: Vec<PluginField>,
    #[serde(default)]
    pub indexes: Vec<PluginIndex>,
    #[serde(default)]
    pub relations: Vec<PluginRelation>,
    #[serde(default)]
    pub data_scope: Option<bool>, // 是否启用行级数据权限
    #[serde(default)]
    pub is_public: Option<bool>, // 是否可被其他插件引用
    #[serde(default)]
    pub importable: Option<bool>, // 是否支持数据导入
    #[serde(default)]
    pub exportable: Option<bool>, // 是否支持数据导出
}

/// 字段校验规则
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldValidation {
    pub pattern: Option<String>, // 正则表达式
    pub message: Option<String>, // 校验失败提示
}

/// 插件字段定义
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginField {
    pub name: String,
    pub field_type: PluginFieldType,
    #[serde(default)]
    pub required: bool,
    #[serde(default)]
    pub unique: bool,
    pub default: Option<serde_json::Value>,
    pub display_name: Option<String>,
    pub ui_widget: Option<String>,
    pub options: Option<Vec<serde_json::Value>>,
    #[serde(default)]
    pub searchable: Option<bool>,
    #[serde(default)]
    pub filterable: Option<bool>,
    #[serde(default)]
    pub sortable: Option<bool>,
    #[serde(default)]
    pub visible_when: Option<String>,
    pub ref_entity: Option<String>,             // 外键引用的实体名
    pub ref_label_field: Option<String>,        // entity_select 下拉显示的字段名
    pub ref_search_fields: Option<Vec<String>>, // entity_select 搜索匹配的字段列表
    pub cascade_from: Option<String>,           // 级联过滤的来源字段（当前实体）
    pub cascade_filter: Option<String>,         // 级联过滤的目标字段（引用实体的字段）
    pub validation: Option<FieldValidation>,    // 字段校验规则
    #[serde(default)]
    pub no_cycle: Option<bool>, // 禁止循环引用
    #[serde(default)]
    pub scope_role: Option<String>, // 标记为数据权限的"所有者"字段
    pub ref_plugin: Option<String>,             // 跨插件引用的目标插件 manifest ID（如 "erp-crm"）
    pub ref_fallback_label: Option<String>,     // 目标插件未安装时的降级显示文本
}

/// 字段类型
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PluginFieldType {
    String,
    Integer,
    Float,
    Boolean,
    Date,
    DateTime,
    Json,
    Uuid,
    Decimal,
}

impl PluginFieldType {
    /// Generated Column 的 SQL 类型
    pub fn generated_sql_type(&self) -> &'static str {
        match self {
            Self::String | Self::Json => "TEXT",
            Self::Integer => "INTEGER",
            Self::Float => "DOUBLE PRECISION",
            Self::Decimal => "NUMERIC",
            Self::Boolean => "BOOLEAN",
            Self::Date => "DATE",
            // TIMESTAMPTZ cast 不是 immutable，generated column 不支持类型转换，存为 TEXT
            Self::DateTime => "TEXT",
            Self::Uuid => "UUID",
        }
    }

    /// Generated Column 的表达式
    pub fn generated_expr(&self, field_name: &str) -> String {
        match self {
            Self::String | Self::Json | Self::DateTime => format!("data->>'{}'", field_name),
            _ => format!("(data->>'{}')::{}", field_name, self.generated_sql_type()),
        }
    }

    /// 该类型是否适合生成 Generated Column
    pub fn supports_generated_column(&self) -> bool {
        !matches!(self, Self::Json)
    }
}

impl PluginField {
    /// 测试辅助：构造一个全默认值的 PluginField
    #[cfg(test)]
    pub fn default_for_field() -> Self {
        Self {
            name: String::new(),
            field_type: PluginFieldType::String,
            required: false,
            unique: false,
            default: None,
            display_name: None,
            ui_widget: None,
            options: None,
            searchable: None,
            filterable: None,
            sortable: None,
            visible_when: None,
            ref_entity: None,
            ref_label_field: None,
            ref_search_fields: None,
            cascade_from: None,
            cascade_filter: None,
            validation: None,
            no_cycle: None,
            scope_role: None,
            ref_plugin: None,
            ref_fallback_label: None,
        }
    }
}

/// 索引定义
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginIndex {
    pub name: String,
    pub fields: Vec<String>,
    #[serde(default)]
    pub unique: bool,
}

/// 级联删除策略
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum OnDeleteStrategy {
    Nullify,  // 置空外键字段
    Cascade,  // 级联软删除
    Restrict, // 存在关联时拒绝删除
}

/// 实体关联关系声明
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginRelation {
    pub entity: String,
    pub foreign_key: String,
    pub on_delete: OnDeleteStrategy,
    #[serde(default)]
    pub name: Option<String>, // 关联名称（UI 显示用）
    #[serde(default, alias = "type")]
    pub relation_type: Option<String>, // "one_to_many" | "many_to_one" | "many_to_many"
    #[serde(default)]
    pub display_field: Option<String>, // 关联记录的显示字段
}

/// 事件订阅配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginEvents {
    pub subscribe: Vec<String>,
}

/// UI 页面配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginUi {
    pub pages: Vec<PluginPageType>,
}

/// 插件页面类型（tagged enum，TOML 中通过 type 字段区分）
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum PluginPageType {
    #[serde(rename = "crud")]
    Crud {
        entity: String,
        label: String,
        #[serde(default)]
        icon: Option<String>,
        #[serde(default)]
        enable_search: Option<bool>,
        #[serde(default)]
        enable_views: Option<Vec<String>>,
    },
    #[serde(rename = "tree")]
    Tree {
        entity: String,
        label: String,
        #[serde(default)]
        icon: Option<String>,
        id_field: String,
        parent_field: String,
        label_field: String,
    },
    #[serde(rename = "detail")]
    Detail {
        entity: String,
        label: String,
        sections: Vec<PluginSection>,
    },
    #[serde(rename = "tabs")]
    Tabs {
        label: String,
        #[serde(default)]
        icon: Option<String>,
        tabs: Vec<PluginPageType>,
    },
    #[serde(rename = "graph")]
    Graph {
        entity: String,
        label: String,
        #[serde(default)]
        icon: Option<String>,
        relationship_entity: String,
        source_field: String,
        target_field: String,
        edge_label_field: String,
        node_label_field: String,
    },
    #[serde(rename = "dashboard")]
    Dashboard {
        label: String,
        #[serde(default)]
        icon: Option<String>,
        #[serde(default)]
        widgets: Vec<PluginWidget>,
    },
    #[serde(rename = "kanban")]
    Kanban {
        entity: String,
        label: String,
        #[serde(default)]
        icon: Option<String>,
        lane_field: String,
        #[serde(default)]
        lane_order: Vec<String>,
        card_title_field: String,
        #[serde(default)]
        card_subtitle_field: Option<String>,
        #[serde(default)]
        card_fields: Vec<String>,
        #[serde(default)]
        enable_drag: Option<bool>,
    },
}

/// Dashboard Widget 类型
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum PluginWidget {
    #[serde(rename = "stat_cards")]
    StatCards { label: String, cards: Vec<StatCard> },
    #[serde(rename = "action_list")]
    ActionList {
        label: String,
        #[serde(default)]
        max_items: Option<u32>,
        queries: Vec<ActionQuery>,
    },
    #[serde(rename = "funnel")]
    Funnel {
        label: String,
        entity: String,
        lane_field: String,
        #[serde(default)]
        value_field: Option<String>,
        lane_order: Vec<String>,
    },
    #[serde(rename = "card_list")]
    CardList {
        label: String,
        entity: String,
        #[serde(default)]
        filter: Option<String>,
        #[serde(default)]
        max_items: Option<u32>,
        title_field: String,
        #[serde(default)]
        subtitle_field: Option<String>,
        #[serde(default)]
        tags: Vec<String>,
    },
}

/// 统计卡片
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct StatCard {
    pub entity: String,
    #[serde(default)]
    pub aggregate: Option<String>,
    #[serde(default)]
    pub field: Option<String>,
    #[serde(default)]
    pub filter: Option<String>,
    pub label: String,
    #[serde(default)]
    pub icon: Option<String>,
    #[serde(default)]
    pub color: Option<String>,
}

/// 待办行动查询
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ActionQuery {
    pub entity: String,
    #[serde(default)]
    pub filter: Option<String>,
    #[serde(default)]
    pub sort: Option<String>,
    pub label_field: String,
    #[serde(default)]
    pub subtitle_field: Option<String>,
    pub action: String,
    #[serde(default)]
    pub icon: Option<String>,
}

/// 插件页面区段（用于 detail 页面类型）
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum PluginSection {
    #[serde(rename = "fields")]
    Fields { label: String, fields: Vec<String> },
    #[serde(rename = "crud")]
    Crud {
        label: String,
        entity: String,
        #[serde(default)]
        filter_field: Option<String>,
        #[serde(default)]
        enable_views: Option<Vec<String>>,
    },
}

/// 权限定义
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginPermission {
    pub code: String,
    pub name: String,
    #[serde(default)]
    pub description: String,
    #[serde(default)]
    pub data_scope_levels: Option<Vec<String>>, // 支持的数据范围等级
}

// ============================================================
// P2 平台通用服务 — manifest 扩展
// ============================================================

/// 插件配置项声明 — 平台根据此声明自动生成配置页面
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSettings {
    pub fields: Vec<PluginSettingField>,
}

/// 单个配置字段
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSettingField {
    pub name: String,
    pub display_name: String,
    #[serde(default)]
    pub field_type: PluginSettingType,
    #[serde(default)]
    pub default_value: Option<serde_json::Value>,
    #[serde(default)]
    pub required: bool,
    #[serde(default)]
    pub description: Option<String>,
    /// select/multiselect 类型的选项列表
    #[serde(default)]
    pub options: Option<Vec<serde_json::Value>>,
    /// 数值范围 [min, max]
    #[serde(default)]
    pub range: Option<(f64, f64)>,
    /// 分组名称 — 同组的字段在 UI 上放在一起
    #[serde(default)]
    pub group: Option<String>,
}

/// 配置字段类型
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum PluginSettingType {
    #[default]
    Text,
    Number,
    Boolean,
    Select,
    Multiselect,
    Color,
    Date,
    Datetime,
    Json,
}

/// 编号规则声明 — 绑定实体字段到自动编号
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginNumbering {
    pub entity: String,
    pub field: String,
    #[serde(default)]
    pub prefix: String,
    #[serde(default = "default_numbering_format")]
    pub format: String,
    #[serde(default)]
    pub reset_rule: PluginNumberingReset,
    #[serde(default = "default_seq_length")]
    pub seq_length: u32,
    #[serde(default)]
    pub separator: Option<String>,
}

fn default_numbering_format() -> String {
    "{PREFIX}-{YEAR}-{SEQ:4}".to_string()
}

fn default_seq_length() -> u32 {
    4
}

/// 编号重置周期
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum PluginNumberingReset {
    #[default]
    Never,
    Daily,
    Monthly,
    Yearly,
}

/// 打印模板声明
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginTemplate {
    pub name: String,
    pub display_name: String,
    pub entity: String,
    #[serde(default = "default_template_format")]
    pub format: String,
    /// 模板文件路径（相对于插件根目录）
    #[serde(default)]
    pub template_file: Option<String>,
    /// 内联 HTML 模板（与 template_file 二选一）
    #[serde(default)]
    pub template_html: Option<String>,
}

fn default_template_format() -> String {
    "pdf".to_string()
}

/// 触发事件声明 — 数据 CRUD 操作时自动发布域事件
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginTriggerEvent {
    pub name: String,
    pub display_name: String,
    #[serde(default)]
    pub description: String,
    pub entity: String,
    pub on: PluginTriggerOn,
}

/// 触发时机
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PluginTriggerOn {
    Create,
    Update,
    Delete,
    CreateOrUpdate,
}

/// 从 TOML 字符串解析插件清单
pub fn parse_manifest(toml_str: &str) -> PluginResult<PluginManifest> {
    let manifest: PluginManifest =
        toml::from_str(toml_str).map_err(|e| PluginError::InvalidManifest(e.to_string()))?;

    // 验证必填字段
    if manifest.metadata.id.is_empty() {
        return Err(PluginError::InvalidManifest(
            "metadata.id 不能为空".to_string(),
        ));
    }
    if manifest.metadata.name.is_empty() {
        return Err(PluginError::InvalidManifest(
            "metadata.name 不能为空".to_string(),
        ));
    }

    // 验证实体名称
    if let Some(schema) = &manifest.schema {
        for entity in &schema.entities {
            if entity.name.is_empty() {
                return Err(PluginError::InvalidManifest(
                    "entity.name 不能为空".to_string(),
                ));
            }
            // 验证实体名称只包含合法字符
            if !entity
                .name
                .chars()
                .all(|c| c.is_ascii_alphanumeric() || c == '_')
            {
                return Err(PluginError::InvalidManifest(format!(
                    "entity.name '{}' 只能包含字母、数字和下划线",
                    entity.name
                )));
            }
        }
    }

    // 验证页面类型配置
    if let Some(ui) = &manifest.ui {
        validate_pages(&ui.pages)?;
    }

    // 验证编号规则引用的实体存在
    if let Some(numbering) = &manifest.numbering {
        let entity_names: Vec<&str> = manifest
            .schema
            .as_ref()
            .map(|s| s.entities.iter().map(|e| e.name.as_str()).collect())
            .unwrap_or_default();
        for rule in numbering {
            if !entity_names.contains(&rule.entity.as_str()) {
                return Err(PluginError::InvalidManifest(format!(
                    "numbering 引用了不存在的 entity '{}'",
                    rule.entity
                )));
            }
        }
    }

    // 验证触发事件引用的实体存在
    if let Some(triggers) = &manifest.trigger_events {
        let entity_names: Vec<&str> = manifest
            .schema
            .as_ref()
            .map(|s| s.entities.iter().map(|e| e.name.as_str()).collect())
            .unwrap_or_default();
        for trigger in triggers {
            if !entity_names.contains(&trigger.entity.as_str()) {
                return Err(PluginError::InvalidManifest(format!(
                    "trigger_events 引用了不存在的 entity '{}'",
                    trigger.entity
                )));
            }
        }
    }

    Ok(manifest)
}

/// 递归验证页面配置
fn validate_pages(pages: &[PluginPageType]) -> PluginResult<()> {
    for page in pages {
        match page {
            PluginPageType::Crud { entity, .. } => {
                if entity.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "crud page 的 entity 不能为空".into(),
                    ));
                }
            }
            PluginPageType::Tree {
                entity: _,
                label: _,
                icon: _,
                id_field,
                parent_field,
                label_field,
            } => {
                if id_field.is_empty() || parent_field.is_empty() || label_field.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "tree page 的 id_field/parent_field/label_field 不能为空".into(),
                    ));
                }
            }
            PluginPageType::Detail {
                entity, sections, ..
            } => {
                if entity.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "detail page 的 entity 不能为空".into(),
                    ));
                }
                if sections.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "detail page 的 sections 不能为空".into(),
                    ));
                }
            }
            PluginPageType::Tabs { tabs, .. } => {
                if tabs.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "tabs page 的 tabs 不能为空".into(),
                    ));
                }
                validate_pages(tabs)?;
            }
            PluginPageType::Graph {
                entity,
                relationship_entity,
                source_field,
                target_field,
                ..
            } => {
                if entity.is_empty() || relationship_entity.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "graph page 的 entity/relationship_entity 不能为空".into(),
                    ));
                }
                if source_field.is_empty() || target_field.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "graph page 的 source_field/target_field 不能为空".into(),
                    ));
                }
            }
            PluginPageType::Dashboard { .. } => {
                // dashboard 无需额外验证
            }
            PluginPageType::Kanban {
                entity,
                lane_field,
                card_title_field,
                ..
            } => {
                if entity.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "kanban page 的 entity 不能为空".into(),
                    ));
                }
                if lane_field.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "kanban page 的 lane_field 不能为空".into(),
                    ));
                }
                if card_title_field.is_empty() {
                    return Err(PluginError::InvalidManifest(
                        "kanban page 的 card_title_field 不能为空".into(),
                    ));
                }
            }
        }
    }
    Ok(())
}

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

    #[test]
    fn parse_minimal_manifest() {
        let toml = r#"
[metadata]
id = "test-plugin"
name = "测试插件"
version = "0.1.0"
"#;
        let manifest = parse_manifest(toml).unwrap();
        assert_eq!(manifest.metadata.id, "test-plugin");
        assert_eq!(manifest.metadata.name, "测试插件");
        assert!(manifest.schema.is_none());
    }

    #[test]
    fn parse_full_manifest() {
        let toml = r#"
[metadata]
id = "inventory"
name = "进销存"
version = "1.0.0"
description = "简单进销存管理"
author = "ERP Team"

[schema]
[[schema.entities]]
name = "product"
display_name = "商品"

[[schema.entities.fields]]
name = "sku"
field_type = "string"
required = true
unique = true
display_name = "SKU 编码"

[[schema.entities.fields]]
name = "price"
field_type = "decimal"
required = true
display_name = "价格"

[events]
subscribe = ["workflow.task.completed", "order.*"]

[ui]
[[ui.pages]]
type = "crud"
entity = "product"
label = "商品管理"
icon = "ShoppingOutlined"

[[permissions]]
code = "product.list"
name = "查看商品"
description = "查看商品列表"
"#;
        let manifest = parse_manifest(toml).unwrap();
        assert_eq!(manifest.metadata.id, "inventory");
        let schema = manifest.schema.unwrap();
        assert_eq!(schema.entities.len(), 1);
        assert_eq!(schema.entities[0].name, "product");
        assert_eq!(schema.entities[0].fields.len(), 2);
        let events = manifest.events.unwrap();
        assert_eq!(events.subscribe.len(), 2);
        let ui = manifest.ui.unwrap();
        assert_eq!(ui.pages.len(), 1);
        // 验证新格式解析正确
        match &ui.pages[0] {
            PluginPageType::Crud { entity, label, .. } => {
                assert_eq!(entity, "product");
                assert_eq!(label, "商品管理");
            }
            _ => panic!("Expected Crud page type"),
        }
    }

    #[test]
    fn reject_empty_id() {
        let toml = r#"
[metadata]
id = ""
name = "测试"
version = "0.1.0"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn reject_invalid_entity_name() {
        let toml = r#"
[metadata]
id = "test"
name = "测试"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "my-table"
display_name = "表格"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn parse_manifest_with_new_fields_and_page_types() {
        let toml = r#"
[metadata]
id = "test-plugin"
name = "Test"
version = "0.1.0"
description = "Test"
author = "Test"
min_platform_version = "0.1.0"

[[schema.entities]]
name = "customer"
display_name = "客户"

[[schema.entities.fields]]
name = "code"
field_type = "string"
required = true
display_name = "编码"
unique = true
searchable = true
filterable = true
visible_when = "customer_type == 'enterprise'"

[[ui.pages]]
type = "tabs"
label = "客户管理"
icon = "team"

[[ui.pages.tabs]]
label = "客户列表"
type = "crud"
entity = "customer"
enable_search = true
enable_views = ["table", "timeline"]

[[ui.pages]]
type = "detail"
entity = "customer"
label = "客户详情"

[[ui.pages.sections]]
type = "fields"
label = "基本信息"
fields = ["code", "name"]
"#;
        let manifest = parse_manifest(toml).expect("should parse");
        let field = &manifest.schema.as_ref().unwrap().entities[0].fields[0];
        assert_eq!(field.searchable, Some(true));
        assert_eq!(field.filterable, Some(true));
        assert_eq!(
            field.visible_when.as_deref(),
            Some("customer_type == 'enterprise'")
        );
        // 验证页面类型解析
        let ui = manifest.ui.as_ref().unwrap();
        assert_eq!(ui.pages.len(), 2);
        // tabs 页面
        match &ui.pages[0] {
            PluginPageType::Tabs { label, tabs, .. } => {
                assert_eq!(label, "客户管理");
                assert_eq!(tabs.len(), 1);
            }
            _ => panic!("Expected Tabs page type"),
        }
        // detail 页面
        match &ui.pages[1] {
            PluginPageType::Detail {
                entity, sections, ..
            } => {
                assert_eq!(entity, "customer");
                assert_eq!(sections.len(), 1);
            }
            _ => panic!("Expected Detail page type"),
        }
    }

    #[test]
    fn reject_empty_entity_in_crud_page() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[ui]
[[ui.pages]]
type = "crud"
entity = ""
label = "测试"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn reject_empty_tabs_in_tabs_page() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[ui]
[[ui.pages]]
type = "tabs"
label = "空标签页"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn field_type_to_sql_mapping() {
        assert_eq!(PluginFieldType::String.generated_sql_type(), "TEXT");
        assert_eq!(PluginFieldType::Integer.generated_sql_type(), "INTEGER");
        assert_eq!(
            PluginFieldType::Float.generated_sql_type(),
            "DOUBLE PRECISION"
        );
        assert_eq!(PluginFieldType::Decimal.generated_sql_type(), "NUMERIC");
        assert_eq!(PluginFieldType::Boolean.generated_sql_type(), "BOOLEAN");
        assert_eq!(PluginFieldType::Date.generated_sql_type(), "DATE");
        assert_eq!(PluginFieldType::DateTime.generated_sql_type(), "TEXT");
        assert_eq!(PluginFieldType::Uuid.generated_sql_type(), "UUID");
        assert_eq!(PluginFieldType::Json.generated_sql_type(), "TEXT");
    }

    #[test]
    fn field_type_generated_expression() {
        assert_eq!(
            PluginFieldType::String.generated_expr("name"),
            "data->>'name'"
        );
        assert_eq!(
            PluginFieldType::Integer.generated_expr("age"),
            "(data->>'age')::INTEGER"
        );
        assert_eq!(
            PluginFieldType::Uuid.generated_expr("ref_id"),
            "(data->>'ref_id')::UUID"
        );
    }

    #[test]
    fn parse_field_with_ref_entity() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "contact"
display_name = "联系人"

[[schema.entities.fields]]
name = "customer_id"
field_type = "uuid"
required = true
display_name = "所属客户"
ref_entity = "customer"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let field = &manifest.schema.unwrap().entities[0].fields[0];
        assert_eq!(field.ref_entity.as_deref(), Some("customer"));
    }

    #[test]
    fn parse_field_with_validation() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "contact"
display_name = "联系人"

[[schema.entities.fields]]
name = "phone"
field_type = "string"
display_name = "手机号"
validation = { pattern = "^1[3-9]\\d{9}$", message = "手机号格式不正确" }
"#;
        let manifest = parse_manifest(toml).unwrap();
        let field = &manifest.schema.unwrap().entities[0].fields[0];
        let v = field.validation.as_ref().unwrap();
        assert_eq!(v.pattern.as_deref(), Some("^1[3-9]\\d{9}$"));
        assert_eq!(v.message.as_deref(), Some("手机号格式不正确"));
    }

    #[test]
    fn parse_field_with_no_cycle() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "customer"
display_name = "客户"

[[schema.entities.fields]]
name = "parent_id"
field_type = "uuid"
display_name = "上级客户"
ref_entity = "customer"
no_cycle = true
"#;
        let manifest = parse_manifest(toml).unwrap();
        let field = &manifest.schema.unwrap().entities[0].fields[0];
        assert_eq!(field.no_cycle, Some(true));
        assert_eq!(field.ref_entity.as_deref(), Some("customer"));
    }

    #[test]
    fn parse_entity_with_relations() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "customer"
display_name = "客户"

[[schema.entities.fields]]
name = "code"
field_type = "string"
required = true
display_name = "编码"

[[schema.entities.relations]]
entity = "contact"
foreign_key = "customer_id"
on_delete = "cascade"

[[schema.entities.relations]]
entity = "customer_tag"
foreign_key = "customer_id"
on_delete = "cascade"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let entity = &manifest.schema.unwrap().entities[0];
        assert_eq!(entity.relations.len(), 2);
        assert_eq!(entity.relations[0].entity, "contact");
        assert_eq!(entity.relations[0].foreign_key, "customer_id");
        assert!(matches!(
            entity.relations[0].on_delete,
            OnDeleteStrategy::Cascade
        ));
    }

    #[test]
    fn parse_entity_with_data_scope() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "customer"
display_name = "客户"
data_scope = true

[[schema.entities.fields]]
name = "owner_id"
field_type = "uuid"
display_name = "负责人"
scope_role = "owner"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let entity = &manifest.schema.unwrap().entities[0];
        assert_eq!(entity.data_scope, Some(true));
        assert_eq!(entity.fields[0].scope_role.as_deref(), Some("owner"));
    }

    #[test]
    fn parse_permission_with_data_scope_levels() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[[permissions]]
code = "customer.list"
name = "查看客户"
data_scope_levels = ["self", "department", "department_tree", "all"]
"#;
        let manifest = parse_manifest(toml).unwrap();
        let perm = &manifest.permissions.unwrap()[0];
        assert_eq!(perm.data_scope_levels.as_ref().unwrap().len(), 4);
    }

    #[test]
    fn parse_field_with_entity_select() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "contact"
display_name = "联系人"

[[schema.entities.fields]]
name = "customer_id"
field_type = "uuid"
required = true
display_name = "所属客户"
ui_widget = "entity_select"
ref_label_field = "name"
ref_search_fields = ["name", "code"]
"#;
        let manifest = parse_manifest(toml).unwrap();
        let field = &manifest.schema.unwrap().entities[0].fields[0];
        assert_eq!(field.ui_widget.as_deref(), Some("entity_select"));
        assert_eq!(field.ref_label_field.as_deref(), Some("name"));
        assert_eq!(
            field.ref_search_fields.as_deref(),
            Some(&["name".to_string(), "code".to_string()][..])
        );
    }

    #[test]
    fn parse_field_with_cascade() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "communication"
display_name = "沟通记录"

[[schema.entities.fields]]
name = "customer_id"
field_type = "uuid"
required = true
display_name = "关联客户"

[[schema.entities.fields]]
name = "contact_id"
field_type = "uuid"
display_name = "关联联系人"
ui_widget = "entity_select"
ref_label_field = "name"
ref_search_fields = ["name"]
cascade_from = "customer_id"
cascade_filter = "customer_id"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let fields = &manifest.schema.unwrap().entities[0].fields;
        let contact_field = &fields[1];
        assert_eq!(contact_field.ui_widget.as_deref(), Some("entity_select"));
        assert_eq!(contact_field.cascade_from.as_deref(), Some("customer_id"));
        assert_eq!(contact_field.cascade_filter.as_deref(), Some("customer_id"));
    }

    #[test]
    fn parse_field_with_cross_plugin_ref() {
        let toml = r#"
[metadata]
id = "erp-inventory"
name = "进销存"
version = "0.2.0"
dependencies = ["erp-crm"]

[schema]
[[schema.entities]]
name = "sales_order"
display_name = "销售订单"

[[schema.entities.fields]]
name = "customer_id"
field_type = "uuid"
display_name = "客户"
ui_widget = "entity_select"
ref_plugin = "erp-crm"
ref_entity = "customer"
ref_label_field = "name"
ref_search_fields = ["name", "code"]
ref_fallback_label = "CRM 客户"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let field = &manifest.schema.unwrap().entities[0].fields[0];
        assert_eq!(field.ref_plugin.as_deref(), Some("erp-crm"));
        assert_eq!(field.ref_entity.as_deref(), Some("customer"));
        assert_eq!(field.ref_label_field.as_deref(), Some("name"));
        assert_eq!(field.ref_fallback_label.as_deref(), Some("CRM 客户"));
        assert_eq!(manifest.metadata.dependencies, vec!["erp-crm"]);
    }

    #[test]
    fn parse_entity_with_is_public() {
        let toml = r#"
[metadata]
id = "erp-crm"
name = "CRM"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "customer"
display_name = "客户"
is_public = true

[[schema.entities]]
name = "internal_config"
display_name = "内部配置"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let entities = &manifest.schema.unwrap().entities;
        assert_eq!(entities[0].is_public, Some(true));
        assert_eq!(entities[1].is_public, None);
    }

    #[test]
    fn parse_relation_with_name_and_type() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "customer"
display_name = "客户"

[[schema.entities.fields]]
name = "code"
field_type = "string"
display_name = "编码"

[[schema.entities.relations]]
entity = "contact"
foreign_key = "customer_id"
on_delete = "cascade"
name = "contacts"
type = "one_to_many"
display_field = "name"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let relation = &manifest.schema.unwrap().entities[0].relations[0];
        assert_eq!(relation.entity, "contact");
        assert_eq!(relation.name.as_deref(), Some("contacts"));
        assert_eq!(relation.relation_type.as_deref(), Some("one_to_many"));
        assert_eq!(relation.display_field.as_deref(), Some("name"));
    }

    #[test]
    fn parse_kanban_page() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[ui]
[[ui.pages]]
type = "kanban"
entity = "customer"
label = "销售漏斗"
icon = "swap"
lane_field = "level"
lane_order = ["potential", "normal", "vip", "svip"]
card_title_field = "name"
card_subtitle_field = "code"
card_fields = ["region", "status"]
enable_drag = true
"#;
        let manifest = parse_manifest(toml).unwrap();
        let ui = manifest.ui.unwrap();
        assert_eq!(ui.pages.len(), 1);
        match &ui.pages[0] {
            PluginPageType::Kanban {
                entity,
                label,
                icon,
                lane_field,
                lane_order,
                card_title_field,
                card_subtitle_field,
                card_fields,
                enable_drag,
            } => {
                assert_eq!(entity, "customer");
                assert_eq!(label, "销售漏斗");
                assert_eq!(icon.as_deref(), Some("swap"));
                assert_eq!(lane_field, "level");
                assert_eq!(lane_order, &["potential", "normal", "vip", "svip"]);
                assert_eq!(card_title_field, "name");
                assert_eq!(card_subtitle_field.as_deref(), Some("code"));
                assert_eq!(card_fields, &["region", "status"]);
                assert_eq!(*enable_drag, Some(true));
            }
            _ => panic!("Expected Kanban page type"),
        }
    }

    #[test]
    fn reject_empty_entity_in_kanban_page() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[ui]
[[ui.pages]]
type = "kanban"
entity = ""
label = "测试"
lane_field = "status"
card_title_field = "name"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn reject_empty_lane_field_in_kanban_page() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[ui]
[[ui.pages]]
type = "kanban"
entity = "customer"
label = "测试"
lane_field = ""
card_title_field = "name"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    // ============================================================
    // P2 manifest 扩展测试
    // ============================================================

    #[test]
    fn parse_settings_section() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[settings]
[[settings.fields]]
name = "default_tax_rate"
display_name = "默认税率"
field_type = "number"
default_value = 0.13
range = [0.0, 1.0]
group = "财务"

[[settings.fields]]
name = "invoice_prefix"
display_name = "发票前缀"
field_type = "text"
default_value = "INV"

[[settings.fields]]
name = "auto_notify"
display_name = "自动通知"
field_type = "boolean"
default_value = true
description = "发票创建后是否自动发送通知"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let settings = manifest.settings.unwrap();
        assert_eq!(settings.fields.len(), 3);
        assert_eq!(settings.fields[0].name, "default_tax_rate");
        assert_eq!(settings.fields[0].range, Some((0.0, 1.0)));
        assert_eq!(settings.fields[0].group.as_deref(), Some("财务"));
        assert_eq!(settings.fields[1].name, "invoice_prefix");
        assert_eq!(settings.fields[2].name, "auto_notify");
        assert!(matches!(
            settings.fields[2].field_type,
            PluginSettingType::Boolean
        ));
    }

    #[test]
    fn parse_numbering_section() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "invoice"
display_name = "发票"

[[numbering]]
entity = "invoice"
field = "invoice_no"
prefix = "INV"
format = "{PREFIX}-{YEAR}-{SEQ:4}"
reset_rule = "yearly"
seq_length = 4
"#;
        let manifest = parse_manifest(toml).unwrap();
        let numbering = manifest.numbering.unwrap();
        assert_eq!(numbering.len(), 1);
        assert_eq!(numbering[0].entity, "invoice");
        assert_eq!(numbering[0].field, "invoice_no");
        assert_eq!(numbering[0].prefix, "INV");
        assert!(matches!(
            numbering[0].reset_rule,
            PluginNumberingReset::Yearly
        ));
    }

    #[test]
    fn reject_numbering_with_unknown_entity() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[[numbering]]
entity = "nonexistent"
field = "code"
prefix = "T"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn parse_trigger_events_section() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "invoice"
display_name = "发票"

[[trigger_events]]
name = "invoice.created"
display_name = "发票创建"
description = "新发票创建时触发"
entity = "invoice"
on = "create"

[[trigger_events]]
name = "invoice.overdue"
display_name = "发票逾期"
description = "发票超过付款期限未收款"
entity = "invoice"
on = "update"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let triggers = manifest.trigger_events.unwrap();
        assert_eq!(triggers.len(), 2);
        assert_eq!(triggers[0].name, "invoice.created");
        assert!(matches!(triggers[0].on, PluginTriggerOn::Create));
        assert_eq!(triggers[1].name, "invoice.overdue");
    }

    #[test]
    fn reject_trigger_event_with_unknown_entity() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[[trigger_events]]
name = "test.trigger"
display_name = "测试"
entity = "nonexistent"
on = "create"
"#;
        let result = parse_manifest(toml);
        assert!(result.is_err());
    }

    #[test]
    fn parse_entity_with_import_export() {
        let toml = r#"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "product"
display_name = "商品"
importable = true
exportable = true

[[schema.entities]]
name = "internal_log"
display_name = "内部日志"
"#;
        let manifest = parse_manifest(toml).unwrap();
        let entities = &manifest.schema.unwrap().entities;
        assert_eq!(entities[0].importable, Some(true));
        assert_eq!(entities[0].exportable, Some(true));
        assert_eq!(entities[1].importable, None);
    }

    #[test]
    fn parse_full_p2_manifest() {
        let toml = r#"
[metadata]
id = "erp-finance"
name = "财务/应收"
version = "0.1.0"
description = "财务管理与应收账款"
author = "ERP Team"

[schema]
[[schema.entities]]
name = "invoice"
display_name = "发票"
importable = true
exportable = true

[[schema.entities.fields]]
name = "invoice_no"
field_type = "string"
required = true
unique = true
display_name = "发票编号"

[[schema.entities.fields]]
name = "customer_id"
field_type = "uuid"
display_name = "客户"
ref_plugin = "erp-crm"
ref_entity = "customer"
ref_label_field = "name"
ref_search_fields = ["name"]
ref_fallback_label = "外部客户"

[[schema.entities]]
name = "payment"
display_name = "收款"

[settings]
[[settings.fields]]
name = "default_tax_rate"
display_name = "默认税率"
field_type = "number"
default_value = 0.13
group = "税务"

[[settings.fields]]
name = "invoice_prefix"
display_name = "发票前缀"
field_type = "text"
default_value = "INV"

[[numbering]]
entity = "invoice"
field = "invoice_no"
prefix = "INV"
format = "{PREFIX}-{YEAR}-{SEQ:4}"
reset_rule = "yearly"

[[trigger_events]]
name = "invoice.created"
display_name = "发票创建"
entity = "invoice"
on = "create"

[[permissions]]
code = "invoice.list"
name = "查看发票"

[[permissions]]
code = "invoice.manage"
name = "管理发票"
"#;
        let manifest = parse_manifest(toml).unwrap();
        assert_eq!(manifest.metadata.id, "erp-finance");

        // settings
        let settings = manifest.settings.unwrap();
        assert_eq!(settings.fields.len(), 2);

        // numbering
        let numbering = manifest.numbering.unwrap();
        assert_eq!(numbering.len(), 1);
        assert_eq!(numbering[0].entity, "invoice");

        // trigger_events
        let triggers = manifest.trigger_events.unwrap();
        assert_eq!(triggers.len(), 1);

        // import/export on entity
        let entities = &manifest.schema.unwrap().entities;
        assert_eq!(entities[0].importable, Some(true));
        assert_eq!(entities[0].exportable, Some(true));
    }

    #[test]
    fn parse_dashboard_with_widgets() {
        let toml = r##"
[metadata]
id = "test"
name = "Test"
version = "0.1.0"

[schema]
[[schema.entities]]
name = "invoice"
display_name = "发票"

[[schema.entities.fields]]
name = "status"
field_type = "string"
display_name = "状态"

[[schema.entities.fields]]
name = "amount"
field_type = "decimal"
display_name = "金额"

[ui]
[[ui.pages]]
type = "dashboard"
label = "工作台"
icon = "DashboardOutlined"

[[ui.pages.widgets]]
type = "stat_cards"
label = "财务概览"

[[ui.pages.widgets.cards]]
entity = "invoice"
aggregate = "count"
label = "总发票"
icon = "FileTextOutlined"
color = "#1890ff"

[[ui.pages.widgets.cards]]
entity = "invoice"
aggregate = "sum"
field = "amount"
filter = "status == 'pending'"
label = "待收金额"
icon = "DollarOutlined"
color = "#faad14"

[[ui.pages.widgets]]
type = "action_list"
label = "紧急待办"
max_items = 5

[[ui.pages.widgets.queries]]
entity = "invoice"
filter = "status == 'overdue'"
sort = "due_date asc"
label_field = "invoice_number"
subtitle_field = "amount"
action = "open_invoice"
icon = "warning"

[[ui.pages.widgets]]
type = "funnel"
label = "商机漏斗"
entity = "invoice"
lane_field = "status"
value_field = "amount"
lane_order = ["pending", "issued", "paid"]

[[ui.pages.widgets]]
type = "card_list"
label = "活跃项目"
entity = "invoice"
filter = "status == 'active'"
max_items = 10
title_field = "invoice_number"
subtitle_field = "amount"
tags = ["status"]
"##;
        let manifest = parse_manifest(toml).unwrap();
        let ui = manifest.ui.unwrap();
        assert_eq!(ui.pages.len(), 1);
        match &ui.pages[0] {
            PluginPageType::Dashboard {
                label,
                icon,
                widgets,
            } => {
                assert_eq!(label, "工作台");
                assert_eq!(icon.as_deref(), Some("DashboardOutlined"));
                assert_eq!(widgets.len(), 4);

                // stat_cards
                match &widgets[0] {
                    PluginWidget::StatCards { label, cards } => {
                        assert_eq!(label, "财务概览");
                        assert_eq!(cards.len(), 2);
                        assert_eq!(cards[0].entity, "invoice");
                        assert_eq!(cards[0].aggregate.as_deref(), Some("count"));
                        assert_eq!(cards[1].aggregate.as_deref(), Some("sum"));
                        assert_eq!(cards[1].filter.as_deref(), Some("status == 'pending'"));
                    }
                    _ => panic!("Expected StatCards"),
                }

                // action_list
                match &widgets[1] {
                    PluginWidget::ActionList {
                        label,
                        max_items,
                        queries,
                    } => {
                        assert_eq!(label, "紧急待办");
                        assert_eq!(*max_items, Some(5));
                        assert_eq!(queries.len(), 1);
                        assert_eq!(queries[0].entity, "invoice");
                        assert_eq!(queries[0].action, "open_invoice");
                    }
                    _ => panic!("Expected ActionList"),
                }

                // funnel
                match &widgets[2] {
                    PluginWidget::Funnel {
                        label,
                        entity,
                        lane_field,
                        value_field,
                        lane_order,
                    } => {
                        assert_eq!(label, "商机漏斗");
                        assert_eq!(entity, "invoice");
                        assert_eq!(lane_field, "status");
                        assert_eq!(value_field.as_deref(), Some("amount"));
                        assert_eq!(lane_order, &["pending", "issued", "paid"]);
                    }
                    _ => panic!("Expected Funnel"),
                }

                // card_list
                match &widgets[3] {
                    PluginWidget::CardList {
                        label,
                        entity,
                        title_field,
                        ..
                    } => {
                        assert_eq!(label, "活跃项目");
                        assert_eq!(entity, "invoice");
                        assert_eq!(title_field, "invoice_number");
                    }
                    _ => panic!("Expected CardList"),
                }
            }
            _ => panic!("Expected Dashboard page type"),
        }
    }
}
