-- ============================================================
-- 84_hmm_carrier_team_vpd.sql
--
-- Row security for HMM_CARRIER_ASSIGNMENTS_V:
--   HMM_HR_VIEWER  -> current employee only
--   HMM_HR_MANAGER -> current employee and direct reports
--   HMM_HR_ADMIN   -> all assignments
--
-- Run as ADMIN after 72_hmm_leave_team_vpd.sql and
-- 81_hmm_carrier_access_groups.sql. Plain bearer tokens are
-- never stored by this script.
-- ============================================================
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO OFF
SET FEEDBACK ON
SET DEFINE OFF

PROMPT === 1. Registering the protected carrier object ===
DECLARE
  v_object_id NUMBER;
BEGIN
  BEGIN
    SELECT object_id
    INTO v_object_id
    FROM hmm_access_objects
    WHERE owner = 'ADMIN'
      AND object_name = 'HMM_CARRIER_ASSIGNMENTS_V';

    UPDATE hmm_access_objects
    SET enabled_yn = 'Y',
        description = 'Employee-to-carrier assignments protected by employee hierarchy',
        ords_path = 'cb-ords/cb-object-query/admin/hmm_carrier_assignments_v'
    WHERE object_id = v_object_id;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      SELECT NVL(MAX(object_id), 0) + 1
      INTO v_object_id
      FROM hmm_access_objects;

      INSERT INTO hmm_access_objects (
        object_id, owner, object_name, ords_path, enabled_yn, description
      ) VALUES (
        v_object_id,
        'ADMIN',
        'HMM_CARRIER_ASSIGNMENTS_V',
        'cb-ords/cb-object-query/admin/hmm_carrier_assignments_v',
        'Y',
        'Employee-to-carrier assignments protected by employee hierarchy'
      );
  END;

  FOR c IN (
    SELECT column_name
    FROM user_tab_columns
    WHERE table_name = 'HMM_CARRIER_ASSIGNMENTS_V'
    ORDER BY column_id
  ) LOOP
    MERGE INTO hmm_access_object_columns dst
    USING (
      SELECT v_object_id object_id, c.column_name column_name FROM dual
    ) src
    ON (dst.object_id = src.object_id AND dst.column_name = src.column_name)
    WHEN NOT MATCHED THEN INSERT (
      column_id, object_id, column_name, sensitive_yn,
      sensitivity_level, redaction_method
    ) VALUES (
      (SELECT NVL(MAX(column_id), 0) + 1 FROM hmm_access_object_columns),
      src.object_id, src.column_name, 'N', 'PUBLIC', 'NONE'
    );
  END LOOP;
END;
/

PROMPT === 2. Registering role rules ===
DECLARE
  PROCEDURE ensure_permission(
    p_role_name IN VARCHAR2,
    p_rule_type IN VARCHAR2
  ) AS
    v_role_id NUMBER;
    v_perm_id NUMBER;
  BEGIN
    SELECT role_id
    INTO v_role_id
    FROM hmm_access_roles
    WHERE role_name = p_role_name
      AND active_yn = 'Y';

    BEGIN
      SELECT perm_id
      INTO v_perm_id
      FROM hmm_access_permissions
      WHERE role_id = v_role_id
        AND target_name = 'HMM_CARRIER_ASSIGNMENTS_V'
        AND action_name = 'SELECT';

      UPDATE hmm_access_permissions
      SET permission_effect = 'ALLOW'
      WHERE perm_id = v_perm_id;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        SELECT cb_permission_seq.NEXTVAL INTO v_perm_id FROM dual;
        INSERT INTO hmm_access_permissions (
          perm_id, role_id, target_name, action_name, permission_effect
        ) VALUES (
          v_perm_id, v_role_id, 'HMM_CARRIER_ASSIGNMENTS_V', 'SELECT', 'ALLOW'
        );
    END;

    DELETE FROM hmm_access_permission_rules WHERE perm_id = v_perm_id;
    INSERT INTO hmm_access_permission_rules (
      rule_id, perm_id, rule_column, rule_type, rule_value
    ) VALUES (
      cb_permission_rule_seq.NEXTVAL,
      v_perm_id,
      'EMPLOYEE_ID',
      p_rule_type,
      NULL
    );
  END;
BEGIN
  ensure_permission('HMM_HR_VIEWER', 'SELF');
  ensure_permission('HMM_HR_MANAGER', 'MANAGED_TEAM');
  ensure_permission('HMM_HR_ADMIN', 'ALL');
END;
/

COMMIT;

PROMPT === 3. Creating the carrier VPD predicate ===
CREATE OR REPLACE FUNCTION hmm_carrier_vpd_filter(
  p_schema IN VARCHAR2,
  p_object IN VARCHAR2
) RETURN VARCHAR2
AUTHID DEFINER
AS
  v_employee_id NUMBER;
  v_all_rule     NUMBER := 0;
  v_team_rule    NUMBER := 0;
  v_self_rule    NUMBER := 0;
BEGIN
  IF UPPER(TRIM(p_schema)) != 'ADMIN'
     OR UPPER(TRIM(p_object)) != 'HMM_CARRIER_ASSIGNMENTS_V' THEN
    RETURN '1 = 0';
  END IF;

  BEGIN
    v_employee_id := TO_NUMBER(
      SYS_CONTEXT('HMM_ACCESS_CTX', 'EMPLOYEE_ID')
    );
  EXCEPTION
    WHEN OTHERS THEN
      RETURN '1 = 0';
  END;

  IF v_employee_id IS NULL THEN
    RETURN '1 = 0';
  END IF;

  SELECT NVL(MAX(CASE WHEN rule.rule_type = 'ALL' THEN 1 ELSE 0 END), 0),
         NVL(MAX(CASE WHEN rule.rule_type = 'MANAGED_TEAM' THEN 1 ELSE 0 END), 0),
         NVL(MAX(CASE WHEN rule.rule_type = 'SELF' THEN 1 ELSE 0 END), 0)
  INTO v_all_rule, v_team_rule, v_self_rule
  FROM (
    SELECT employee_role.role_id
    FROM hmm_employee_access_roles employee_role
    WHERE employee_role.employee_id = v_employee_id
    UNION
    SELECT group_role.role_id
    FROM hmm_access_group_members group_member
    JOIN hmm_access_groups access_group
      ON access_group.group_id = group_member.group_id
     AND access_group.active_yn = 'Y'
    JOIN hmm_group_access_roles group_role
      ON group_role.group_id = group_member.group_id
    WHERE group_member.employee_id = v_employee_id
  ) effective_role
  JOIN hmm_access_roles role
    ON role.role_id = effective_role.role_id
   AND role.active_yn = 'Y'
  JOIN hmm_access_permissions permission
    ON permission.role_id = role.role_id
   AND permission.target_name = 'HMM_CARRIER_ASSIGNMENTS_V'
   AND permission.action_name = 'SELECT'
   AND permission.permission_effect = 'ALLOW'
  JOIN hmm_access_permission_rules rule
    ON rule.perm_id = permission.perm_id
   AND UPPER(TRIM(rule.rule_column)) = 'EMPLOYEE_ID';

  IF v_all_rule = 1 THEN
    RETURN '1 = 1';
  END IF;

  IF v_team_rule = 1 THEN
    RETURN 'EMPLOYEE_ID IN ('
      || 'SELECT employee.employee_id '
      || 'FROM ADMIN.HMM_HR_EMPLOYEES employee '
      || 'WHERE employee.employee_id = '
      || 'TO_NUMBER(SYS_CONTEXT(''HMM_ACCESS_CTX'', ''EMPLOYEE_ID'')) '
      || 'OR employee.manager_employee_id = '
      || 'TO_NUMBER(SYS_CONTEXT(''HMM_ACCESS_CTX'', ''EMPLOYEE_ID''))'
      || ')';
  END IF;

  IF v_self_rule = 1 THEN
    RETURN 'EMPLOYEE_ID = TO_NUMBER('
      || 'SYS_CONTEXT(''HMM_ACCESS_CTX'', ''EMPLOYEE_ID''))';
  END IF;

  RETURN '1 = 0';
EXCEPTION
  WHEN OTHERS THEN
    RETURN '1 = 0';
END;
/

SHOW ERRORS FUNCTION hmm_carrier_vpd_filter

PROMPT === 4. Attaching the VPD policy ===
BEGIN
  BEGIN
    DBMS_RLS.DROP_POLICY(
      object_schema => 'ADMIN',
      object_name => 'HMM_CARRIER_ASSIGNMENTS_V',
      policy_name => 'HMM_CARRIER_SCOPE_POLICY'
    );
  EXCEPTION
    WHEN OTHERS THEN
      IF SQLCODE != -28102 THEN
        RAISE;
      END IF;
  END;

  DBMS_RLS.ADD_POLICY(
    object_schema => 'ADMIN',
    object_name => 'HMM_CARRIER_ASSIGNMENTS_V',
    policy_name => 'HMM_CARRIER_SCOPE_POLICY',
    function_schema => 'ADMIN',
    policy_function => 'HMM_CARRIER_VPD_FILTER',
    statement_types => 'SELECT',
    update_check => FALSE,
    enable => TRUE,
    policy_type => DBMS_RLS.CONTEXT_SENSITIVE
  );
END;
/

PROMPT === 5. Granting the non-exempt runtime read boundary ===
GRANT SELECT ON hmm_carrier_assignments_v TO cb_ords;
GRANT SELECT ON hmm_rds_carriers_v TO cb_ords;
GRANT SELECT ON hmm_rds_carrier_perf_v TO cb_ords;
GRANT SELECT ON hmm_rds_carrier_latest_v TO cb_ords;

CREATE OR REPLACE SYNONYM cb_ords.hmm_carrier_assignments_v
  FOR admin.hmm_carrier_assignments_v;
CREATE OR REPLACE SYNONYM cb_ords.hmm_rds_carriers_v
  FOR admin.hmm_rds_carriers_v;
CREATE OR REPLACE SYNONYM cb_ords.hmm_rds_carrier_perf_v
  FOR admin.hmm_rds_carrier_perf_v;
CREATE OR REPLACE SYNONYM cb_ords.hmm_rds_carrier_latest_v
  FOR admin.hmm_rds_carrier_latest_v;

PROMPT === 6. Recording policy notes ===
MERGE INTO hmm_access_vpd_filter_notes dst
USING (
  SELECT 'ADMIN' function_owner,
         'HMM_CARRIER_VPD_FILTER' function_name,
         'Filters carrier assignments by the authenticated employee role and employee hierarchy.' description
  FROM dual
) src
ON (dst.function_owner = src.function_owner AND dst.function_name = src.function_name)
WHEN MATCHED THEN UPDATE SET
  dst.description = src.description,
  dst.updated_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN INSERT (
  function_owner, function_name, description, updated_at
) VALUES (
  src.function_owner, src.function_name, src.description, SYSTIMESTAMP
);

MERGE INTO hmm_access_vpd_policy_notes dst
USING (
  SELECT 'ADMIN' object_owner,
         'HMM_CARRIER_ASSIGNMENTS_V' object_name,
         'HMM_CARRIER_SCOPE_POLICY' policy_name,
         'Restricts carrier assignments to self, direct reports, or explicit HR administrator access.' description
  FROM dual
) src
ON (
  dst.object_owner = src.object_owner
  AND dst.object_name = src.object_name
  AND dst.policy_name = src.policy_name
)
WHEN MATCHED THEN UPDATE SET
  dst.description = src.description,
  dst.updated_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN INSERT (
  object_owner, object_name, policy_name, description, updated_at
) VALUES (
  src.object_owner, src.object_name, src.policy_name,
  src.description, SYSTIMESTAMP
);

COMMIT;

PROMPT === 7. Verification inventory ===
SELECT object_name, policy_name, function, sel, enable, policy_type
FROM all_policies
WHERE object_owner = 'ADMIN'
  AND object_name = 'HMM_CARRIER_ASSIGNMENTS_V'
  AND policy_name = 'HMM_CARRIER_SCOPE_POLICY';

SELECT role.role_name,
       permission.target_name,
       rule.rule_column,
       rule.rule_type
FROM hmm_access_permissions permission
JOIN hmm_access_roles role ON role.role_id = permission.role_id
JOIN hmm_access_permission_rules rule ON rule.perm_id = permission.perm_id
WHERE permission.target_name = 'HMM_CARRIER_ASSIGNMENTS_V'
ORDER BY role.role_name;

PROMPT === HMM carrier team VPD ready ===
