BEGIN;

-- 锁住卡表，保证冲突扫描与索引切换之间不会插入新的重复 ICCID。
LOCK TABLE tb_iot_card IN ACCESS EXCLUSIVE MODE;

DO $$
DECLARE
    iccid_19_conflict_groups bigint;
    iccid_20_conflict_groups bigint;
    invalid_dual_column_rows bigint;
BEGIN
    SELECT COUNT(*)
      INTO invalid_dual_column_rows
      FROM tb_iot_card
     WHERE deleted_at IS NULL
       AND (
           LENGTH(iccid) NOT IN (19, 20)
           OR iccid_19 IS NULL
           OR LENGTH(iccid_19) <> 19
           OR (LENGTH(iccid) = 19 AND (iccid_19 IS DISTINCT FROM iccid OR iccid_20 IS NOT NULL))
           OR (LENGTH(iccid) = 20 AND (iccid_19 IS DISTINCT FROM LEFT(iccid, 19) OR iccid_20 IS DISTINCT FROM iccid))
       );

    SELECT COUNT(*)
      INTO iccid_19_conflict_groups
      FROM (
          SELECT iccid_19
           FROM tb_iot_card
           WHERE deleted_at IS NULL
             AND iccid_19 IS NOT NULL
           GROUP BY iccid_19
          HAVING COUNT(*) > 1
      ) conflicts;

    SELECT COUNT(*)
      INTO iccid_20_conflict_groups
      FROM (
          SELECT iccid_20
            FROM tb_iot_card
           WHERE deleted_at IS NULL
             AND iccid_20 IS NOT NULL
           GROUP BY iccid_20
          HAVING COUNT(*) > 1
      ) conflicts;

    IF invalid_dual_column_rows > 0 OR iccid_19_conflict_groups > 0 OR iccid_20_conflict_groups > 0 THEN
        RAISE EXCEPTION 'ICCID 唯一索引切换中止：双列异常卡数=%，19位冲突组数=%，20位冲突组数=%；请导出异常清单并人工处理后重试',
            invalid_dual_column_rows, iccid_19_conflict_groups, iccid_20_conflict_groups;
    END IF;
END
$$;

-- 事务内先删除普通索引再以原名创建唯一索引；任何失败都会整体回滚。
DROP INDEX IF EXISTS idx_iot_card_iccid_19;
DROP INDEX IF EXISTS idx_iot_card_iccid_20;

CREATE UNIQUE INDEX idx_iot_card_iccid_19
    ON tb_iot_card (iccid_19)
    WHERE deleted_at IS NULL;

CREATE UNIQUE INDEX idx_iot_card_iccid_20
    ON tb_iot_card (iccid_20)
    WHERE deleted_at IS NULL AND iccid_20 IS NOT NULL;

COMMENT ON INDEX idx_iot_card_iccid_19 IS '未删除卡的非空19位ICCID精确唯一索引';
COMMENT ON INDEX idx_iot_card_iccid_20 IS '未删除卡的非空20位ICCID精确唯一索引';

COMMIT;
