-- 评查点分组正式迁移脚本（业务大类根版本）
-- 目标：
--   一级分组 = 业务大类（合同 / 行政卷宗 / 后续新增业务）
--   二级分组 = 具体业务类型
--   规则集   = 挂在二级分组下
--   入口模块 = 绑定一级分组
--
-- 重要说明：
-- 1. 本脚本面向当前真实库结构：
--      - 已存在旧一级根（一级=具体文档类型）
--      - 已存在新一级业务根 root.contract / root.casefile
--      - 已存在默认二级分组 *.default / 通用
-- 2. 执行前必须先跑：
--      scripts/创建sql/precheck_rule_group_migration.sql
-- 3. 必须先备份：
--      leaudit_evaluation_point_groups
--      leaudit_rule_group_bindings
--      leaudit_rule_type_bindings
--      leaudit_document_types
-- 4. 本脚本不会立刻删除旧一级根，只做结构补齐 + 绑定迁移 + 汇总重建。

BEGIN;

-- =========================================================
-- 0. 补齐结构：一级分组入口模块字段
-- =========================================================
ALTER TABLE leaudit_evaluation_point_groups
ADD COLUMN IF NOT EXISTS entry_module_id BIGINT NULL REFERENCES leaudit_entry_modules(id);

CREATE INDEX IF NOT EXISTS idx_leaudit_ep_groups_entry_module
ON leaudit_evaluation_point_groups(entry_module_id);

-- =========================================================
-- 1. 保证一级业务大类根存在，并统一编码
--    当前统一使用：
--      root.contract  -> 合同
--      root.casefile  -> 行政卷宗
-- =========================================================
WITH root_candidates AS (
    SELECT
        'root.contract'::varchar AS code,
        '合同'::varchar AS name,
        '合同业务大类'::text AS description,
        1::bigint AS entry_module_id,
        10::int AS sort_order
    UNION ALL
    SELECT
        'root.casefile'::varchar,
        '行政卷宗'::varchar,
        '行政卷宗业务大类'::text,
        2::bigint,
        20::int
),
upsert_roots AS (
    INSERT INTO leaudit_evaluation_point_groups (
        pid,
        code,
        name,
        description,
        document_type_id,
        entry_module_id,
        sort_order,
        is_enabled,
        created_at,
        updated_at
    )
    SELECT
        0,
        rc.code,
        rc.name,
        rc.description,
        NULL,
        rc.entry_module_id,
        rc.sort_order,
        TRUE,
        NOW(),
        NOW()
    FROM root_candidates rc
    WHERE NOT EXISTS (
        SELECT 1
        FROM leaudit_evaluation_point_groups g
        WHERE g.deleted_at IS NULL
          AND COALESCE(g.pid, 0) = 0
          AND LOWER(g.code) = LOWER(rc.code)
    )
    RETURNING id, code
)
SELECT COUNT(*) AS inserted_root_count FROM upsert_roots;

UPDATE leaudit_evaluation_point_groups
SET
    name = '合同',
    description = '合同业务大类',
    entry_module_id = 1,
    sort_order = 10,
    updated_at = NOW()
WHERE deleted_at IS NULL
  AND COALESCE(pid, 0) = 0
  AND LOWER(code) = LOWER('root.contract');

UPDATE leaudit_evaluation_point_groups
SET
    name = '行政卷宗',
    description = '行政卷宗业务大类',
    entry_module_id = 2,
    sort_order = 20,
    updated_at = NOW()
WHERE deleted_at IS NULL
  AND COALESCE(pid, 0) = 0
  AND LOWER(code) = LOWER('root.casefile');

-- =========================================================
-- 2. 优先把旧一级根直接下沉为目标二级分组
--    这样可复用现有 code，避免唯一索引冲突
-- =========================================================
WITH target_root_map AS (
    SELECT
        dt.id AS document_type_id,
        CASE
            WHEN dt.entry_module_id = 1 THEN (
                SELECT id FROM leaudit_evaluation_point_groups
                WHERE deleted_at IS NULL AND COALESCE(pid, 0) = 0 AND code = 'root.contract'
                LIMIT 1
            )
            WHEN dt.entry_module_id = 2 THEN (
                SELECT id FROM leaudit_evaluation_point_groups
                WHERE deleted_at IS NULL AND COALESCE(pid, 0) = 0 AND code = 'root.casefile'
                LIMIT 1
            )
            ELSE NULL
        END AS new_root_id
    FROM leaudit_document_types dt
    WHERE dt.deleted_at IS NULL
),
reparented_old_roots AS (
    UPDATE leaudit_evaluation_point_groups g
    SET
        pid = trm.new_root_id,
        entry_module_id = NULL,
        updated_at = NOW()
    FROM target_root_map trm
    WHERE g.deleted_at IS NULL
      AND COALESCE(g.pid, 0) = 0
      AND g.document_type_id = trm.document_type_id
      AND trm.new_root_id IS NOT NULL
    RETURNING g.id, g.document_type_id
)
SELECT COUNT(*) AS reparented_old_root_count FROM reparented_old_roots;

-- =========================================================
-- 3. 如果某个文档类型没有旧一级根，再补建新的目标二级分组
-- =========================================================
WITH mapped_doc_types AS (
    SELECT
        dt.id AS document_type_id,
        dt.code AS document_type_code,
        dt.name AS document_type_name,
        dt.description,
        dt.entry_module_id,
        dt.sort_order,
        dt.is_enabled,
        CASE
            WHEN dt.entry_module_id = 1 THEN (
                SELECT id FROM leaudit_evaluation_point_groups
                WHERE deleted_at IS NULL AND COALESCE(pid, 0) = 0 AND code = 'root.contract'
                LIMIT 1
            )
            WHEN dt.entry_module_id = 2 THEN (
                SELECT id FROM leaudit_evaluation_point_groups
                WHERE deleted_at IS NULL AND COALESCE(pid, 0) = 0 AND code = 'root.casefile'
                LIMIT 1
            )
            ELSE NULL
        END AS root_group_id
    FROM leaudit_document_types dt
    WHERE dt.deleted_at IS NULL
),
insert_children AS (
    INSERT INTO leaudit_evaluation_point_groups (
        pid,
        code,
        name,
        description,
        document_type_id,
        entry_module_id,
        sort_order,
        is_enabled,
        created_at,
        updated_at
    )
    SELECT
        m.root_group_id,
        m.document_type_code,
        m.document_type_name,
        COALESCE(m.description, m.document_type_name || '二级分组'),
        m.document_type_id,
        NULL,
        COALESCE(m.sort_order, 0),
        COALESCE(m.is_enabled, TRUE),
        NOW(),
        NOW()
    FROM mapped_doc_types m
    WHERE m.root_group_id IS NOT NULL
      AND NOT EXISTS (
          SELECT 1
          FROM leaudit_evaluation_point_groups g
          JOIN leaudit_evaluation_point_groups root
            ON root.id = g.pid
           AND root.deleted_at IS NULL
          WHERE g.deleted_at IS NULL
            AND COALESCE(g.pid, 0) <> 0
            AND g.document_type_id = m.document_type_id
            AND root.document_type_id IS NULL
      )
      AND NOT EXISTS (
          SELECT 1
          FROM leaudit_evaluation_point_groups g
          WHERE g.deleted_at IS NULL
            AND LOWER(g.code) = LOWER(m.document_type_code)
      )
    RETURNING id, document_type_id
)
SELECT COUNT(*) AS inserted_child_count FROM insert_children;

-- =========================================================
-- 4. 将旧一级根上的规则绑定迁到目标二级分组
-- =========================================================
WITH old_roots AS (
    SELECT id AS old_group_id, document_type_id
    FROM leaudit_evaluation_point_groups
    WHERE deleted_at IS NULL
      AND COALESCE(pid, 0) = 0
      AND document_type_id IS NOT NULL
),
target_children AS (
    SELECT child.id AS new_group_id, child.document_type_id
    FROM leaudit_evaluation_point_groups child
    JOIN leaudit_evaluation_point_groups root
      ON root.id = child.pid
     AND root.deleted_at IS NULL
     AND root.document_type_id IS NULL
    WHERE child.deleted_at IS NULL
      AND COALESCE(child.pid, 0) <> 0
      AND child.document_type_id IS NOT NULL
),
move_pairs AS (
    SELECT old_roots.old_group_id, target_children.new_group_id
    FROM old_roots
    JOIN target_children
      ON target_children.document_type_id = old_roots.document_type_id
),
updated_bindings AS (
    UPDATE leaudit_rule_group_bindings rgb
    SET group_id = mp.new_group_id,
        updated_at = NOW()
    FROM move_pairs mp
    WHERE rgb.group_id = mp.old_group_id
      AND rgb.deleted_at IS NULL
    RETURNING rgb.id
)
SELECT COUNT(*) AS moved_old_root_binding_count FROM updated_bindings;

-- =========================================================
-- 5. 将旧默认子级上的规则绑定迁到目标二级分组
--    注意：只迁“父级仍是旧一级根”的默认子级
-- =========================================================
WITH old_default_children AS (
    SELECT
        child.id AS old_group_id,
        child.document_type_id
    FROM leaudit_evaluation_point_groups child
    JOIN leaudit_evaluation_point_groups parent
      ON parent.id = child.pid
     AND parent.deleted_at IS NULL
    WHERE child.deleted_at IS NULL
      AND COALESCE(child.pid, 0) <> 0
      AND parent.document_type_id IS NOT NULL
      AND (
          child.name = '通用'
          OR child.code LIKE '%.default'
      )
),
target_children AS (
    SELECT child.id AS new_group_id, child.document_type_id
    FROM leaudit_evaluation_point_groups child
    JOIN leaudit_evaluation_point_groups root
      ON root.id = child.pid
     AND root.deleted_at IS NULL
     AND root.document_type_id IS NULL
    WHERE child.deleted_at IS NULL
      AND COALESCE(child.pid, 0) <> 0
      AND child.document_type_id IS NOT NULL
),
move_pairs AS (
    SELECT old_default_children.old_group_id, target_children.new_group_id
    FROM old_default_children
    JOIN target_children
      ON target_children.document_type_id = old_default_children.document_type_id
),
updated_bindings AS (
    UPDATE leaudit_rule_group_bindings rgb
    SET group_id = mp.new_group_id,
        updated_at = NOW()
    FROM move_pairs mp
    WHERE rgb.group_id = mp.old_group_id
      AND rgb.deleted_at IS NULL
      AND NOT EXISTS (
          SELECT 1
          FROM leaudit_rule_group_bindings existing
          WHERE existing.deleted_at IS NULL
            AND existing.group_id = mp.new_group_id
            AND existing.rule_set_id = rgb.rule_set_id
      )
    RETURNING rgb.id
),
soft_deleted_duplicates AS (
    UPDATE leaudit_rule_group_bindings rgb
    SET deleted_at = NOW(),
        updated_at = NOW(),
        note = COALESCE(rgb.note, '') || ' [soft-deleted after business-root migration: duplicate with target child binding]'
    FROM move_pairs mp
    WHERE rgb.group_id = mp.old_group_id
      AND rgb.deleted_at IS NULL
      AND EXISTS (
          SELECT 1
          FROM leaudit_rule_group_bindings existing
          WHERE existing.deleted_at IS NULL
            AND existing.group_id = mp.new_group_id
            AND existing.rule_set_id = rgb.rule_set_id
      )
    RETURNING rgb.id
)
SELECT
    (SELECT COUNT(*) FROM updated_bindings) AS moved_default_child_binding_count,
    (SELECT COUNT(*) FROM soft_deleted_duplicates) AS soft_deleted_duplicate_default_binding_count;

-- =========================================================
-- 6. 去重修复：避免迁移后同一二级分组重复绑定同一规则集
--    保留 priority 更高、id 更小的一条
-- =========================================================
WITH ranked AS (
    SELECT
        id,
        ROW_NUMBER() OVER (
            PARTITION BY group_id, rule_set_id
            ORDER BY priority DESC, id ASC
        ) AS rn
    FROM leaudit_rule_group_bindings
    WHERE deleted_at IS NULL
),
soft_deleted AS (
    UPDATE leaudit_rule_group_bindings rgb
    SET deleted_at = NOW(),
        updated_at = NOW()
    FROM ranked r
    WHERE rgb.id = r.id
      AND r.rn > 1
    RETURNING rgb.id
)
SELECT COUNT(*) AS deduped_binding_count FROM soft_deleted;

-- =========================================================
-- 7. 清理迁移后已空置的旧默认子级
--    条件：
--      - 自身无有效规则绑定
--      - 自身是“通用 / *.default”
--      - 父级已是具体业务类型节点
-- =========================================================
WITH empty_default_children AS (
    SELECT child.id
    FROM leaudit_evaluation_point_groups child
    JOIN leaudit_evaluation_point_groups parent
      ON parent.id = child.pid
     AND parent.deleted_at IS NULL
    LEFT JOIN leaudit_rule_group_bindings rgb
      ON rgb.group_id = child.id
     AND rgb.deleted_at IS NULL
    WHERE child.deleted_at IS NULL
      AND COALESCE(child.pid, 0) <> 0
      AND parent.document_type_id IS NOT NULL
      AND (
          child.name = '通用'
          OR child.code LIKE '%.default'
      )
    GROUP BY child.id
    HAVING COUNT(rgb.id) = 0
),
soft_deleted_groups AS (
    UPDATE leaudit_evaluation_point_groups g
    SET deleted_at = NOW(),
        updated_at = NOW()
    FROM empty_default_children edc
    WHERE g.id = edc.id
    RETURNING g.id
)
SELECT COUNT(*) AS soft_deleted_empty_default_group_count FROM soft_deleted_groups;

-- =========================================================
-- 8. 重建文档类型汇总绑定表
-- =========================================================
UPDATE leaudit_rule_type_bindings
SET deleted_at = NOW(),
    updated_at = NOW()
WHERE deleted_at IS NULL;

INSERT INTO leaudit_rule_type_bindings (
    doc_type_id,
    doc_type_code,
    rule_set_id,
    binding_mode,
    priority,
    is_active,
    note,
    created_at,
    updated_at,
    region
)
SELECT DISTINCT ON (child.document_type_id, rgb.rule_set_id)
    child.document_type_id,
    dt.code,
    rgb.rule_set_id,
    'explicit',
    100,
    TRUE,
    're-built from second-level group bindings after business-root migration',
    NOW(),
    NOW(),
    'default'
FROM leaudit_rule_group_bindings rgb
JOIN leaudit_evaluation_point_groups child
  ON child.id = rgb.group_id
 AND child.deleted_at IS NULL
 AND COALESCE(child.pid, 0) <> 0
JOIN leaudit_evaluation_point_groups root
  ON root.id = child.pid
 AND root.deleted_at IS NULL
 AND root.document_type_id IS NULL
JOIN leaudit_document_types dt
  ON dt.id = child.document_type_id
WHERE rgb.deleted_at IS NULL
  AND rgb.is_active = TRUE
ORDER BY child.document_type_id, rgb.rule_set_id, rgb.priority DESC, rgb.id ASC;

-- =========================================================
-- 9. 迁移后输出检查摘要
-- =========================================================
SELECT
    root.code AS root_code,
    root.name AS root_name,
    root.entry_module_id,
    COUNT(child.id) AS child_count
FROM leaudit_evaluation_point_groups root
LEFT JOIN leaudit_evaluation_point_groups child
  ON child.pid = root.id
 AND child.deleted_at IS NULL
WHERE root.deleted_at IS NULL
  AND COALESCE(root.pid, 0) = 0
  AND root.document_type_id IS NULL
GROUP BY root.id, root.code, root.name, root.entry_module_id
ORDER BY root.sort_order, root.id;

COMMIT;
