<?php
/**
 * acl-scope-enforce.php — اعمال محدوده (scope) در APIها
 */

if ( ! defined( 'ABSPATH' ) ) exit;

function acl_current_user_id(): int {
    $uid = (int) get_current_user_id();
    if ( ! $uid && isset( $GLOBALS['acc_jwt_user_id'] ) ) {
        $uid = (int) $GLOBALS['acc_jwt_user_id'];
    }
    return $uid;
}

function acl_user_is_super_admin( int $user_id ): bool {
    global $wpdb;
    return (bool) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}acl_user_roles ur
         INNER JOIN {$wpdb->prefix}acl_roles r ON r.id = ur.role_id
         WHERE ur.user_id = %d AND r.slug = 'super_admin'",
        $user_id
    ) );
}

/**
 * آیا کاربر برای این scope_type محدودیت ندارد (سراسری)؟
 * null در get_allowed = همه مجاز
 */
function acl_is_scope_unrestricted( int $user_id, string $scope_type ): bool {
    if ( $user_id <= 0 ) return false;
    if ( acl_user_is_super_admin( $user_id ) ) return true;

    global $wpdb;
    $scoped = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}acl_user_roles
         WHERE user_id = %d AND scope_type = %s AND scope_id IS NOT NULL",
        $user_id, $scope_type
    ) );
    if ( $scoped > 0 ) return false;

    $global = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$wpdb->prefix}acl_user_roles
         WHERE user_id = %d AND scope_type = 'global'",
        $user_id
    ) );
    return $global > 0;
}

/**
 * لیست IDهای مجاز — null یعنی همه، [] یعنی هیچ
 */
function acl_get_allowed_scope_ids( int $user_id, string $scope_type ): ?array {
    if ( acl_is_scope_unrestricted( $user_id, $scope_type ) ) return null;
    $ids = acl_get_user_scope_ids( $user_id, $scope_type );
    return $ids ?: [];
}

/**
 * ساخت بند SQL برای فیلتر ستون
 * @return array{sql:string,args:array}|null  null = بدون فیلتر
 */
function acl_scope_sql_filter( int $user_id, string $scope_type, string $column ): ?array {
    $allowed = acl_get_allowed_scope_ids( $user_id, $scope_type );
    if ( $allowed === null ) return null;
    if ( ! $allowed ) {
        return ['sql' => ' AND 1=0 ', 'args' => []];
    }
    $ph = implode( ',', array_fill( 0, count( $allowed ), '%d' ) );
    return [
        'sql'  => " AND {$column} IN ({$ph}) ",
        'args' => $allowed,
    ];
}

/**
 * پاسخ 403 استاندارد یا null اگر مجاز
 */
function acl_deny_scope( int $user_id, string $scope_type, ?int $entity_id, string $permission_key = '' ): ?WP_REST_Response {
    if ( $entity_id === null || $entity_id <= 0 ) {
        if ( ! acl_is_scope_unrestricted( $user_id, $scope_type ) ) {
            return new WP_REST_Response( [
                'success' => false,
                'message' => 'دسترسی به این ' . acl_scope_type_label( $scope_type ) . ' مجاز نیست',
            ], 403 );
        }
        return null;
    }
    if ( $permission_key && ! acl_user_can( $user_id, $permission_key, $scope_type, $entity_id ) ) {
        return new WP_REST_Response( [
            'success' => false,
            'message' => 'دسترسی به این ' . acl_scope_type_label( $scope_type ) . ' مجاز نیست',
        ], 403 );
    }
    if ( ! $permission_key ) {
        $allowed = acl_get_allowed_scope_ids( $user_id, $scope_type );
        if ( $allowed === null ) return null;
        if ( ! in_array( $entity_id, $allowed, true ) ) {
            return new WP_REST_Response( [
                'success' => false,
                'message' => 'دسترسی به این ' . acl_scope_type_label( $scope_type ) . ' مجاز نیست',
            ], 403 );
        }
    }
    return null;
}

function acl_scope_type_label( string $scope_type ): string {
    $labels = [
        'warehouse'     => 'انبار',
        'project'       => 'پروژه',
        'district'      => 'منطقه',
        'neighborhood'  => 'محله',
    ];
    return $labels[ $scope_type ] ?? $scope_type;
}

/**
 * فیلتر جغرافیایی ترکیبی: منطقه و/یا محله (OR بین دو نوع scope)
 * @return bool false = دسترسی به هیچ‌چیز
 */
function acl_append_geo_scope_where( int $user_id, string $district_col, string $neighborhood_col, array &$wheres, array &$args ): bool {
    if ( acl_user_is_super_admin( $user_id ) ) return true;

    $district_allowed = acl_get_allowed_scope_ids( $user_id, 'district' );
    $neigh_allowed    = acl_get_allowed_scope_ids( $user_id, 'neighborhood' );

    if ( $district_allowed === null && $neigh_allowed === null ) return true;

    $conds = [];
    if ( $district_allowed !== null && $district_allowed ) {
        $ph     = implode( ',', array_fill( 0, count( $district_allowed ), '%d' ) );
        $conds[] = "{$district_col} IN ({$ph})";
        $args    = array_merge( $args, $district_allowed );
    }
    if ( $neigh_allowed !== null && $neigh_allowed ) {
        $ph     = implode( ',', array_fill( 0, count( $neigh_allowed ), '%d' ) );
        $conds[] = "{$neighborhood_col} IN ({$ph})";
        $args    = array_merge( $args, $neigh_allowed );
    }
    if ( ! $conds ) return false;

    $wheres[] = '(' . implode( ' OR ', $conds ) . ')';
    return true;
}

/**
 * بررسی چند انبار در ردیف‌های سند
 */
function acl_deny_warehouses_in_items( int $user_id, array $items, string $permission = 'inv.vouchers.create' ): ?WP_REST_Response {
    foreach ( $items as $item ) {
        foreach ( ['from_warehouse_id', 'to_warehouse_id', 'warehouse_id'] as $key ) {
            if ( empty( $item[ $key ] ) ) continue;
            $deny = acl_deny_scope( $user_id, 'warehouse', (int) $item[ $key ], $permission );
            if ( $deny ) return $deny;
        }
    }
    return null;
}

/**
 * فیلتر آرایه نتایج بر اساس warehouse_id
 */
function acl_filter_rows_by_warehouse( array $rows, int $user_id, string $field = 'warehouse_id' ): array {
    return acl_filter_rows_by_scope( $rows, $user_id, 'warehouse', $field );
}

/**
 * فیلتر آرایه نتایج بر اساس scope_type
 */
function acl_filter_rows_by_scope( array $rows, int $user_id, string $scope_type, string $field = 'id' ): array {
    $allowed = acl_get_allowed_scope_ids( $user_id, $scope_type );
    if ( $allowed === null ) return $rows;
    if ( ! $allowed ) return [];
    return array_values( array_filter( $rows, function( $r ) use ( $allowed, $field ) {
        $id = isset( $r[ $field ] ) ? (int) $r[ $field ] : 0;
        return $id > 0 && in_array( $id, $allowed, true );
    } ) );
}

/**
 * افزودن شرط scope به آرایه wheres (برای کوئری‌های پویا)
 * @return bool false = دسترسی به هیچ‌چیز
 */
function acl_append_scope_where( int $user_id, string $scope_type, string $column, array &$wheres, array &$args ): bool {
    $filter = acl_scope_sql_filter( $user_id, $scope_type, $column );
    if ( $filter === null ) return true;
    if ( trim( $filter['sql'] ) === 'AND 1=0' || trim( $filter['sql'] ) === '1=0' ) return false;
    $wheres[] = ltrim( $filter['sql'], ' AND' );
    $args     = array_merge( $args, $filter['args'] );
    return true;
}

/**
 * پاسخ 403 اگر کاربر محدود به scope است (برای عملیات سراسری مثل ایجاد)
 */
function acl_deny_if_scope_restricted( int $user_id, string $scope_type, string $message = '' ): ?WP_REST_Response {
    if ( acl_is_scope_unrestricted( $user_id, $scope_type ) ) return null;
    return new WP_REST_Response( [
        'success' => false,
        'message' => $message ?: 'این عملیات فقط برای کاربران با دسترسی سراسری مجاز است',
    ], 403 );
}

/** استخراج district_id از block_code (مثلاً 6-91-24 → 6) */
function acl_block_code_district_id( string $block_code ): ?int {
    $parts = explode( '-', trim( $block_code ) );
    if ( ! $parts || ! is_numeric( $parts[0] ) ) return null;
    return (int) $parts[0];
}

function acl_deny_block_code( int $user_id, string $block_code, string $perm = 'gis.view' ): ?WP_REST_Response {
    $did = acl_block_code_district_id( $block_code );
    return $did ? acl_deny_scope( $user_id, 'district', $did, $perm ) : null;
}

/** بررسی دسترسی پروژه روی یک رکورد (project_id ممکن است null باشد) */
function acl_deny_entity_project( int $user_id, string $table, int $entity_id, string $perm = '', string $project_column = 'project_id' ): ?WP_REST_Response {
    global $wpdb;
    $pid = $wpdb->get_var( $wpdb->prepare(
        "SELECT {$project_column} FROM {$wpdb->prefix}{$table} WHERE id = %d", $entity_id
    ) );
    if ( $pid === null ) {
        return new WP_REST_Response( [ 'success' => false, 'message' => 'یافت نشد' ], 404 );
    }
    if ( ! $pid ) {
        if ( ! acl_is_scope_unrestricted( $user_id, 'project' ) ) {
            return new WP_REST_Response( [ 'success' => false, 'message' => 'دسترسی به این پروژه مجاز نیست' ], 403 );
        }
        return null;
    }
    return acl_deny_scope( $user_id, 'project', (int) $pid, $perm );
}

function acl_deny_wf_instance( int $user_id, int $instance_id, string $perm = 'cpm.view' ): ?WP_REST_Response {
    return acl_deny_entity_project( $user_id, 'wf_instances', $instance_id, $perm );
}

function acl_deny_wf_stage( int $user_id, int $stage_id, string $perm = 'cpm.view' ): ?WP_REST_Response {
    global $wpdb;
    $iid = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT instance_id FROM {$wpdb->prefix}wf_instance_stages WHERE id = %d", $stage_id
    ) );
    if ( ! $iid ) return new WP_REST_Response( [ 'success' => false, 'message' => 'یافت نشد' ], 404 );
    return acl_deny_wf_instance( $user_id, $iid, $perm );
}

function acl_deny_wf_task( int $user_id, int $task_id, string $perm = 'cpm.view' ): ?WP_REST_Response {
    global $wpdb;
    $iid = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT s.instance_id FROM {$wpdb->prefix}wf_instance_tasks t
         INNER JOIN {$wpdb->prefix}wf_instance_stages s ON s.id = t.instance_stage_id
         WHERE t.id = %d", $task_id
    ) );
    if ( ! $iid ) return new WP_REST_Response( [ 'success' => false, 'message' => 'یافت نشد' ], 404 );
    return acl_deny_wf_instance( $user_id, $iid, $perm );
}

/**
 * scopeهای کاربر فعلی برای فرانت
 */
function acl_get_current_user_scopes(): array {
    $uid = acl_current_user_id();
    if ( ! $uid ) return [];

    $types = ['warehouse', 'project', 'district', 'neighborhood'];
    $out   = [];
    foreach ( $types as $t ) {
        $allowed = acl_get_allowed_scope_ids( $uid, $t );
        $out[ $t ] = [
            'unrestricted' => $allowed === null,
            'ids'          => $allowed ?? [],
        ];
    }
    return $out;
}
