<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cloudhandson.vpdbackoffice.mapper.MaskingRuleMapper">
  <select id="findAllRules" resultType="com.cloudhandson.vpdbackoffice.domain.masking.MaskingRule">
    SELECT rule_id, rule_code, rule_name, template_code, description, enabled_yn
    FROM cb_masking_rule
    ORDER BY enabled_yn DESC, rule_name, rule_id
  </select>

  <select id="findEnabledRules" resultType="com.cloudhandson.vpdbackoffice.domain.masking.MaskingRule">
    SELECT rule_id, rule_code, rule_name, template_code, description, enabled_yn
    FROM cb_masking_rule
    WHERE enabled_yn = 'Y'
    ORDER BY rule_name, rule_id
  </select>

  <select id="findRuleById" resultType="com.cloudhandson.vpdbackoffice.domain.masking.MaskingRule">
    SELECT rule_id, rule_code, rule_name, template_code, description, enabled_yn
    FROM cb_masking_rule
    WHERE rule_id = #{ruleId}
  </select>

  <select id="findRuleByCode" resultType="com.cloudhandson.vpdbackoffice.domain.masking.MaskingRule">
    SELECT rule_id, rule_code, rule_name, template_code, description, enabled_yn
    FROM cb_masking_rule
    WHERE rule_code = #{ruleCode}
  </select>

  <select id="nextRuleId" resultType="long">
    SELECT NVL(MAX(rule_id), 0) + 1 FROM cb_masking_rule
  </select>

  <insert id="insertRule">
    INSERT INTO cb_masking_rule (rule_id, rule_code, rule_name, template_code, description, enabled_yn)
    VALUES (#{ruleId}, #{command.ruleCode}, #{command.ruleName}, #{command.templateCode},
            #{command.description}, 'Y')
  </insert>

  <update id="updateRuleActive">
    UPDATE cb_masking_rule
    SET enabled_yn = #{enabledYn}
    WHERE rule_id = #{ruleId}
  </update>

  <select id="findColumnRules" resultType="com.cloudhandson.vpdbackoffice.domain.masking.ColumnMaskingRule">
    SELECT c.column_id,
           c.object_id,
           o.owner,
           o.object_name,
           c.column_name,
           r.rule_id,
           r.rule_code,
           r.rule_name,
           r.template_code,
           r.enabled_yn AS rule_enabled_yn
    FROM cb_column_masking_rule link
    JOIN cb_protected_column c ON c.column_id = link.column_id
    JOIN cb_protected_object o ON o.object_id = c.object_id
    JOIN cb_masking_rule r ON r.rule_id = link.rule_id
    ORDER BY o.owner, o.object_name, c.column_name
  </select>

  <!--
    Runtime truth comes from Oracle's REDACTION_POLICIES / REDACTION_COLUMNS,
    while CB_* tables represent the backoffice's desired configuration.
    Keep this comparison limited to the policies managed by this screen.
  -->
  <select id="findPolicyStatuses" resultType="com.cloudhandson.vpdbackoffice.domain.masking.MaskingPolicyStatus">
    WITH managed_policy AS (
      <foreach collection="targets" item="target" separator=" UNION ALL ">
        SELECT #{target.objectName} AS object_name, #{target.policyName} AS policy_name FROM dual
      </foreach>
    ),
    configured AS (
      SELECT protected_object.object_name,
             COUNT(*) AS configured_column_count
      FROM sg_column_masking_rule link
      JOIN sg_masking_rule rule ON rule.rule_id = link.rule_id
      JOIN sg_protected_column protected_column ON protected_column.column_id = link.column_id
      JOIN sg_protected_object protected_object ON protected_object.object_id = protected_column.object_id
      WHERE protected_object.owner = #{owner}
        AND rule.enabled_yn = 'Y'
      GROUP BY protected_object.object_name
    ),
    database_policy AS (
      SELECT policy.object_name,
             policy.policy_name,
             policy.enable,
             COUNT(policy_column.column_name) AS applied_column_count
      FROM redaction_policies policy
      JOIN managed_policy managed
        ON managed.object_name = policy.object_name
       AND managed.policy_name = policy.policy_name
      LEFT JOIN redaction_columns policy_column
        ON policy_column.object_owner = policy.object_owner
       AND policy_column.object_name = policy.object_name
      WHERE policy.object_owner = #{owner}
      GROUP BY policy.object_name, policy.policy_name, policy.enable
    ),
    missing_columns AS (
      SELECT protected_object.object_name,
             COUNT(*) AS missing_column_count
      FROM cb_column_masking_rule link
      JOIN cb_masking_rule rule ON rule.rule_id = link.rule_id
      JOIN cb_protected_column protected_column ON protected_column.column_id = link.column_id
      JOIN cb_protected_object protected_object ON protected_object.object_id = protected_column.object_id
      LEFT JOIN redaction_columns policy_column
        ON policy_column.object_owner = protected_object.owner
       AND policy_column.object_name = protected_object.object_name
       AND policy_column.column_name = protected_column.column_name
      WHERE protected_object.owner = #{owner}
        AND rule.enabled_yn = 'Y'
        AND policy_column.column_name IS NULL
      GROUP BY protected_object.object_name
    ),
    extra_columns AS (
      SELECT policy_column.object_name,
             COUNT(*) AS extra_column_count
      FROM redaction_columns policy_column
      JOIN managed_policy managed ON managed.object_name = policy_column.object_name
      WHERE policy_column.object_owner = #{owner}
        AND NOT EXISTS (
          SELECT 1
          FROM sg_column_masking_rule link
          JOIN sg_masking_rule rule ON rule.rule_id = link.rule_id
          JOIN sg_protected_column protected_column ON protected_column.column_id = link.column_id
          JOIN sg_protected_object protected_object ON protected_object.object_id = protected_column.object_id
          WHERE protected_object.owner = #{owner}
            AND protected_object.object_name = policy_column.object_name
            AND protected_column.column_name = policy_column.column_name
            AND rule.enabled_yn = 'Y'
        )
      GROUP BY policy_column.object_name
    ),
    legacy_vpd_column_policy AS (
      SELECT policy.object_name,
             COUNT(*) AS legacy_vpd_column_policy_count
      FROM all_policies policy
      JOIN managed_policy managed
        ON managed.object_name = policy.object_name
       AND managed.policy_name = policy.policy_name
      WHERE policy.object_owner = #{owner}
        AND policy.enable = 'YES'
      GROUP BY policy.object_name
    )
    SELECT #{owner} AS owner,
           managed.object_name,
           managed.policy_name,
           database_policy.enable AS enabled,
           NVL(configured.configured_column_count, 0) AS configured_column_count,
           NVL(database_policy.applied_column_count, 0) AS applied_column_count,
           NVL(missing_columns.missing_column_count, 0) + NVL(extra_columns.extra_column_count, 0)
             AS mismatched_column_count,
           0 AS legacy_vpd_column_policy_count
    FROM managed_policy managed
    LEFT JOIN configured ON configured.object_name = managed.object_name
    LEFT JOIN database_policy ON database_policy.object_name = managed.object_name
    LEFT JOIN missing_columns ON missing_columns.object_name = managed.object_name
    LEFT JOIN extra_columns ON extra_columns.object_name = managed.object_name
    ORDER BY managed.object_name
  </select>

  <select id="findColumnRule" resultType="com.cloudhandson.vpdbackoffice.domain.masking.ColumnMaskingRule">
    SELECT c.column_id,
           c.object_id,
           o.owner,
           o.object_name,
           c.column_name,
           r.rule_id,
           r.rule_code,
           r.rule_name,
           r.template_code,
           r.enabled_yn AS rule_enabled_yn
    FROM cb_column_masking_rule link
    JOIN cb_protected_column c ON c.column_id = link.column_id
    JOIN cb_protected_object o ON o.object_id = c.object_id
    JOIN cb_masking_rule r ON r.rule_id = link.rule_id
    WHERE c.column_id = #{columnId}
  </select>

  <insert id="upsertColumnRule">
    MERGE INTO cb_column_masking_rule dst
    USING (SELECT #{columnId} column_id, #{ruleId} rule_id FROM dual) src
    ON (dst.column_id = src.column_id)
    WHEN MATCHED THEN UPDATE SET dst.rule_id = src.rule_id, dst.updated_at = SYSTIMESTAMP
    WHEN NOT MATCHED THEN INSERT (column_id, rule_id, updated_at)
      VALUES (src.column_id, src.rule_id, SYSTIMESTAMP)
  </insert>

  <delete id="deleteColumnRule">
    DELETE FROM cb_column_masking_rule
    WHERE column_id = #{columnId}
  </delete>

  <delete id="deleteUserRulesForColumn">
    DELETE FROM cb_user_masking_rule
    WHERE column_id = #{columnId}
  </delete>

  <select id="findUserRules" resultType="com.cloudhandson.vpdbackoffice.domain.masking.UserMaskingRule">
    SELECT u.user_id,
           u.user_name AS username,
           c.column_id,
           o.owner,
           o.object_name,
           c.column_name,
           r.rule_name,
           r.template_code,
           assignment.decision,
           assignment.active_yn
    FROM cb_user_masking_rule assignment
    JOIN cb_app_user u ON u.user_id = assignment.user_id
    JOIN cb_protected_column c ON c.column_id = assignment.column_id
    JOIN cb_protected_object o ON o.object_id = c.object_id
    JOIN cb_column_masking_rule link ON link.column_id = c.column_id
    JOIN cb_masking_rule r ON r.rule_id = link.rule_id
    ORDER BY u.user_name, o.owner, o.object_name, c.column_name
  </select>

  <select id="findUserRule" resultType="com.cloudhandson.vpdbackoffice.domain.masking.UserMaskingRule">
    SELECT u.user_id,
           u.user_name AS username,
           c.column_id,
           o.owner,
           o.object_name,
           c.column_name,
           r.rule_name,
           r.template_code,
           assignment.decision,
           assignment.active_yn
    FROM cb_user_masking_rule assignment
    JOIN cb_app_user u ON u.user_id = assignment.user_id
    JOIN cb_protected_column c ON c.column_id = assignment.column_id
    JOIN cb_protected_object o ON o.object_id = c.object_id
    JOIN cb_column_masking_rule link ON link.column_id = c.column_id
    JOIN cb_masking_rule r ON r.rule_id = link.rule_id
    WHERE assignment.user_id = #{userId}
      AND assignment.column_id = #{columnId}
  </select>

  <insert id="upsertUserRule">
    MERGE INTO cb_user_masking_rule dst
    USING (SELECT #{userId} user_id, #{columnId} column_id, #{decision} decision FROM dual) src
    ON (dst.user_id = src.user_id AND dst.column_id = src.column_id)
    WHEN MATCHED THEN UPDATE SET dst.decision = src.decision, dst.active_yn = 'Y', dst.updated_at = SYSTIMESTAMP
    WHEN NOT MATCHED THEN INSERT (user_id, column_id, decision, active_yn, updated_at)
      VALUES (src.user_id, src.column_id, src.decision, 'Y', SYSTIMESTAMP)
  </insert>

  <delete id="deleteUserRule">
    DELETE FROM cb_user_masking_rule
    WHERE user_id = #{userId}
      AND column_id = #{columnId}
  </delete>
</mapper>
