<?php

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

/**
 * Called on plugin activation via register_activation_hook().
 * Creates all required database tables if they do not exist.
 */
function acc_engine_install(): void {
    global $wpdb;

    $charset = $wpdb->get_charset_collate();

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';

    // -------------------------------------------------------
    // ERP Core Tables
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_entities (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        entity_name  VARCHAR(100)    NOT NULL,
        table_name   VARCHAR(100)    NOT NULL,
        primary_key  VARCHAR(100)    NOT NULL DEFAULT 'id',
        label        VARCHAR(200)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        UNIQUE KEY entity_name (entity_name)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_fields (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        entity_name  VARCHAR(100)    NOT NULL,
        field_name   VARCHAR(100)    NOT NULL,
        field_type   VARCHAR(50)     NOT NULL DEFAULT 'text',
        label        VARCHAR(200)    NOT NULL DEFAULT '',
        is_required  TINYINT(1)      NOT NULL DEFAULT 0,
        field_order  INT             NOT NULL DEFAULT 0,
        PRIMARY KEY (id),
        KEY entity_name (entity_name)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_api_routes (
        id                  BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        route               VARCHAR(300)    NOT NULL,
        method              VARCHAR(10)     NOT NULL DEFAULT 'GET',
        callback            VARCHAR(200)    NOT NULL,
        permission_callback VARCHAR(200)    NOT NULL DEFAULT 'acc_jwt_check_permission',
        PRIMARY KEY (id),
        UNIQUE KEY route_method (route, method)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_ui_permissions (
        id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        entity_key VARCHAR(100)    NOT NULL,
        action     VARCHAR(50)     NOT NULL,
        role       VARCHAR(100)    NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY entity_action_role (entity_key, action, role)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_workflow_states (
        id        BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        state_key VARCHAR(100)    NOT NULL,
        label     VARCHAR(200)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        UNIQUE KEY state_key (state_key)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_workflow_instances (
        id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        entity_key VARCHAR(100)    NOT NULL,
        object_id  BIGINT UNSIGNED NOT NULL,
        state_id   BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY entity_object (entity_key, object_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_user_roles (
        id      BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        user_id BIGINT UNSIGNED NOT NULL,
        role_id BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY user_role (user_id, role_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_role_permissions (
        id             BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        role_id        BIGINT UNSIGNED NOT NULL,
        permission_key VARCHAR(200)    NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY role_permission (role_id, permission_key)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}erp_object_permissions (
        id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        user_id    BIGINT UNSIGNED NOT NULL,
        entity_key VARCHAR(100)    NOT NULL,
        object_id  BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY user_entity_object (user_id, entity_key, object_id)
    ) {$charset};");

    // -------------------------------------------------------
    // ACL Tables (accj_)
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_roles (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        name        VARCHAR(200)    NOT NULL,
        slug        VARCHAR(100)    NOT NULL,
        description TEXT,
        PRIMARY KEY (id),
        UNIQUE KEY slug (slug)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_permissions (
        id             BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        permission_key VARCHAR(200)    NOT NULL,
        label          VARCHAR(200)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        UNIQUE KEY permission_key (permission_key)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_role_permissions (
        id             BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        role_id        BIGINT UNSIGNED NOT NULL,
        permission_key VARCHAR(200)    NOT NULL,
        scope          VARCHAR(50)     NOT NULL DEFAULT 'global',
        PRIMARY KEY (id),
        UNIQUE KEY role_perm_scope (role_id, permission_key, scope)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_user_roles (
        id      BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        user_id BIGINT UNSIGNED NOT NULL,
        role_id BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY user_role (user_id, role_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_menus (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        parent_id   BIGINT UNSIGNED NOT NULL DEFAULT 0,
        title       VARCHAR(200)    NOT NULL,
        description TEXT,
        icon        VARCHAR(100)    NOT NULL DEFAULT '',
        route       VARCHAR(300)    NOT NULL DEFAULT '',
        menu_order  INT             NOT NULL DEFAULT 0,
        is_active   TINYINT(1)      NOT NULL DEFAULT 1,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}accj_menu_permissions (
        id       BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        menu_id  BIGINT UNSIGNED NOT NULL,
        role_id  BIGINT UNSIGNED NOT NULL,
        can_view TINYINT(1)      NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        UNIQUE KEY menu_role (menu_id, role_id)
    ) {$charset};");

    // -------------------------------------------------------
    // CPM (Critical Path Method) Tables
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_projects (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        title       VARCHAR(300)    NOT NULL,
        description TEXT,
        start_date  DATE,
        end_date    DATE,
        status      VARCHAR(50)     NOT NULL DEFAULT 'planning',
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_tasks (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        project_id  BIGINT UNSIGNED NOT NULL,
        title       VARCHAR(300)    NOT NULL,
        duration    INT             NOT NULL DEFAULT 0,
        start_date  DATE,
        end_date    DATE,
        wbs_code    VARCHAR(50)     NOT NULL DEFAULT '',
        status      VARCHAR(50)     NOT NULL DEFAULT 'pending',
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY project_id (project_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_task_dependencies (
        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        task_id         BIGINT UNSIGNED NOT NULL,
        depends_on_id   BIGINT UNSIGNED NOT NULL,
        type            VARCHAR(10)     NOT NULL DEFAULT 'FS',
        PRIMARY KEY (id),
        UNIQUE KEY task_dep (task_id, depends_on_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_resources (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        project_id   BIGINT UNSIGNED NOT NULL,
        name         VARCHAR(200)    NOT NULL,
        type         VARCHAR(50)     NOT NULL DEFAULT 'human',
        unit_cost    DECIMAL(15,2)   NOT NULL DEFAULT 0.00,
        PRIMARY KEY (id),
        KEY project_id (project_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_task_assignments (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        task_id     BIGINT UNSIGNED NOT NULL,
        resource_id BIGINT UNSIGNED NOT NULL,
        units       DECIMAL(10,2)   NOT NULL DEFAULT 1.00,
        PRIMARY KEY (id),
        UNIQUE KEY task_resource (task_id, resource_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}cp_financial_items (
        id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        project_id BIGINT UNSIGNED NOT NULL,
        title      VARCHAR(300)    NOT NULL,
        amount     DECIMAL(15,2)   NOT NULL DEFAULT 0.00,
        item_date  DATE,
        type       VARCHAR(50)     NOT NULL DEFAULT 'cost',
        PRIMARY KEY (id),
        KEY project_id (project_id)
    ) {$charset};");

    // -------------------------------------------------------
    // GIS Tables
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_districts (
        id                 INT           NOT NULL AUTO_INCREMENT,
        city               VARCHAR(100)  NOT NULL DEFAULT 'تهران',
        name               VARCHAR(200)  NOT NULL,
        code               VARCHAR(100)  NOT NULL DEFAULT '',
        area_km2           DECIMAL(10,4) DEFAULT NULL,
        assigned_user_id   INT           DEFAULT NULL,
        assigned_user_name VARCHAR(200)  NOT NULL DEFAULT '',
        color              VARCHAR(20)   NOT NULL DEFAULT '#3b82f6',
        geometry           LONGTEXT      DEFAULT NULL,
        notes              TEXT,
        created_at         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_neighborhoods (
        id                 INT           NOT NULL AUTO_INCREMENT,
        district_id        INT           NOT NULL DEFAULT 0,
        name               VARCHAR(200)  NOT NULL,
        area_km2           DECIMAL(10,4) DEFAULT NULL,
        assigned_user_id   INT           DEFAULT NULL,
        assigned_user_name VARCHAR(200)  NOT NULL DEFAULT '',
        dominant_use       VARCHAR(100)  NOT NULL DEFAULT '',
        development_trend  VARCHAR(50)   NOT NULL DEFAULT 'stable',
        has_metro          TINYINT(1)    NOT NULL DEFAULT 0,
        has_brt            TINYINT(1)    NOT NULL DEFAULT 0,
        infrastructure     VARCHAR(50)   NOT NULL DEFAULT 'complete',
        geometry           LONGTEXT      DEFAULT NULL,
        notes              TEXT,
        created_at         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_district (district_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcels (
        id                 INT           NOT NULL AUTO_INCREMENT,
        district_id        INT           DEFAULT NULL,
        neighborhood_id    INT           DEFAULT NULL,
        assigned_user_id   INT           DEFAULT NULL,
        assigned_user_name VARCHAR(200)  NOT NULL DEFAULT '',
        code               VARCHAR(200)  DEFAULT NULL,
        sub_code           VARCHAR(100)  NOT NULL DEFAULT '',
        registry_section   VARCHAR(100)  NOT NULL DEFAULT '',
        address            TEXT,
        lat                DECIMAL(10,8) DEFAULT NULL,
        lng                DECIMAL(11,8) DEFAULT NULL,
        geometry           LONGTEXT      DEFAULT NULL,
        status             VARCHAR(50)   NOT NULL DEFAULT 'prospect',
        pipeline_stage     VARCHAR(50)   NOT NULL DEFAULT 'discovery',
        cpm_project_id     INT           DEFAULT NULL,
        locked_by_user_id  INT           DEFAULT NULL,
        locked_by_name     VARCHAR(200)  DEFAULT NULL,
        locked_at          DATETIME      DEFAULT NULL,
        notes              TEXT,
        created_at         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at         DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_district (district_id),
        KEY idx_neighborhood (neighborhood_id),
        KEY idx_status (status)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_land (
        id                  INT           NOT NULL AUTO_INCREMENT,
        parcel_id           INT           NOT NULL,
        area_sqm            DECIMAL(10,2) NOT NULL DEFAULT 0,
        north_dim           DECIMAL(8,2)  DEFAULT NULL,
        south_dim           DECIMAL(8,2)  DEFAULT NULL,
        east_dim            DECIMAL(8,2)  DEFAULT NULL,
        west_dim            DECIMAL(8,2)  DEFAULT NULL,
        shape               VARCHAR(50)   NOT NULL DEFAULT 'rectangular',
        position            VARCHAR(50)   NOT NULL DEFAULT 'middle',
        ownership_type      VARCHAR(50)   NOT NULL DEFAULT 'private',
        is_national_land    TINYINT(1)    NOT NULL DEFAULT 0,
        main_street_width   DECIMAL(6,2)  DEFAULT NULL,
        main_street_dir     VARCHAR(20)   NOT NULL DEFAULT '',
        second_street_width DECIMAL(6,2)  DEFAULT NULL,
        second_street_dir   VARCHAR(20)   NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        UNIQUE KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_building (
        id               INT           NOT NULL AUTO_INCREMENT,
        parcel_id        INT           NOT NULL,
        has_building     TINYINT(1)    NOT NULL DEFAULT 0,
        floors           INT           DEFAULT NULL,
        units            INT           DEFAULT NULL,
        shops            INT           DEFAULT NULL,
        total_area_sqm   DECIMAL(10,2) DEFAULT NULL,
        built_year       INT           DEFAULT NULL,
        structure_type   VARCHAR(50)   NOT NULL DEFAULT 'concrete',
        facade           VARCHAR(100)  NOT NULL DEFAULT '',
        has_basement     TINYINT(1)    NOT NULL DEFAULT 0,
        has_permit       VARCHAR(20)   NOT NULL DEFAULT 'unknown',
        permit_number    VARCHAR(100)  NOT NULL DEFAULT '',
        has_completion   VARCHAR(20)   NOT NULL DEFAULT 'no',
        has_water        TINYINT(1)    NOT NULL DEFAULT 0,
        has_electricity  TINYINT(1)    NOT NULL DEFAULT 0,
        has_gas          TINYINT(1)    NOT NULL DEFAULT 0,
        has_elevator     TINYINT(1)    NOT NULL DEFAULT 0,
        has_parking      TINYINT(1)    NOT NULL DEFAULT 0,
        has_storage      TINYINT(1)    NOT NULL DEFAULT 0,
        PRIMARY KEY (id),
        UNIQUE KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_use (
        id             INT           NOT NULL AUTO_INCREMENT,
        parcel_id      INT           NOT NULL,
        current_use    VARCHAR(100)  NOT NULL DEFAULT '',
        approved_use   VARCHAR(100)  NOT NULL DEFAULT '',
        density_pct    DECIMAL(6,2)  DEFAULT NULL,
        coverage_pct   DECIMAL(6,2)  DEFAULT NULL,
        max_floors     INT           DEFAULT NULL,
        max_build_area DECIMAL(10,2) DEFAULT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_owners (
        id              INT          NOT NULL AUTO_INCREMENT,
        parcel_id       INT          NOT NULL,
        name            VARCHAR(300) NOT NULL,
        national_id     VARCHAR(20)  NOT NULL DEFAULT '',
        ownership_share VARCHAR(20)  NOT NULL DEFAULT '6/6',
        ownership_type  VARCHAR(50)  NOT NULL DEFAULT 'full',
        deed_type       VARCHAR(50)  NOT NULL DEFAULT 'single_page',
        deed_number     VARCHAR(100) NOT NULL DEFAULT '',
        registry_office VARCHAR(200) NOT NULL DEFAULT '',
        deed_date       VARCHAR(20)  NOT NULL DEFAULT '',
        phone           VARCHAR(20)  NOT NULL DEFAULT '',
        is_primary      TINYINT(1)   NOT NULL DEFAULT 0,
        created_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_ownership_history (
        id             INT           NOT NULL AUTO_INCREMENT,
        parcel_id      INT           NOT NULL,
        owner_name     VARCHAR(300)  NOT NULL,
        transfer_type  VARCHAR(50)   NOT NULL DEFAULT 'sale',
        transfer_date  VARCHAR(20)   NOT NULL DEFAULT '',
        transfer_price VARCHAR(255)  DEFAULT NULL,
        notes          TEXT,
        created_at     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_issues (
        id          INT           NOT NULL AUTO_INCREMENT,
        parcel_id   INT           NOT NULL,
        type        VARCHAR(50)   NOT NULL DEFAULT 'other',
        severity    VARCHAR(20)   NOT NULL DEFAULT 'warning',
        title       VARCHAR(300)  NOT NULL DEFAULT '',
        description TEXT,
        amount      DECIMAL(15,2) DEFAULT NULL,
        status      VARCHAR(20)   NOT NULL DEFAULT 'active',
        resolved_at VARCHAR(50)   NOT NULL DEFAULT '',
        created_at  DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parcel (parcel_id),
        KEY idx_severity (severity),
        KEY idx_status (status)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_commissions (
        id             INT           NOT NULL AUTO_INCREMENT,
        parcel_id      INT           NOT NULL,
        year           INT           NOT NULL,
        violation_type VARCHAR(50)   NOT NULL DEFAULT 'other',
        description    TEXT,
        verdict        VARCHAR(50)   NOT NULL DEFAULT 'fine',
        fine_amount    DECIMAL(15,2) DEFAULT NULL,
        verdict_status VARCHAR(50)   NOT NULL DEFAULT 'pending',
        case_number    VARCHAR(100)  NOT NULL DEFAULT '',
        created_at     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parcel (parcel_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_parcel_units (
        id               INT          NOT NULL AUTO_INCREMENT,
        parcel_id        INT          NOT NULL,
        renovation_code  VARCHAR(100) NOT NULL,
        created_at       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parcel (parcel_id),
        KEY idx_code (renovation_code)
    ) {$charset};");

    // -------------------------------------------------------
    // Inventory (انبار) Tables
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_categories (
        id         INT          NOT NULL AUTO_INCREMENT,
        parent_id  INT          NOT NULL DEFAULT 0,
        code       VARCHAR(50)  NOT NULL DEFAULT '',
        name       VARCHAR(200) NOT NULL,
        level      TINYINT      NOT NULL DEFAULT 0,
        path       VARCHAR(500) NOT NULL DEFAULT '',
        created_at DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parent (parent_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_products (
        id                INT           NOT NULL AUTO_INCREMENT,
        category_id       INT           DEFAULT NULL,
        code              VARCHAR(100)  NOT NULL DEFAULT '',
        name              VARCHAR(300)  NOT NULL,
        unit              VARCHAR(30)   NOT NULL DEFAULT 'عدد',
        unit2             VARCHAR(30)   NOT NULL DEFAULT '',
        conversion_factor DECIMAL(10,4) NOT NULL DEFAULT 1,
        costing_method    VARCHAR(10)   NOT NULL DEFAULT 'avg',
        tracking          VARCHAR(10)   NOT NULL DEFAULT 'none',
        min_stock         DECIMAL(14,4) NOT NULL DEFAULT 0,
        max_stock         DECIMAL(14,4) NOT NULL DEFAULT 0,
        reorder_qty       DECIMAL(14,4) NOT NULL DEFAULT 0,
        description       TEXT,
        is_active         TINYINT(1)    NOT NULL DEFAULT 1,
        created_at        DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_category (category_id),
        KEY idx_code (code)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_warehouses (
        id           INT          NOT NULL AUTO_INCREMENT,
        code         VARCHAR(50)  NOT NULL DEFAULT '',
        name         VARCHAR(200) NOT NULL,
        type         VARCHAR(20)  NOT NULL DEFAULT 'main',
        location     VARCHAR(300) NOT NULL DEFAULT '',
        manager_name VARCHAR(200) NOT NULL DEFAULT '',
        is_active    TINYINT(1)   NOT NULL DEFAULT 1,
        created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_stock (
        id           INT           NOT NULL AUTO_INCREMENT,
        product_id   INT           NOT NULL,
        warehouse_id INT           NOT NULL,
        qty          DECIMAL(14,4) NOT NULL DEFAULT 0,
        avg_cost     DECIMAL(18,4) NOT NULL DEFAULT 0,
        updated_at   DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_product_wh (product_id, warehouse_id),
        KEY idx_product (product_id),
        KEY idx_warehouse (warehouse_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_vouchers (
        id            INT          NOT NULL AUTO_INCREMENT,
        voucher_no    VARCHAR(50)  NOT NULL DEFAULT '',
        type          VARCHAR(30)  NOT NULL,
        status        VARCHAR(15)  NOT NULL DEFAULT 'draft',
        ref_id        INT          DEFAULT NULL,
        ref_type      VARCHAR(50)  NOT NULL DEFAULT '',
        ref_no        VARCHAR(100) NOT NULL DEFAULT '',
        date          DATE         NOT NULL,
        description   TEXT,
        created_by    VARCHAR(200) NOT NULL DEFAULT '',
        confirmed_by  VARCHAR(200) NOT NULL DEFAULT '',
        created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        confirmed_at  DATETIME     DEFAULT NULL,
        PRIMARY KEY (id),
        KEY idx_type   (type),
        KEY idx_status (status),
        KEY idx_date   (date),
        KEY idx_ref    (ref_type, ref_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_voucher_items (
        id                INT           NOT NULL AUTO_INCREMENT,
        voucher_id        INT           NOT NULL,
        product_id        INT           NOT NULL,
        from_warehouse_id INT           DEFAULT NULL,
        to_warehouse_id   INT           DEFAULT NULL,
        qty               DECIMAL(14,4) NOT NULL DEFAULT 0,
        unit_price        DECIMAL(18,4) NOT NULL DEFAULT 0,
        total_price       DECIMAL(18,4) NOT NULL DEFAULT 0,
        batch_id          INT           DEFAULT NULL,
        notes             VARCHAR(500)  NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        KEY idx_voucher (voucher_id),
        KEY idx_product (product_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_batches (
        id          INT           NOT NULL AUTO_INCREMENT,
        product_id  INT           NOT NULL,
        batch_no    VARCHAR(100)  NOT NULL,
        mfg_date    DATE          DEFAULT NULL,
        exp_date    DATE          DEFAULT NULL,
        cost_price  DECIMAL(18,4) NOT NULL DEFAULT 0,
        notes       VARCHAR(500)  NOT NULL DEFAULT '',
        created_at  DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_product  (product_id),
        KEY idx_batch_no (batch_no)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_batch_stock (
        id           INT           NOT NULL AUTO_INCREMENT,
        batch_id     INT           NOT NULL,
        warehouse_id INT           NOT NULL,
        qty          DECIMAL(14,4) NOT NULL DEFAULT 0,
        PRIMARY KEY (id),
        UNIQUE KEY idx_batch_wh (batch_id, warehouse_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_serials (
        id              INT          NOT NULL AUTO_INCREMENT,
        product_id      INT          NOT NULL,
        serial_no       VARCHAR(200) NOT NULL,
        status          VARCHAR(20)  NOT NULL DEFAULT 'in_stock',
        warehouse_id    INT          DEFAULT NULL,
        batch_id        INT          DEFAULT NULL,
        voucher_item_id INT          DEFAULT NULL,
        issued_to       VARCHAR(300) NOT NULL DEFAULT '',
        notes           VARCHAR(500) NOT NULL DEFAULT '',
        created_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_serial (product_id, serial_no),
        KEY idx_product   (product_id),
        KEY idx_status    (status),
        KEY idx_warehouse (warehouse_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}inv_fifo_layers (
        id              INT           NOT NULL AUTO_INCREMENT,
        product_id      INT           NOT NULL,
        warehouse_id    INT           NOT NULL,
        voucher_item_id INT           NOT NULL,
        qty_original    DECIMAL(14,4) NOT NULL DEFAULT 0,
        qty_remaining   DECIMAL(14,4) NOT NULL DEFAULT 0,
        unit_cost       DECIMAL(18,4) NOT NULL DEFAULT 0,
        created_at      DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_product_wh (product_id, warehouse_id),
        KEY idx_remaining  (qty_remaining)
    ) {$charset};");

    // -------------------------------------------------------
    // User Preferences (تاریخچه و تنظیمات کاربر — cross-device)
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}user_preferences (
        id               INT          NOT NULL AUTO_INCREMENT,
        user_id          BIGINT UNSIGNED NOT NULL,
        preference_key   VARCHAR(100) NOT NULL,
        preference_value LONGTEXT     NOT NULL,
        updated_at       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_user_key (user_id, preference_key),
        KEY idx_user (user_id)
    ) {$charset};");

    // -------------------------------------------------------
    // ادغام انبار با خرید و فروش — ستون‌های جدید
    // -------------------------------------------------------

    // inv_product_id در جدول محصولات فروش
    $col = $wpdb->get_var("SHOW COLUMNS FROM {$wpdb->prefix}ai_sale_products LIKE 'inv_product_id'");
    if ( ! $col ) {
        $wpdb->query("ALTER TABLE {$wpdb->prefix}ai_sale_products ADD COLUMN inv_product_id INT DEFAULT NULL");
        $wpdb->query("ALTER TABLE {$wpdb->prefix}ai_sale_products ADD KEY idx_inv_product (inv_product_id)");
    }

    // warehouse_id در فاکتور خرید
    $col = $wpdb->get_var("SHOW COLUMNS FROM {$wpdb->prefix}ai_purchase_invoices LIKE 'warehouse_id'");
    if ( ! $col ) {
        $wpdb->query("ALTER TABLE {$wpdb->prefix}ai_purchase_invoices ADD COLUMN warehouse_id INT DEFAULT NULL");
    }

    // warehouse_id در فاکتور فروش
    $col = $wpdb->get_var("SHOW COLUMNS FROM {$wpdb->prefix}ai_sale_invoices LIKE 'warehouse_id'");
    if ( ! $col ) {
        $wpdb->query("ALTER TABLE {$wpdb->prefix}ai_sale_invoices ADD COLUMN warehouse_id INT DEFAULT NULL");
    }

    // -------------------------------------------------------
    // ACL یکپارچه ۳ لایه — حذف جداول قدیمی
    // -------------------------------------------------------
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_menu_permissions");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_menus");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_user_roles");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_role_permissions");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_permissions");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}accj_roles");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}erp_user_roles");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}erp_role_permissions");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}erp_object_permissions");
    $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}erp_ui_permissions");

    // -------------------------------------------------------
    // ACL — جداول جدید
    // -------------------------------------------------------

    // 1) لیست مرجع همه مجوزها (ماژول.منبع.عمل)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_permissions (
        id              INT          NOT NULL AUTO_INCREMENT,
        permission_key  VARCHAR(150) NOT NULL,
        label           VARCHAR(200) NOT NULL DEFAULT '',
        module          VARCHAR(50)  NOT NULL DEFAULT '',
        resource        VARCHAR(50)  NOT NULL DEFAULT '',
        action          VARCHAR(50)  NOT NULL DEFAULT '',
        is_system       TINYINT(1)   NOT NULL DEFAULT 0,
        created_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_key (permission_key),
        KEY idx_module (module)
    ) {$charset};");

    // 2) نقش‌ها
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_roles (
        id          INT          NOT NULL AUTO_INCREMENT,
        slug        VARCHAR(100) NOT NULL,
        name        VARCHAR(200) NOT NULL,
        description TEXT,
        is_system   TINYINT(1)   NOT NULL DEFAULT 0,
        sort_order  INT          NOT NULL DEFAULT 0,
        created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_slug (slug)
    ) {$charset};");

    // 3) مجوزهای هر نقش
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_role_permissions (
        id             INT          NOT NULL AUTO_INCREMENT,
        role_id        INT          NOT NULL,
        permission_key VARCHAR(150) NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY idx_role_perm (role_id, permission_key),
        KEY idx_role (role_id)
    ) {$charset};");

    // 4) کاربر ↔ نقش (با scope)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_user_roles (
        id          INT             NOT NULL AUTO_INCREMENT,
        user_id     BIGINT UNSIGNED NOT NULL,
        role_id     INT             NOT NULL,
        scope_type  VARCHAR(50)     NOT NULL DEFAULT 'global',
        scope_id    INT             DEFAULT NULL,
        granted_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        granted_by  BIGINT UNSIGNED DEFAULT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY idx_user_role_scope (user_id, role_id, scope_type, scope_id),
        KEY idx_user (user_id),
        KEY idx_role (role_id),
        KEY idx_scope (scope_type, scope_id)
    ) {$charset};");

    // 5) Override روی شیء خاص (allow/deny)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_object_overrides (
        id             INT             NOT NULL AUTO_INCREMENT,
        user_id        BIGINT UNSIGNED NOT NULL,
        entity_type    VARCHAR(50)     NOT NULL,
        entity_id      INT             NOT NULL,
        permission_key VARCHAR(150)    NOT NULL,
        effect         VARCHAR(10)     NOT NULL DEFAULT 'allow',
        notes          VARCHAR(500)    NOT NULL DEFAULT '',
        created_at     DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_uniq (user_id, entity_type, entity_id, permission_key),
        KEY idx_user (user_id),
        KEY idx_entity (entity_type, entity_id)
    ) {$charset};");

    // 6) منوها با مجوز لازم
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_menus (
        id                  INT          NOT NULL AUTO_INCREMENT,
        menu_key            VARCHAR(100) NOT NULL,
        parent_key          VARCHAR(100) NOT NULL DEFAULT '',
        title               VARCHAR(200) NOT NULL,
        icon                VARCHAR(50)  NOT NULL DEFAULT '',
        route               VARCHAR(200) NOT NULL DEFAULT '',
        required_permission VARCHAR(150) NOT NULL DEFAULT '',
        sort_order          INT          NOT NULL DEFAULT 0,
        is_active           TINYINT(1)   NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        UNIQUE KEY idx_key (menu_key),
        KEY idx_parent (parent_key)
    ) {$charset};");

    // 7) منوهای صریح هر کاربر (پروفایل دسترسی)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}acl_user_menu_access (
        user_id   BIGINT UNSIGNED NOT NULL,
        menu_key  VARCHAR(100)    NOT NULL,
        granted_at DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (user_id, menu_key),
        KEY idx_menu (menu_key)
    ) {$charset};");

    // -------------------------------------------------------
    // ACL — Seed Data
    // -------------------------------------------------------

    // مجوزهای پیش‌فرض همه ماژول‌ها
    $default_permissions = [
        // GIS
        ['gis.view',              'مشاهده نقشه',              'gis', 'map',      'view'],
        ['gis.parcels.view',      'مشاهده پارسل',              'gis', 'parcels',  'view'],
        ['gis.parcels.create',    'ثبت پارسل جدید',            'gis', 'parcels',  'create'],
        ['gis.parcels.edit',      'ویرایش پارسل',              'gis', 'parcels',  'edit'],
        ['gis.parcels.delete',    'حذف پارسل',                 'gis', 'parcels',  'delete'],
        ['gis.parcels.lock',      'قفل / آزاد کردن پارسل',      'gis', 'parcels',  'lock'],

        // Inventory
        ['inv.view',              'مشاهده انبار',                'inv', 'stock',     'view'],
        ['inv.products.manage',   'مدیریت محصولات',              'inv', 'products',  'manage'],
        ['inv.warehouses.manage', 'مدیریت انبارها',              'inv', 'warehouses','manage'],
        ['inv.vouchers.create',   'ثبت سند انبار',               'inv', 'vouchers',  'create'],
        ['inv.vouchers.confirm',  'تأیید سند انبار',             'inv', 'vouchers',  'confirm'],
        ['inv.vouchers.cancel',   'لغو سند انبار',               'inv', 'vouchers',  'cancel'],
        ['inv.reports.view',      'مشاهده گزارش انبار',          'inv', 'reports',   'view'],

        // Sales
        ['sales.view',            'مشاهده فروش',                 'sales', 'invoices',  'view'],
        ['sales.invoices.create', 'ثبت فاکتور فروش',             'sales', 'invoices',  'create'],
        ['sales.invoices.edit',   'ویرایش فاکتور فروش',          'sales', 'invoices',  'edit'],
        ['sales.invoices.finalize','نهایی‌سازی فاکتور فروش',     'sales', 'invoices',  'finalize'],
        ['sales.invoices.delete', 'حذف فاکتور فروش',             'sales', 'invoices',  'delete'],
        ['sales.customers.manage','مدیریت مشتریان',              'sales', 'customers', 'manage'],

        // Purchase
        ['purchase.view',             'مشاهده خرید',              'purchase', 'invoices',  'view'],
        ['purchase.invoices.create',  'ثبت فاکتور خرید',          'purchase', 'invoices',  'create'],
        ['purchase.invoices.edit',    'ویرایش فاکتور خرید',       'purchase', 'invoices',  'edit'],
        ['purchase.invoices.finalize','نهایی‌سازی فاکتور خرید',    'purchase', 'invoices',  'finalize'],
        ['purchase.suppliers.manage', 'مدیریت تأمین‌کنندگان',     'purchase', 'suppliers', 'manage'],

        // CPM
        ['cpm.view',           'مشاهده پروژه',         'cpm', 'projects', 'view'],
        ['cpm.projects.create','ساخت پروژه',           'cpm', 'projects', 'create'],
        ['cpm.projects.edit',  'ویرایش پروژه',         'cpm', 'projects', 'edit'],
        ['cpm.projects.delete','حذف پروژه',            'cpm', 'projects', 'delete'],
        ['cpm.tasks.manage',   'مدیریت وظایف',         'cpm', 'tasks',    'manage'],

        // Accounting
        ['acc.view',     'مشاهده حسابداری',  'acc', 'ledger', 'view'],
        ['acc.post',     'صدور سند حسابداری', 'acc', 'ledger', 'post'],
        ['acc.reports',  'گزارش‌های مالی',    'acc', 'reports','view'],

        // Payroll
        ['payroll.view',   'مشاهده حقوق', 'payroll', 'payroll', 'view'],
        ['payroll.manage', 'مدیریت حقوق', 'payroll', 'payroll', 'manage'],

        // Treasury
        ['treasury.view',   'مشاهده خزانه',    'treasury', 'treasury', 'view'],
        ['treasury.manage', 'مدیریت خزانه',    'treasury', 'treasury', 'manage'],

        // CRM
        ['crm.view',   'مشاهده CRM',  'crm', 'crm', 'view'],
        ['crm.manage', 'مدیریت CRM',  'crm', 'crm', 'manage'],

        // System
        ['system.users.manage', 'مدیریت کاربران',   'system', 'users', 'manage'],
        ['system.acl.manage',   'مدیریت دسترسی‌ها', 'system', 'acl',   'manage'],
        ['system.settings',     'تنظیمات سیستم',    'system', 'settings', 'manage'],

        // Field Service
        ['service.view',            'مشاهده خدمات میدانی', 'service', 'service',  'view'],
        ['service.requests.manage', 'مدیریت درخواست‌ها',   'service', 'requests', 'manage'],
        ['service.reports.manage',  'مدیریت گزارش فنی',    'service', 'reports',  'manage'],
        ['service.devices.manage',  'مدیریت دستگاه‌ها',    'service', 'devices',  'manage'],
        ['service.senior.manage',   'پنل کارشناس ارشد',    'service', 'senior',   'manage'],
        ['service.technician.panel','پنل کارشناس میدانی',  'service', 'technician','panel'],
        ['service.dispatch',        'Dispatch خدمات میدانی','service', 'dispatch', 'manage'],
        ['service.accounting',      'حسابداری پورتال خدمات', 'service', 'accounting', 'view'],
        ['service.accounting.post', 'صدور سند خدمات',        'service', 'accounting', 'post'],

        // اقتصاد ملک — پورتال بنگاه
        ['melk.view',              'مشاهده پورتال اقتصاد ملک',     'melk', 'portal',  'view'],
        ['melk.manage',              'مدیریت سازمان بنگاه',          'melk', 'org',     'manage'],
        ['melk.cases.create',      'ایجاد پرونده ملک',             'melk', 'cases',   'create'],
        ['melk.cases.edit',        'ویرایش پرونده ملک',             'melk', 'cases',   'edit'],
        ['melk.cases.participate', 'مشارکت در پرونده (کارشناس)',   'melk', 'cases',   'participate'],
    ];

    foreach ( $default_permissions as $p ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_permissions WHERE permission_key = %s", $p[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_permissions", [
                'permission_key' => $p[0],
                'label'          => $p[1],
                'module'         => $p[2],
                'resource'       => $p[3],
                'action'         => $p[4],
                'is_system'      => 1,
            ] );
        }
    }

    // نقش‌های پیش‌فرض
    $default_roles = [
        ['super_admin',       'مدیر کل سیستم',     'دسترسی کامل به همه قسمت‌ها',  1, 1],
        ['manager',           'مدیر',              'مدیریت ماژول‌های اصلی',        2, 1],
        ['accountant',        'حسابدار',           'دسترسی به حسابداری و گزارش‌ها', 3, 1],
        ['warehouse_keeper',  'انباردار',          'مدیریت انبار و موجودی',         4, 1],
        ['sales_user',        'فروشنده',           'ثبت فروش و فاکتور',             5, 1],
        ['purchase_user',     'کارشناس خرید',     'ثبت خرید و فاکتور تأمین',       6, 1],
        ['gis_user',          'کارشناس GIS',      'مدیریت پارسل و نقشه',          7, 1],
        ['project_manager',   'مدیر پروژه',        'مدیریت پروژه‌ها (CPM)',         8, 1],
        ['viewer',            'فقط مشاهده',        'فقط دیدن، بدون ویرایش',         9, 1],
        ['service_senior',    'کارشناس ارشد خدمات', 'مدیریت تیم کارشناسان میدانی',  10, 1],
        ['service_technician','کارشناس میدانی',     'پاسخگویی به درخواست‌های تخصیص‌یافته', 11, 1],
        ['district_lead',     'سرگروه منطقه',       'مدیریت منطقه و هماهنگی بنگاه',   12, 1],
        ['neighborhood_manager','مدیر محله',        'مدیریت محله و بلوک‌های ملک',      13, 1],
        ['melk_specialist',   'کارشناس مشترک ملک',  'حقوقی، شهرداری، ثبت و ...',       14, 1],
        ['melk_coordinator',  'هماهنگ‌کننده ملک',   'نظارت سراسری شبکه بنگاه',        15, 1],
    ];

    foreach ( $default_roles as $r ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = %s", $r[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_roles", [
                'slug'        => $r[0],
                'name'        => $r[1],
                'description' => $r[2],
                'sort_order'  => $r[3],
                'is_system'   => $r[4],
            ] );
        }
    }

    // مجوزهای پیش‌فرض هر نقش
    $role_perms_map = [
        'super_admin'      => '*', // همه چیز
        'manager'          => ['gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.create',
                                'inv.view','inv.reports.view','inv.products.manage','inv.warehouses.manage',
                                'sales.view','sales.invoices.create','sales.invoices.edit','sales.invoices.finalize','sales.customers.manage',
                                'purchase.view','purchase.invoices.create','purchase.invoices.edit','purchase.invoices.finalize','purchase.suppliers.manage',
                                'cpm.view','cpm.projects.create','cpm.projects.edit','cpm.tasks.manage',
                                'acc.view','acc.reports','treasury.view','treasury.manage','crm.view','crm.manage',
                                'service.view','service.requests.manage','service.reports.manage','service.devices.manage',
                                'service.dispatch'],
        'service_senior'   => ['service.view','service.senior.manage','service.requests.manage','service.reports.manage','service.dispatch'],
        'service_technician' => ['service.view','service.technician.panel'],
        'accountant'       => ['acc.view','acc.post','acc.reports','treasury.view','treasury.manage','payroll.view','payroll.manage',
                                'sales.view','purchase.view','inv.reports.view',
                                'service.view','service.accounting','service.accounting.post'],
        'warehouse_keeper' => ['inv.view','inv.products.manage','inv.warehouses.manage','inv.vouchers.create',
                                'inv.vouchers.confirm','inv.vouchers.cancel','inv.reports.view'],
        'sales_user'       => ['sales.view','sales.invoices.create','sales.invoices.edit','sales.customers.manage','inv.view'],
        'purchase_user'    => ['purchase.view','purchase.invoices.create','purchase.invoices.edit','purchase.suppliers.manage','inv.view'],
        'gis_user'         => ['gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.create','gis.parcels.lock'],
        'project_manager'  => ['cpm.view','cpm.projects.create','cpm.projects.edit','cpm.tasks.manage','gis.view'],
        'viewer'           => ['gis.view','gis.parcels.view','inv.view','inv.reports.view','sales.view','purchase.view','cpm.view','acc.view','crm.view','treasury.view'],
        'district_lead'    => ['melk.view','melk.manage','melk.cases.create','melk.cases.edit','gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.lock'],
        'neighborhood_manager' => ['melk.view','melk.cases.create','melk.cases.edit','gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.lock'],
        'melk_specialist'  => ['melk.view','melk.cases.participate','gis.view','gis.parcels.view'],
        'melk_coordinator' => ['melk.view','melk.manage','melk.cases.create','melk.cases.edit','melk.cases.participate','gis.view','gis.parcels.view','gis.parcels.edit'],
    ];

    foreach ( $role_perms_map as $role_slug => $perms ) {
        $role_id = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = %s", $role_slug
        ) );
        if ( ! $role_id ) continue;

        $perm_keys = $perms === '*'
            ? $wpdb->get_col( "SELECT permission_key FROM {$wpdb->prefix}acl_permissions" )
            : $perms;

        foreach ( $perm_keys as $pk ) {
            // INSERT IGNORE — از خطای duplicate جلوگیری می‌کند
            $wpdb->query( $wpdb->prepare(
                "INSERT IGNORE INTO {$wpdb->prefix}acl_role_permissions (role_id, permission_key) VALUES (%d, %s)",
                $role_id, $pk
            ) );
        }
    }

    // همگام‌سازی super_admin با همه مجوزهای فعلی (برای مجوزهای اضافه‌شده بعد از نصب اول)
    $super_role_id = (int) $wpdb->get_var(
        "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = 'super_admin' LIMIT 1"
    );
    if ( $super_role_id ) {
        $all_perm_keys = $wpdb->get_col( "SELECT permission_key FROM {$wpdb->prefix}acl_permissions" ) ?: [];
        foreach ( $all_perm_keys as $pk ) {
            $wpdb->query( $wpdb->prepare(
                "INSERT IGNORE INTO {$wpdb->prefix}acl_role_permissions (role_id, permission_key) VALUES (%d, %s)",
                $super_role_id, $pk
            ) );
        }
    }

    // اختصاص super_admin به اولین کاربر admin (اگر وجود ندارد)
    $admin_user = $wpdb->get_var(
        "SELECT u.ID FROM {$wpdb->users} u
         INNER JOIN {$wpdb->usermeta} m ON m.user_id = u.ID
         WHERE m.meta_key = '{$wpdb->prefix}capabilities'
           AND m.meta_value LIKE '%administrator%'
         ORDER BY u.ID ASC LIMIT 1"
    );
    if ( $admin_user ) {
        $super_role_id = (int) $wpdb->get_var(
            "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = 'super_admin'"
        );
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_user_roles
             WHERE user_id = %d AND role_id = %d AND scope_type = 'global'",
            $admin_user, $super_role_id
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_user_roles", [
                'user_id'    => $admin_user,
                'role_id'    => $super_role_id,
                'scope_type' => 'global',
                'scope_id'   => null,
            ] );
        }
    }

    // -------------------------------------------------------
    // Chat Tables (v15)
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}chat_rooms (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        type         ENUM('direct','group','support') NOT NULL DEFAULT 'direct',
        name         VARCHAR(200)    NOT NULL DEFAULT '',
        project_id   BIGINT UNSIGNED NULL,
        created_by   BIGINT UNSIGNED NOT NULL DEFAULT 0,
        status       ENUM('open','closed','pending') NOT NULL DEFAULT 'open',
        last_msg_at  DATETIME        NULL,
        created_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY type (type),
        KEY status (status),
        KEY last_msg_at (last_msg_at)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}chat_messages (
        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        conversation_id BIGINT UNSIGNED NOT NULL,
        sender_type     ENUM('user','visitor','agent','system') NOT NULL DEFAULT 'user',
        sender_id       VARCHAR(64)     NOT NULL DEFAULT '0',
        message         TEXT            NOT NULL,
        created_at      DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY conversation_id (conversation_id),
        KEY created_at (created_at)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}chat_participants (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        room_id      BIGINT UNSIGNED NOT NULL,
        user_id      BIGINT UNSIGNED NOT NULL,
        role         ENUM('admin','member') NOT NULL DEFAULT 'member',
        last_read_at DATETIME        NULL,
        joined_at    DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY room_user (room_id, user_id),
        KEY user_id (user_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}chat_visitors (
        id           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        visitor_id   VARCHAR(64)     NOT NULL,
        name         VARCHAR(100)    NOT NULL DEFAULT 'بازدیدکننده',
        phone        VARCHAR(20)     NOT NULL DEFAULT '',
        email        VARCHAR(200)    NOT NULL DEFAULT '',
        ip_address   VARCHAR(45)     NOT NULL DEFAULT '',
        user_agent   VARCHAR(500)    NOT NULL DEFAULT '',
        created_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY visitor_id (visitor_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}chat_conversations (
        id             BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        visitor_id     VARCHAR(64)     NOT NULL,
        tracking_code  VARCHAR(6)      NOT NULL,
        agent_id       BIGINT UNSIGNED NULL,
        status         ENUM('open','pending','closed') NOT NULL DEFAULT 'open',
        source         VARCHAR(50)     NOT NULL DEFAULT 'website',
        created_at     DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at     DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY tracking_code (tracking_code),
        KEY visitor_id (visitor_id),
        KEY status (status),
        KEY updated_at (updated_at)
    ) {$charset};");

    // -------------------------------------------------------
    // Chat Migration (v16) — اصلاح ستون‌های chat_messages و chat_visitors
    // -------------------------------------------------------

    // chat_messages: اضافه کردن conversation_id (اگر وجود ندارد)
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_messages LIKE 'conversation_id'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_messages
                       ADD COLUMN conversation_id BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER id,
                       ADD KEY conversation_id (conversation_id)" );
        // انتقال از room_id به conversation_id (اگر ستون قدیمی وجود دارد)
        if ( $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_messages LIKE 'room_id'" ) ) {
            $wpdb->query( "UPDATE {$wpdb->prefix}chat_messages SET conversation_id = room_id WHERE conversation_id = 0" );
        }
    }

    // chat_messages: اضافه کردن message (اگر وجود ندارد)
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_messages LIKE 'message'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_messages ADD COLUMN message TEXT NOT NULL DEFAULT '' AFTER sender_id" );
        if ( $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_messages LIKE 'body'" ) ) {
            $wpdb->query( "UPDATE {$wpdb->prefix}chat_messages SET message = body WHERE message = ''" );
        }
    }

    // chat_messages: تبدیل sender_id به VARCHAR اگر هنوز BIGINT است
    $sender_col = $wpdb->get_row( "SHOW COLUMNS FROM {$wpdb->prefix}chat_messages LIKE 'sender_id'", ARRAY_A );
    if ( $sender_col && strpos( strtolower( $sender_col['Type'] ), 'bigint' ) !== false ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_messages MODIFY COLUMN sender_id VARCHAR(64) NOT NULL DEFAULT '0'" );
    }

    // chat_visitors: اضافه کردن visitor_id (اگر وجود ندارد)
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'visitor_id'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_visitors ADD COLUMN visitor_id VARCHAR(64) NOT NULL DEFAULT '' AFTER id" );
        if ( $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'session_key'" ) ) {
            $wpdb->query( "UPDATE {$wpdb->prefix}chat_visitors SET visitor_id = session_key WHERE visitor_id = ''" );
        }
        // اضافه کردن unique index
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_visitors ADD UNIQUE KEY visitor_id (visitor_id)" );
    }

    // chat_visitors: اضافه کردن ip_address (اگر وجود ندارد)
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'ip_address'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_visitors ADD COLUMN ip_address VARCHAR(45) NOT NULL DEFAULT ''" );
        if ( $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'ip'" ) ) {
            $wpdb->query( "UPDATE {$wpdb->prefix}chat_visitors SET ip_address = ip WHERE ip_address = ''" );
        }
    }

    // chat_visitors: اضافه کردن phone و user_agent (اگر وجود ندارند)
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'phone'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_visitors ADD COLUMN phone VARCHAR(20) NOT NULL DEFAULT '' AFTER name" );
    }
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}chat_visitors LIKE 'user_agent'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}chat_visitors ADD COLUMN user_agent VARCHAR(500) NOT NULL DEFAULT '' AFTER ip_address" );
    }

    // منوهای پیش‌فرض
    $default_menus = [
        ['gis',        '',     '🗺️ نقشه هوشمند',     '🗺️', 'gis',        'gis.view',              10],
        ['inventory',  '',     '🏭 انبار',            '🏭', 'inventory',  'inv.view',              20],
        ['sales',      '',     '🧾 فروش',             '🧾', 'sales',      'sales.view',            30],
        ['purchase',   '',     '🛒 خرید',             '🛒', 'purchase',   'purchase.view',         40],
        ['cpm',        '',     '📊 کنترل پروژه',      '📊', 'cpm',        'cpm.view',              50],
        ['accounting', '',     '📦 حسابداری',         '📦', 'accounting', 'acc.view',              60],
        ['treasury',   '',     '💰 خزانه',            '💰', 'treasury',   'treasury.view',         70],
        ['payroll',    '',     '👨‍💼 حقوق',           '👨‍💼', 'payroll',    'payroll.view',          80],
        ['crm',        '',     '🎯 CRM',              '🎯', 'crm',        'crm.view',              90],
        ['contracts',  '',     '📋 قراردادها',        '📋', 'contracts',  '',                      100],
        ['unitsales',  '',     '🏢 فروش واحد',        '🏢', 'unitsales',  '',                      110],
        ['permits',    '',     '📋 مجوزها',           '📋', 'permits',    '',                      120],
        ['access',      '',     '🛡️ مدیریت دسترسی‌ها', '🛡️', 'access',      'system.acl.manage',     900],
        ['users',       '',     '👥 کاربران',          '👥', 'users',       'system.users.manage',   910],
        ['workflow',    '',     '🔄 Workflow پروژه‌ها', '🔄', 'workflow',    'cpm.view',              55],
        ['wpproducts',  '',     '🛍️ محصولات فروشگاه',  '🛍️', 'wpproducts',  '',                      25],
        ['chat',        '',     '💬 چت و پشتیبانی',    '💬', 'chat',        '',                      15],
        ['service',     '',     '🔧 خدمات میدانی',     '🔧', 'service',     'service.view',          16],
    ];

    foreach ( $default_menus as $m ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_menus WHERE menu_key = %s", $m[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_menus", [
                'menu_key'            => $m[0],
                'parent_key'          => $m[1],
                'title'               => $m[2],
                'icon'                => $m[3],
                'route'               => $m[4],
                'required_permission' => $m[5],
                'sort_order'          => $m[6],
            ] );
        }
    }

    // -------------------------------------------------------
    // GIS Performance Optimization (v7) — ستون block_code + indexes
    // -------------------------------------------------------

    // (1) ستون block_code در wp_gis_parcels
    $col = $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}gis_parcels LIKE 'block_code'" );
    if ( ! $col ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels
                       ADD COLUMN block_code VARCHAR(50) DEFAULT NULL AFTER code" );
    }

    // پر کردن خودکار block_code از code (یک‌بار)
    if ( ! get_option( 'acc_gis_block_code_populated' ) ) {
        $wpdb->query( "UPDATE {$wpdb->prefix}gis_parcels
                       SET block_code = SUBSTRING_INDEX(code, '-', 3)
                       WHERE block_code IS NULL AND code IS NOT NULL AND code LIKE '%-%-%-%'" );
        update_option( 'acc_gis_block_code_populated', 1 );
    }

    // (2) Index روی block_code (یا composite block_code+status)
    $idx_exists = $wpdb->get_var( "SHOW INDEX FROM {$wpdb->prefix}gis_parcels WHERE Key_name = 'idx_block_status'" );
    if ( ! $idx_exists ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels
                       ADD INDEX idx_block_status (block_code, status)" );
    }

    // (3) Index روی status
    $idx_exists = $wpdb->get_var( "SHOW INDEX FROM {$wpdb->prefix}gis_parcels WHERE Key_name = 'idx_status_only'" );
    if ( ! $idx_exists ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels
                       ADD INDEX idx_status_only (status)" );
    }

    // (4) Index روی center_lat, center_lng در wp_gis_blocks (اگر جدول وجود دارد)
    $blocks_exists = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}gis_blocks'" );
    if ( $blocks_exists ) {
        $idx_exists = $wpdb->get_var( "SHOW INDEX FROM {$wpdb->prefix}gis_blocks WHERE Key_name = 'idx_blocks_lat_lng'" );
        if ( ! $idx_exists ) {
            $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_blocks
                           ADD INDEX idx_blocks_lat_lng (center_lat, center_lng)" );
        }

        // (5) Spatial Index — اختیاری، فقط اگر MySQL >= 5.7.5 و InnoDB
        $col = $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}gis_blocks LIKE 'location'" );
        if ( ! $col ) {
            // location POINT NOT NULL (برای spatial باید NOT NULL باشد)
            $r = $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_blocks
                                ADD COLUMN location POINT NOT NULL DEFAULT (ST_GeomFromText('POINT(0 0)')) AFTER center_lng" );
            if ( $r !== false ) {
                // پر کردن location از center_lat/center_lng
                $wpdb->query( "UPDATE {$wpdb->prefix}gis_blocks
                               SET location = ST_GeomFromText(CONCAT('POINT(', center_lng, ' ', center_lat, ')'))
                               WHERE center_lat IS NOT NULL AND center_lng IS NOT NULL" );

                // ایجاد spatial index
                $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_blocks
                               ADD SPATIAL INDEX idx_blocks_spatial (location)" );
            }
        }
    }

    // -------------------------------------------------------
    // GIS Performance v8 — composite indexes برای get_parcel
    // -------------------------------------------------------

    $idx_checks = [
        // (parcel_id, is_primary) → برای ORDER BY is_primary DESC در owners
        ['gis_parcel_owners',     'idx_parcel_primary',  '(parcel_id, is_primary)'],
        // (parcel_id, transfer_date) → برای ORDER BY transfer_date DESC در history
        ['gis_ownership_history', 'idx_parcel_date',     '(parcel_id, transfer_date)'],
        // (parcel_id, severity, status) → برای ORDER BY severity DESC در issues
        ['gis_parcel_issues',     'idx_parcel_sev_stat', '(parcel_id, severity, status)'],
        // (parcel_id, year) → برای ORDER BY year DESC در commissions
        ['gis_commissions',       'idx_parcel_year',     '(parcel_id, year)'],
        // active issue count optimization
        ['gis_parcel_issues',     'idx_severity_status', '(severity, status, parcel_id)'],
        // داشبورد املاک — covering index برای GROUP BY (neighborhood_id, district_id)
        // به‌علاوه ستون‌های تجمیع (status, bld/apt/shop_count) تا اسکن index-only شود
        ['gis_parcels', 'idx_dashboard', '(neighborhood_id, district_id, status, bld_count, apt_count, shop_count)'],
    ];

    foreach ( $idx_checks as $ck ) {
        [ $table, $idx_name, $cols ] = $ck;
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM information_schema.STATISTICS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s",
            $wpdb->prefix . $table, $idx_name
        ) );
        if ( ! $exists ) {
            $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$table} ADD INDEX {$idx_name} {$cols}" );
        }
    }

    // -------------------------------------------------------
    // GIS Data Warehouse — denormalize + جدول خلاصه‌ی بلوک
    // (داشبورد املاک: خواندن سریع به‌جای aggregation زنده)
    // -------------------------------------------------------

    // (1) ستون‌های denormalize روی gis_parcels (حذف JOIN در فیلترها)
    $parcel_cols = array_column(
        $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}gis_parcels" ), 'Field'
    );
    if ( ! in_array('area_sqm', $parcel_cols, true) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels
                       ADD COLUMN area_sqm DECIMAL(12,2) NULL DEFAULT NULL AFTER shop_count" );
    }
    if ( ! in_array('current_use', $parcel_cols, true) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels
                       ADD COLUMN current_use VARCHAR(100) NULL DEFAULT NULL AFTER area_sqm" );
    }
    // ایندکس‌های فیلترِ زنده (مسیر منعطف)
    $dw_idx = [
        ['gis_parcels', 'idx_use_area',  '(current_use, area_sqm)'],
        ['gis_parcels', 'idx_area',      '(area_sqm)'],
    ];
    foreach ( $dw_idx as $ck ) {
        [ $t, $idx_name, $cols ] = $ck;
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM information_schema.STATISTICS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s",
            $wpdb->prefix . $t, $idx_name
        ) );
        if ( ! $exists ) $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$t} ADD INDEX {$idx_name} {$cols}" );
    }

    // (2) جدول خلاصه‌ی بلوک — یک ردیف به ازای هر بلوک (~چند هزار ردیف)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_block_stats (
        block_code        VARCHAR(100)    NOT NULL,
        district_id       INT             DEFAULT NULL,
        neighborhood_id   INT             DEFAULT NULL,
        layer_id          VARCHAR(100)    DEFAULT NULL,
        total_parcels     INT             NOT NULL DEFAULT 0,
        cnt_building      INT             NOT NULL DEFAULT 0,
        cnt_apartment     INT             NOT NULL DEFAULT 0,
        cnt_shop          INT             NOT NULL DEFAULT 0,
        cnt_land_only     INT             NOT NULL DEFAULT 0,
        status_active     INT             NOT NULL DEFAULT 0,
        status_prospect   INT             NOT NULL DEFAULT 0,
        status_completed  INT             NOT NULL DEFAULT 0,
        status_suspended  INT             NOT NULL DEFAULT 0,
        center_lat        DECIMAL(10,8)   DEFAULT NULL,
        center_lng        DECIMAL(11,8)   DEFAULT NULL,
        updated_at        DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (block_code),
        KEY idx_district     (district_id),
        KEY idx_neighborhood (neighborhood_id),
        KEY idx_layer        (layer_id)
    ) {$charset};");

    // (3) زمان‌بندی cron شبانه برای بازسازی انبار
    if ( ! wp_next_scheduled( 'acc_gis_rebuild_block_stats' ) ) {
        // ۰۳:۰۰ بامداد (تقریبی — اولین اجرا فردا شب)
        wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', 'acc_gis_rebuild_block_stats' );
    }

    // -------------------------------------------------------
    // GIS Locations — جدول generic موقعیت برای همه ماژول‌ها
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}gis_locations (
        id              INT             NOT NULL AUTO_INCREMENT,
        entity_type     VARCHAR(50)     NOT NULL,
        entity_id       INT             NOT NULL,
        label           VARCHAR(200)    NOT NULL DEFAULT '',
        location_type   VARCHAR(50)     NOT NULL DEFAULT 'office',
        address         TEXT,
        lat             DECIMAL(10,8)   DEFAULT NULL,
        lng             DECIMAL(11,8)   DEFAULT NULL,
        parcel_id       INT             DEFAULT NULL,
        is_primary      TINYINT(1)      NOT NULL DEFAULT 0,
        notes           TEXT,
        created_at      DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at      DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_entity (entity_type, entity_id),
        KEY idx_primary (entity_type, is_primary),
        KEY idx_parcel (parcel_id),
        KEY idx_lat_lng (lat, lng)
    ) {$charset};");

    // -------------------------------------------------------
    // HR (نیروی انسانی)
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_departments (
        id          INT          NOT NULL AUTO_INCREMENT,
        parent_id   INT          NOT NULL DEFAULT 0,
        code        VARCHAR(50)  NOT NULL DEFAULT '',
        name        VARCHAR(200) NOT NULL,
        manager_id  INT          DEFAULT NULL,
        description TEXT,
        is_active   TINYINT(1)   NOT NULL DEFAULT 1,
        created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parent (parent_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_positions (
        id            INT          NOT NULL AUTO_INCREMENT,
        code          VARCHAR(50)  NOT NULL DEFAULT '',
        title         VARCHAR(200) NOT NULL,
        department_id INT          DEFAULT NULL,
        level         VARCHAR(50)  NOT NULL DEFAULT 'staff',
        description   TEXT,
        is_active     TINYINT(1)   NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        KEY idx_dept (department_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_employees (
        id               INT             NOT NULL AUTO_INCREMENT,
        employee_code    VARCHAR(50)     NOT NULL DEFAULT '',
        wp_user_id       BIGINT UNSIGNED DEFAULT NULL,
        first_name       VARCHAR(100)    NOT NULL DEFAULT '',
        last_name        VARCHAR(100)    NOT NULL DEFAULT '',
        national_id      VARCHAR(20)     NOT NULL DEFAULT '',
        father_name      VARCHAR(100)    NOT NULL DEFAULT '',
        birth_date       VARCHAR(20)     NOT NULL DEFAULT '',
        gender           VARCHAR(10)     NOT NULL DEFAULT '',
        marital_status   VARCHAR(20)     NOT NULL DEFAULT '',
        phone            VARCHAR(20)     NOT NULL DEFAULT '',
        mobile           VARCHAR(20)     NOT NULL DEFAULT '',
        email            VARCHAR(200)    NOT NULL DEFAULT '',
        department_id    INT             DEFAULT NULL,
        position_id      INT             DEFAULT NULL,
        manager_id       INT             DEFAULT NULL,
        hire_date        VARCHAR(20)     NOT NULL DEFAULT '',
        end_date         VARCHAR(20)     NOT NULL DEFAULT '',
        employment_type  VARCHAR(30)     NOT NULL DEFAULT 'full_time',
        base_salary      DECIMAL(15,2)   NOT NULL DEFAULT 0,
        bank_account     VARCHAR(50)     NOT NULL DEFAULT '',
        iban             VARCHAR(50)     NOT NULL DEFAULT '',
        emergency_contact VARCHAR(200)   NOT NULL DEFAULT '',
        emergency_phone  VARCHAR(20)     NOT NULL DEFAULT '',
        photo_url        VARCHAR(500)    NOT NULL DEFAULT '',
        status           VARCHAR(20)     NOT NULL DEFAULT 'active',
        notes            TEXT,
        created_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_emp_code (employee_code),
        KEY idx_dept (department_id),
        KEY idx_pos (position_id),
        KEY idx_status (status),
        KEY idx_wp_user (wp_user_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_attendance (
        id            INT             NOT NULL AUTO_INCREMENT,
        employee_id   INT             NOT NULL,
        date          DATE            NOT NULL,
        check_in      DATETIME        DEFAULT NULL,
        check_out     DATETIME        DEFAULT NULL,
        check_in_lat  DECIMAL(10,8)   DEFAULT NULL,
        check_in_lng  DECIMAL(11,8)   DEFAULT NULL,
        check_out_lat DECIMAL(10,8)   DEFAULT NULL,
        check_out_lng DECIMAL(11,8)   DEFAULT NULL,
        hours         DECIMAL(5,2)    DEFAULT NULL,
        status        VARCHAR(30)     NOT NULL DEFAULT 'present',
        notes         VARCHAR(500)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        UNIQUE KEY idx_emp_date (employee_id, date),
        KEY idx_date (date)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_leaves (
        id           INT             NOT NULL AUTO_INCREMENT,
        employee_id  INT             NOT NULL,
        leave_type   VARCHAR(30)     NOT NULL DEFAULT 'annual',
        from_date    DATE            NOT NULL,
        to_date      DATE            NOT NULL,
        days         DECIMAL(5,2)    NOT NULL DEFAULT 0,
        reason       TEXT,
        status       VARCHAR(20)     NOT NULL DEFAULT 'pending',
        approved_by  INT             DEFAULT NULL,
        approved_at  DATETIME        DEFAULT NULL,
        created_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_emp (employee_id),
        KEY idx_status (status),
        KEY idx_from_date (from_date)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}hr_assignments (
        id           INT             NOT NULL AUTO_INCREMENT,
        employee_id  INT             NOT NULL,
        project_id   INT             DEFAULT NULL,
        task_id      INT             DEFAULT NULL,
        role         VARCHAR(100)    NOT NULL DEFAULT '',
        from_date    DATE            NOT NULL,
        to_date      DATE            DEFAULT NULL,
        allocation   DECIMAL(5,2)    NOT NULL DEFAULT 100.00,
        notes        VARCHAR(500)    NOT NULL DEFAULT '',
        is_active    TINYINT(1)      NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        KEY idx_emp (employee_id),
        KEY idx_project (project_id)
    ) {$charset};");

    // -------------------------------------------------------
    // Workflow پروژه — قالب‌ها + Instanceها
    // -------------------------------------------------------

    /* قالب workflow (نوع پروژه) */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_templates (
        id          INT          NOT NULL AUTO_INCREMENT,
        code        VARCHAR(50)  NOT NULL DEFAULT '',
        name        VARCHAR(200) NOT NULL,
        description TEXT,
        icon        VARCHAR(20)  NOT NULL DEFAULT '',
        color       VARCHAR(20)  NOT NULL DEFAULT '#3b82f6',
        is_active   TINYINT(1)   NOT NULL DEFAULT 1,
        is_system   TINYINT(1)   NOT NULL DEFAULT 0,
        created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_code (code)
    ) {$charset};");

    /* مراحل قالب */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_template_stages (
        id              INT          NOT NULL AUTO_INCREMENT,
        template_id     INT          NOT NULL,
        code            VARCHAR(50)  NOT NULL DEFAULT '',
        name            VARCHAR(200) NOT NULL,
        description     TEXT,
        icon            VARCHAR(20)  NOT NULL DEFAULT '',
        color           VARCHAR(20)  NOT NULL DEFAULT '#3b82f6',
        sort_order      INT          NOT NULL DEFAULT 0,
        estimated_days  INT          NOT NULL DEFAULT 0,
        budget_pct      DECIMAL(5,2) NOT NULL DEFAULT 0,
        PRIMARY KEY (id),
        KEY idx_template (template_id, sort_order)
    ) {$charset};");

    /* tasks پیش‌فرض هر مرحله */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_template_tasks (
        id          INT          NOT NULL AUTO_INCREMENT,
        stage_id    INT          NOT NULL,
        title       VARCHAR(300) NOT NULL,
        description TEXT,
        sort_order  INT          NOT NULL DEFAULT 0,
        is_required TINYINT(1)   NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        KEY idx_stage (stage_id, sort_order)
    ) {$charset};");

    /* Gateها — شرایط ورود به مرحله بعد */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_template_gates (
        id           INT          NOT NULL AUTO_INCREMENT,
        stage_id     INT          NOT NULL,
        gate_type    VARCHAR(50)  NOT NULL DEFAULT 'task_completion',
        title        VARCHAR(300) NOT NULL,
        description  TEXT,
        is_required  TINYINT(1)   NOT NULL DEFAULT 1,
        config       TEXT,
        PRIMARY KEY (id),
        KEY idx_stage (stage_id)
    ) {$charset};");

    /* Instance — workflow اعمال شده روی یک پروژه */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_instances (
        id              INT          NOT NULL AUTO_INCREMENT,
        project_id      INT          NOT NULL,
        template_id     INT          NOT NULL,
        current_stage_id INT          DEFAULT NULL,
        status          VARCHAR(20)  NOT NULL DEFAULT 'active',
        progress_pct    DECIMAL(5,2) NOT NULL DEFAULT 0,
        started_at      DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        completed_at    DATETIME     DEFAULT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY idx_project (project_id),
        KEY idx_status (status)
    ) {$charset};");

    /* مراحل instance */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_instance_stages (
        id                  INT          NOT NULL AUTO_INCREMENT,
        instance_id         INT          NOT NULL,
        template_stage_id   INT          NOT NULL,
        code                VARCHAR(50)  NOT NULL DEFAULT '',
        name                VARCHAR(200) NOT NULL,
        icon                VARCHAR(20)  NOT NULL DEFAULT '',
        color               VARCHAR(20)  NOT NULL DEFAULT '#3b82f6',
        sort_order          INT          NOT NULL DEFAULT 0,
        status              VARCHAR(20)  NOT NULL DEFAULT 'pending',
        progress_pct        DECIMAL(5,2) NOT NULL DEFAULT 0,
        planned_start_date  DATE         DEFAULT NULL,
        planned_end_date    DATE         DEFAULT NULL,
        actual_start_date   DATE         DEFAULT NULL,
        actual_end_date     DATE         DEFAULT NULL,
        budget_amount       DECIMAL(15,2) NOT NULL DEFAULT 0,
        spent_amount        DECIMAL(15,2) NOT NULL DEFAULT 0,
        notes               TEXT,
        PRIMARY KEY (id),
        KEY idx_instance (instance_id, sort_order),
        KEY idx_status (status)
    ) {$charset};");

    /* tasks instance */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_instance_tasks (
        id                  INT          NOT NULL AUTO_INCREMENT,
        instance_stage_id   INT          NOT NULL,
        title               VARCHAR(300) NOT NULL,
        description         TEXT,
        sort_order          INT          NOT NULL DEFAULT 0,
        is_required         TINYINT(1)   NOT NULL DEFAULT 1,
        status              VARCHAR(20)  NOT NULL DEFAULT 'pending',
        assigned_to         INT          DEFAULT NULL,
        assigned_name       VARCHAR(200) NOT NULL DEFAULT '',
        due_date            DATE         DEFAULT NULL,
        completed_at        DATETIME     DEFAULT NULL,
        completed_by        INT          DEFAULT NULL,
        completed_by_name   VARCHAR(200) NOT NULL DEFAULT '',
        notes               TEXT,
        PRIMARY KEY (id),
        KEY idx_stage (instance_stage_id, sort_order),
        KEY idx_status (status)
    ) {$charset};");

    /* تاریخچه transitions */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wf_transitions (
        id            INT          NOT NULL AUTO_INCREMENT,
        instance_id   INT          NOT NULL,
        from_stage_id INT          DEFAULT NULL,
        to_stage_id   INT          DEFAULT NULL,
        action        VARCHAR(50)  NOT NULL,
        notes         TEXT,
        actor_id      INT          DEFAULT NULL,
        actor_name    VARCHAR(200) NOT NULL DEFAULT '',
        created_at    DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_instance (instance_id, created_at)
    ) {$charset};");

    // ── Seed قالب‌های پیش‌فرض ──
    ACC_WF_Seeder::run();

    // -------------------------------------------------------
    // Shipping (حمل و نقل) — مثل دیجی‌کالا
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_methods (
        id           INT          NOT NULL AUTO_INCREMENT,
        code         VARCHAR(50)  NOT NULL DEFAULT '',
        name         VARCHAR(200) NOT NULL,
        icon         VARCHAR(20)  NOT NULL DEFAULT '🚚',
        avg_days     INT          NOT NULL DEFAULT 1,
        base_cost    DECIMAL(15,2) NOT NULL DEFAULT 0,
        description  TEXT,
        is_active    TINYINT(1)   NOT NULL DEFAULT 1,
        PRIMARY KEY (id),
        UNIQUE KEY idx_code (code)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_carriers (
        id           INT          NOT NULL AUTO_INCREMENT,
        code         VARCHAR(50)  NOT NULL DEFAULT '',
        name         VARCHAR(200) NOT NULL,
        contact_name VARCHAR(200) NOT NULL DEFAULT '',
        phone        VARCHAR(20)  NOT NULL DEFAULT '',
        email        VARCHAR(200) NOT NULL DEFAULT '',
        address      TEXT,
        rating       DECIMAL(3,2) NOT NULL DEFAULT 0,
        is_active    TINYINT(1)   NOT NULL DEFAULT 1,
        created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_shipments (
        id                    INT          NOT NULL AUTO_INCREMENT,
        shipment_no           VARCHAR(50)  NOT NULL DEFAULT '',
        direction             VARCHAR(20)  NOT NULL DEFAULT 'inbound',
        status                VARCHAR(30)  NOT NULL DEFAULT 'pending',

        method_id             INT          DEFAULT NULL,
        carrier_id            INT          DEFAULT NULL,
        carrier_name          VARCHAR(200) NOT NULL DEFAULT '',

        ref_type              VARCHAR(50)  NOT NULL DEFAULT '',
        ref_id                INT          DEFAULT NULL,
        ref_no                VARCHAR(100) NOT NULL DEFAULT '',

        warehouse_id          INT          DEFAULT NULL,
        from_warehouse_id     INT          DEFAULT NULL,

        origin_address        TEXT,
        origin_lat            DECIMAL(10,8) DEFAULT NULL,
        origin_lng            DECIMAL(11,8) DEFAULT NULL,
        destination_address   TEXT,
        destination_lat       DECIMAL(10,8) DEFAULT NULL,
        destination_lng       DECIMAL(11,8) DEFAULT NULL,

        recipient_name        VARCHAR(200) NOT NULL DEFAULT '',
        recipient_phone       VARCHAR(20)  NOT NULL DEFAULT '',

        driver_name           VARCHAR(200) NOT NULL DEFAULT '',
        driver_phone          VARCHAR(20)  NOT NULL DEFAULT '',
        vehicle_plate         VARCHAR(50)  NOT NULL DEFAULT '',
        tracking_code         VARCHAR(100) NOT NULL DEFAULT '',

        planned_pickup_date   DATETIME     DEFAULT NULL,
        planned_delivery_date DATETIME     DEFAULT NULL,
        actual_pickup_date    DATETIME     DEFAULT NULL,
        actual_delivery_date  DATETIME     DEFAULT NULL,

        shipping_cost         DECIMAL(15,2) NOT NULL DEFAULT 0,
        total_weight_kg       DECIMAL(10,3) NOT NULL DEFAULT 0,
        total_packages        INT          NOT NULL DEFAULT 0,

        notes                 TEXT,
        created_at            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at            DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

        PRIMARY KEY (id),
        UNIQUE KEY idx_shipment_no (shipment_no),
        KEY idx_status (status),
        KEY idx_direction (direction),
        KEY idx_ref (ref_type, ref_id),
        KEY idx_warehouse (warehouse_id),
        KEY idx_planned (planned_delivery_date)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_items (
        id                INT             NOT NULL AUTO_INCREMENT,
        shipment_id       INT             NOT NULL,
        ref_item_id       INT             DEFAULT NULL,
        product_id        INT             DEFAULT NULL,
        product_name      VARCHAR(300)    NOT NULL DEFAULT '',
        qty_planned       DECIMAL(14,4)   NOT NULL DEFAULT 0,
        qty_shipped       DECIMAL(14,4)   NOT NULL DEFAULT 0,
        qty_received      DECIMAL(14,4)   NOT NULL DEFAULT 0,
        qty_damaged       DECIMAL(14,4)   NOT NULL DEFAULT 0,
        qty_returned      DECIMAL(14,4)   NOT NULL DEFAULT 0,
        unit              VARCHAR(30)     NOT NULL DEFAULT '',
        unit_weight_kg    DECIMAL(10,3)   NOT NULL DEFAULT 0,
        package_count     INT             NOT NULL DEFAULT 0,
        notes             VARCHAR(500)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        KEY idx_shipment (shipment_id),
        KEY idx_product (product_id),
        KEY idx_ref_item (ref_item_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_stages (
        id            INT             NOT NULL AUTO_INCREMENT,
        shipment_id   INT             NOT NULL,
        stage         VARCHAR(50)     NOT NULL,
        title         VARCHAR(300)    NOT NULL DEFAULT '',
        notes         TEXT,
        lat           DECIMAL(10,8)   DEFAULT NULL,
        lng           DECIMAL(11,8)   DEFAULT NULL,
        actor_id      INT             DEFAULT NULL,
        actor_name    VARCHAR(200)    NOT NULL DEFAULT '',
        photo_url     VARCHAR(500)    NOT NULL DEFAULT '',
        created_at    DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_shipment (shipment_id, created_at)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}ship_inspections (
        id              INT          NOT NULL AUTO_INCREMENT,
        shipment_id     INT          NOT NULL,
        inspector_id    INT          DEFAULT NULL,
        inspector_name  VARCHAR(200) NOT NULL DEFAULT '',
        inspection_date DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        quality_score   INT          NOT NULL DEFAULT 0,
        packaging_ok    TINYINT(1)   NOT NULL DEFAULT 1,
        quantity_ok     TINYINT(1)   NOT NULL DEFAULT 1,
        specs_ok        TINYINT(1)   NOT NULL DEFAULT 1,
        damage_report   TEXT,
        result          VARCHAR(20)  NOT NULL DEFAULT 'accepted',
        photos          TEXT,
        notes           TEXT,
        PRIMARY KEY (id),
        KEY idx_shipment (shipment_id)
    ) {$charset};");

    // ── Seed داده‌های پیش‌فرض روش‌های حمل ──
    $methods = [
        ['truck',    '🚛 کامیون',       '🚛', 3, 500000],
        ['van',      '🚐 وانت',         '🚐', 1, 200000],
        ['pickup',   '🛻 نیسان',        '🛻', 1, 150000],
        ['courier',  '🛵 پیک موتوری',   '🛵', 1, 50000],
        ['post',     '📬 پست',           '📬', 5, 30000],
        ['express',  '✈️ پست پیشتاز',    '✈️', 2, 80000],
        ['ship',     '🚢 کشتی',         '🚢', 30, 2000000],
        ['plane',    '✈️ هواپیما',      '✈️', 2, 1500000],
        ['rail',     '🚂 قطار',          '🚂', 5, 800000],
        ['internal', '🏭 ناوگان داخلی',  '🏭', 1, 100000],
    ];
    foreach ( $methods as $m ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}ship_methods WHERE code = %s", $m[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}ship_methods", [
                'code' => $m[0], 'name' => $m[1], 'icon' => $m[2],
                'avg_days' => $m[3], 'base_cost' => $m[4],
            ] );
        }
    }

    // -------------------------------------------------------
    // Assets (مدیریت اموال) + Maintenance (نگهداری)
    // -------------------------------------------------------

    // دسته‌بندی درختی
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_categories (
        id           INT          NOT NULL AUTO_INCREMENT,
        parent_id    INT          NOT NULL DEFAULT 0,
        code         VARCHAR(50)  NOT NULL DEFAULT '',
        name         VARCHAR(200) NOT NULL,
        icon         VARCHAR(20)  NOT NULL DEFAULT '📦',
        depr_method  VARCHAR(20)  NOT NULL DEFAULT 'straight_line',
        useful_years INT          NOT NULL DEFAULT 5,
        salvage_pct  DECIMAL(5,2) NOT NULL DEFAULT 0,
        is_active    TINYINT(1)   NOT NULL DEFAULT 1,
        created_at   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_parent (parent_id)
    ) {$charset};");

    // اموال
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}assets (
        id                  INT             NOT NULL AUTO_INCREMENT,
        asset_code          VARCHAR(50)     NOT NULL DEFAULT '',
        name                VARCHAR(300)    NOT NULL,
        category_id         INT             DEFAULT NULL,
        serial_no           VARCHAR(200)    NOT NULL DEFAULT '',
        plate_no            VARCHAR(50)     NOT NULL DEFAULT '',
        brand               VARCHAR(100)    NOT NULL DEFAULT '',
        model               VARCHAR(100)    NOT NULL DEFAULT '',
        description         TEXT,

        status              VARCHAR(20)     NOT NULL DEFAULT 'active',
        condition_grade     VARCHAR(20)     NOT NULL DEFAULT 'good',

        purchase_date       DATE            DEFAULT NULL,
        purchase_price      DECIMAL(15,2)   NOT NULL DEFAULT 0,
        current_value       DECIMAL(15,2)   NOT NULL DEFAULT 0,
        supplier_id         INT             DEFAULT NULL,
        purchase_invoice_id INT             DEFAULT NULL,

        warranty_expiry     DATE            DEFAULT NULL,
        insurance_expiry    DATE            DEFAULT NULL,
        next_service_date   DATE            DEFAULT NULL,

        assigned_to         INT             DEFAULT NULL,
        assigned_name       VARCHAR(200)    NOT NULL DEFAULT '',
        location_id         INT             DEFAULT NULL,
        warehouse_id        INT             DEFAULT NULL,
        project_id          INT             DEFAULT NULL,

        photo_url           VARCHAR(500)    NOT NULL DEFAULT '',
        qr_code             VARCHAR(200)    NOT NULL DEFAULT '',
        notes               TEXT,
        created_at          DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at          DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_code (asset_code),
        KEY idx_category (category_id),
        KEY idx_status (status),
        KEY idx_assigned (assigned_to),
        KEY idx_next_service (next_service_date)
    ) {$charset};");

    // اسناد (مدارک)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_documents (
        id         INT          NOT NULL AUTO_INCREMENT,
        asset_id   INT          NOT NULL,
        doc_type   VARCHAR(50)  NOT NULL DEFAULT 'other',
        title      VARCHAR(300) NOT NULL,
        file_url   VARCHAR(500) NOT NULL DEFAULT '',
        notes      TEXT,
        uploaded_at DATETIME    NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_asset (asset_id)
    ) {$charset};");

    // برنامه نگهداری پیشگیرانه (preventive maintenance schedules)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_pm_schedules (
        id                INT          NOT NULL AUTO_INCREMENT,
        asset_id          INT          NOT NULL,
        title             VARCHAR(300) NOT NULL,
        description       TEXT,
        interval_days     INT          NOT NULL DEFAULT 90,
        last_done_date    DATE         DEFAULT NULL,
        next_due_date     DATE         DEFAULT NULL,
        estimated_cost    DECIMAL(15,2) NOT NULL DEFAULT 0,
        assigned_to       INT          DEFAULT NULL,
        priority          VARCHAR(20)  NOT NULL DEFAULT 'medium',
        is_active         TINYINT(1)   NOT NULL DEFAULT 1,
        created_at        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_asset (asset_id),
        KEY idx_due (next_due_date, is_active)
    ) {$charset};");

    // دستور کار (Work Orders)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_work_orders (
        id                INT             NOT NULL AUTO_INCREMENT,
        wo_no             VARCHAR(50)     NOT NULL DEFAULT '',
        asset_id          INT             NOT NULL,
        type              VARCHAR(20)     NOT NULL DEFAULT 'corrective',
        priority          VARCHAR(20)     NOT NULL DEFAULT 'medium',
        status            VARCHAR(20)     NOT NULL DEFAULT 'open',

        title             VARCHAR(300)    NOT NULL,
        problem_desc      TEXT,
        solution_desc     TEXT,

        reporter_id       INT             DEFAULT NULL,
        reporter_name     VARCHAR(200)    NOT NULL DEFAULT '',
        assigned_to       INT             DEFAULT NULL,
        assigned_name     VARCHAR(200)    NOT NULL DEFAULT '',
        external_vendor   VARCHAR(300)    NOT NULL DEFAULT '',

        pm_schedule_id    INT             DEFAULT NULL,
        reported_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        started_at        DATETIME        DEFAULT NULL,
        completed_at      DATETIME        DEFAULT NULL,
        due_date          DATE            DEFAULT NULL,

        labor_cost        DECIMAL(15,2)   NOT NULL DEFAULT 0,
        parts_cost        DECIMAL(15,2)   NOT NULL DEFAULT 0,
        total_cost        DECIMAL(15,2)   NOT NULL DEFAULT 0,
        downtime_hours    DECIMAL(8,2)    NOT NULL DEFAULT 0,

        notes             TEXT,
        PRIMARY KEY (id),
        UNIQUE KEY idx_wo_no (wo_no),
        KEY idx_asset (asset_id),
        KEY idx_status (status),
        KEY idx_assigned (assigned_to),
        KEY idx_type (type)
    ) {$charset};");

    // ردیف‌های دستور کار (قطعات + کار)
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_wo_items (
        id          INT             NOT NULL AUTO_INCREMENT,
        wo_id       INT             NOT NULL,
        item_type   VARCHAR(20)     NOT NULL DEFAULT 'part',
        product_id  INT             DEFAULT NULL,
        description VARCHAR(300)    NOT NULL DEFAULT '',
        qty         DECIMAL(10,2)   NOT NULL DEFAULT 1,
        unit_cost   DECIMAL(15,2)   NOT NULL DEFAULT 0,
        total_cost  DECIMAL(15,2)   NOT NULL DEFAULT 0,
        notes       VARCHAR(500)    NOT NULL DEFAULT '',
        PRIMARY KEY (id),
        KEY idx_wo (wo_id)
    ) {$charset};");

    // استهلاک
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}asset_depreciations (
        id              INT             NOT NULL AUTO_INCREMENT,
        asset_id        INT             NOT NULL,
        period_year     INT             NOT NULL,
        period_month    INT             NOT NULL DEFAULT 0,
        depr_amount     DECIMAL(15,2)   NOT NULL DEFAULT 0,
        book_value_before DECIMAL(15,2) NOT NULL DEFAULT 0,
        book_value_after  DECIMAL(15,2) NOT NULL DEFAULT 0,
        calculated_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_asset_period (asset_id, period_year, period_month),
        KEY idx_period (period_year, period_month)
    ) {$charset};");

    // Seed دسته‌بندی پیش‌فرض
    $default_cats = [
        ['BLD',  '🏢 ساختمان و املاک',     '🏢', 30, 10],
        ['VEH',  '🚗 خودرو و ناوگان',      '🚗', 7,  20],
        ['OFC',  '🪑 تجهیزات اداری',       '🪑', 5,  5],
        ['IT',   '💻 تجهیزات IT',           '💻', 4,  10],
        ['IND',  '⚙️ تجهیزات صنعتی',       '⚙️', 10, 10],
        ['TOOL', '🔧 ابزارآلات',            '🔧', 3,  0],
        ['FUR',  '🛋️ مبلمان',               '🛋️', 7,  5],
        ['MED',  '🏥 تجهیزات پزشکی',       '🏥', 8,  10],
    ];
    foreach ( $default_cats as $c ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}asset_categories WHERE code=%s", $c[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert("{$wpdb->prefix}asset_categories", [
                'code' => $c[0], 'name' => $c[1], 'icon' => $c[2],
                'useful_years' => $c[3], 'salvage_pct' => $c[4],
            ]);
        }
    }

    // -------------------------------------------------------
    // GIS — LayerID به wp_gis_parcels
    // -------------------------------------------------------
    $col = $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}gis_parcels LIKE 'LayerID'" );
    if ( ! $col ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels ADD COLUMN LayerID VARCHAR(100) DEFAULT NULL AFTER block_code" );
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels ADD INDEX idx_layer (LayerID)" );
    } else {
        // اگر index هنوز نیست
        $idx = $wpdb->get_var( "SHOW INDEX FROM {$wpdb->prefix}gis_parcels WHERE Key_name = 'idx_layer'" );
        if ( ! $idx ) $wpdb->query( "ALTER TABLE {$wpdb->prefix}gis_parcels ADD INDEX idx_layer (LayerID)" );
    }

    // -------------------------------------------------------
    // Commission ماده ۱۰۰ — تغییر type ستون‌های کلیدی + index
    // (چون CSV ها همه ستون‌ها را LONGTEXT کرده‌اند، روی longtext نمی‌توان index ساخت)
    // -------------------------------------------------------

    /**
     * توضیح: نام‌گذاری جداول گمراه‌کننده است
     *   wp_gis_trepass    → در واقع پرونده‌ها (با NidCommission UUID)
     *   wp_gis_commission → خلاصه رأی‌ها (با UrbanNidKartablItem)
     */
    $alters = [
        'gis_trepass'         => ['NidCommission VARCHAR(50)', 'CodeString VARCHAR(50)'],
        'gis_commission'      => ['UrbanNidKartablItem VARCHAR(50)', 'CodeString VARCHAR(50)'],
        'gis_vote'            => ['NidVote VARCHAR(50)', 'NidCommission VARCHAR(50)'],
        'gis_vote_vs_trepass' => ['NidVote VARCHAR(50)', 'NidTrepass VARCHAR(50)'],
    ];

    foreach ( $alters as $tbl => $cols ) {
        $exists_tbl = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}{$tbl}'" );
        if ( ! $exists_tbl ) continue;
        foreach ( $cols as $col_def ) {
            [$col_name] = explode( ' ', $col_def );
            $col_info = $wpdb->get_row( "SHOW COLUMNS FROM {$wpdb->prefix}{$tbl} LIKE '{$col_name}'", ARRAY_A );
            if ( $col_info && stripos( $col_info['Type'], 'longtext' ) !== false ) {
                $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$tbl} MODIFY COLUMN {$col_def}" );
            }
        }
    }

    // حالا index ها
    $com_indexes = [
        'gis_commission'        => [['idx_codestring',  '(CodeString)'], ['idx_urban', '(UrbanNidKartablItem)']],
        'gis_trepass'           => [['idx_commission',  '(NidCommission)'], ['idx_codestring', '(CodeString)']],
        'gis_vote'              => [['idx_commission',  '(NidCommission)'], ['idx_vote_nid', '(NidVote)']],
        'gis_vote_vs_trepass'   => [['idx_vote',        '(NidVote)'], ['idx_trepass', '(NidTrepass)']],
    ];

    foreach ( $com_indexes as $tbl => $idxs ) {
        $exists_tbl = $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}{$tbl}'" );
        if ( ! $exists_tbl ) continue;
        foreach ( $idxs as $idx ) {
            [$idx_name, $cols] = $idx;
            $exists = $wpdb->get_var( $wpdb->prepare(
                "SELECT COUNT(*) FROM information_schema.STATISTICS
                 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s",
                $wpdb->prefix . $tbl, $idx_name
            ) );
            if ( ! $exists ) {
                // اگر هنوز longtext است → اضافه کردن index با طول 50
                $col_clean = trim( $cols, '()' );
                $col_info = $wpdb->get_row( "SHOW COLUMNS FROM {$wpdb->prefix}{$tbl} LIKE '{$col_clean}'", ARRAY_A );
                if ( $col_info && stripos( $col_info['Type'], 'longtext' ) !== false ) {
                    $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$tbl} ADD INDEX {$idx_name} ({$col_clean}(50))" );
                } else {
                    $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$tbl} ADD INDEX {$idx_name} {$cols}" );
                }
            }
        }
    }

    // -------------------------------------------------------
    // Field Service — خدمات میدانی (سردخانه / چیلر)
    // -------------------------------------------------------

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_devices (
        id               BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        site_label       VARCHAR(200) NOT NULL DEFAULT '',
        customer_name    VARCHAR(200) NOT NULL DEFAULT '',
        phone            VARCHAR(20)  NOT NULL DEFAULT '',
        device_type      VARCHAR(100) NOT NULL DEFAULT '',
        brand            VARCHAR(100) NOT NULL DEFAULT '',
        model            VARCHAR(100) NOT NULL DEFAULT '',
        serial_no        VARCHAR(100) NOT NULL DEFAULT '',
        capacity         VARCHAR(50)  NOT NULL DEFAULT '',
        refrigerant_type VARCHAR(50)  NOT NULL DEFAULT '',
        install_location VARCHAR(200) NOT NULL DEFAULT '',
        operating_hours  INT UNSIGNED NOT NULL DEFAULT 0,
        last_service_date DATE NULL,
        notes            TEXT,
        status           VARCHAR(30)  NOT NULL DEFAULT 'active',
        created_at       DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at       DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY serial_no (serial_no),
        KEY site_label (site_label)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_requests (
        id                     BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        request_no             VARCHAR(30)  NOT NULL DEFAULT '',
        tracking_code          VARCHAR(6)   NOT NULL DEFAULT '',
        conversation_id        BIGINT UNSIGNED NULL,
        session_key            VARCHAR(64)  NOT NULL DEFAULT '',
        service_type           VARCHAR(30)  NOT NULL DEFAULT 'repair',
        status                 VARCHAR(30)  NOT NULL DEFAULT 'submitted',
        customer_name          VARCHAR(200) NOT NULL DEFAULT '',
        company                VARCHAR(200) NOT NULL DEFAULT '',
        phone                  VARCHAR(20)  NOT NULL DEFAULT '',
        email                  VARCHAR(200) NOT NULL DEFAULT '',
        address                TEXT,
        site_label             VARCHAR(200) NOT NULL DEFAULT '',
        device_id              BIGINT UNSIGNED NULL,
        device_type            VARCHAR(100) NOT NULL DEFAULT '',
        brand                  VARCHAR(100) NOT NULL DEFAULT '',
        model                  VARCHAR(100) NOT NULL DEFAULT '',
        serial_no              VARCHAR(100) NOT NULL DEFAULT '',
        capacity               VARCHAR(50)  NOT NULL DEFAULT '',
        refrigerant            VARCHAR(50)  NOT NULL DEFAULT '',
        install_location       VARCHAR(200) NOT NULL DEFAULT '',
        complaint_text         TEXT,
        complaint_tags         JSON,
        assigned_technician_id BIGINT UNSIGNED NULL,
        created_at             DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at             DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY tracking_code (tracking_code),
        UNIQUE KEY request_no (request_no),
        KEY session_key (session_key),
        KEY phone (phone),
        KEY status (status),
        KEY device_id (device_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_reports (
        id                  BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        report_no           VARCHAR(30)  NOT NULL DEFAULT '',
        request_id          BIGINT UNSIGNED NULL,
        device_id           BIGINT UNSIGNED NULL,
        service_type        VARCHAR(30)  NOT NULL DEFAULT 'repair',
        status              ENUM('draft','final') NOT NULL DEFAULT 'draft',
        report_date         DATE NULL,
        customer_name       VARCHAR(200) NOT NULL DEFAULT '',
        company             VARCHAR(200) NOT NULL DEFAULT '',
        phone               VARCHAR(20)  NOT NULL DEFAULT '',
        email               VARCHAR(200) NOT NULL DEFAULT '',
        address             TEXT,
        site_label          VARCHAR(200) NOT NULL DEFAULT '',
        case_no             VARCHAR(30)  NOT NULL DEFAULT '',
        complaint_text      TEXT,
        complaint_tags      JSON,
        device_snapshot     JSON,
        technical_readings  JSON,
        diagnosis_result    TEXT,
        service_lines       JSON,
        part_lines          JSON,
        material_lines      JSON,
        extra_costs         JSON,
        recommendations     JSON,
        pm_offer            JSON,
        subtotal_services   DECIMAL(15,2) NOT NULL DEFAULT 0,
        subtotal_parts      DECIMAL(15,2) NOT NULL DEFAULT 0,
        subtotal_materials  DECIMAL(15,2) NOT NULL DEFAULT 0,
        subtotal_extra      DECIMAL(15,2) NOT NULL DEFAULT 0,
        discount            DECIMAL(15,2) NOT NULL DEFAULT 0,
        tax                 DECIMAL(15,2) NOT NULL DEFAULT 0,
        total               DECIMAL(15,2) NOT NULL DEFAULT 0,
        paid                DECIMAL(15,2) NOT NULL DEFAULT 0,
        balance             DECIMAL(15,2) NOT NULL DEFAULT 0,
        tech_signature      TEXT,
        customer_signature  TEXT,
        public_token        VARCHAR(64) NOT NULL DEFAULT '',
        created_by          BIGINT UNSIGNED NOT NULL DEFAULT 0,
        finalized_at        DATETIME NULL,
        created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY report_no (report_no),
        UNIQUE KEY public_token (public_token),
        KEY request_id (request_id),
        KEY device_id (device_id),
        KEY status (status)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_pm_contracts (
        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        report_id       BIGINT UNSIGNED NULL,
        device_id       BIGINT UNSIGNED NULL,
        customer_name   VARCHAR(200) NOT NULL DEFAULT '',
        phone           VARCHAR(20)  NOT NULL DEFAULT '',
        site_label      VARCHAR(200) NOT NULL DEFAULT '',
        plan            VARCHAR(30)  NOT NULL DEFAULT 'monthly',
        status          VARCHAR(20)  NOT NULL DEFAULT 'active',
        next_visit_date DATE NULL,
        created_at      DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY next_visit (next_visit_date),
        KEY status (status)
    ) {$charset};");

    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}svc_requests LIKE 'scheduled_at'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}svc_requests ADD COLUMN scheduled_at DATETIME NULL AFTER assigned_technician_id" );
    }
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}svc_requests LIKE 'dispatch_notes'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}svc_requests ADD COLUMN dispatch_notes TEXT NULL AFTER scheduled_at" );
    }

    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}svc_devices LIKE 'estimated_value'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}svc_devices ADD COLUMN estimated_value DECIMAL(15,2) NOT NULL DEFAULT 0 AFTER status" );
    }
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}svc_pm_contracts LIKE 'last_request_id'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}svc_pm_contracts ADD COLUMN last_request_id BIGINT UNSIGNED NULL AFTER next_visit_date" );
    }
    if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}svc_pm_contracts LIKE 'last_reminder_at'" ) ) {
        $wpdb->query( "ALTER TABLE {$wpdb->prefix}svc_pm_contracts ADD COLUMN last_reminder_at DATETIME NULL AFTER last_request_id" );
    }

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_payments (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        report_id   BIGINT UNSIGNED NOT NULL,
        amount      DECIMAL(15,2) NOT NULL DEFAULT 0,
        authority   VARCHAR(64)  NOT NULL DEFAULT '',
        ref_id      VARCHAR(64)  NOT NULL DEFAULT '',
        status      VARCHAR(20)  NOT NULL DEFAULT 'pending',
        created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        paid_at     DATETIME NULL,
        PRIMARY KEY (id),
        KEY report_id (report_id),
        KEY authority (authority),
        KEY status (status)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_technician_teams (
        id                  BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        senior_user_id      BIGINT UNSIGNED NOT NULL,
        technician_user_id  BIGINT UNSIGNED NOT NULL,
        status              VARCHAR(20) NOT NULL DEFAULT 'active',
        created_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at          DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY senior_tech (senior_user_id, technician_user_id),
        KEY senior_user_id (senior_user_id),
        KEY technician_user_id (technician_user_id),
        KEY status (status)
    ) {$charset};");

    /* ── فروشگاه عرقیات ── */
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}arag_families (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        name        VARCHAR(120)    NOT NULL,
        slug        VARCHAR(120)    NOT NULL,
        icon        VARCHAR(20)     NOT NULL DEFAULT '🌿',
        image_url   VARCHAR(500)    NULL,
        sort_order  INT             NOT NULL DEFAULT 0,
        is_active   TINYINT(1)      NOT NULL DEFAULT 1,
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY slug (slug),
        KEY is_active (is_active)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}arag_variants (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        family_id   BIGINT UNSIGNED NOT NULL,
        name        VARCHAR(120)    NOT NULL,
        slug        VARCHAR(120)    NOT NULL,
        description TEXT            NULL,
        sort_order  INT             NOT NULL DEFAULT 0,
        is_active   TINYINT(1)      NOT NULL DEFAULT 1,
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY family_id (family_id),
        UNIQUE KEY family_slug (family_id, slug)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}arag_packages (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        variant_id  BIGINT UNSIGNED NOT NULL,
        label       VARCHAR(120)    NOT NULL,
        volume      VARCHAR(30)     NOT NULL DEFAULT '',
        price       DECIMAL(12,0)   NOT NULL DEFAULT 0,
        sort_order  INT             NOT NULL DEFAULT 0,
        is_active   TINYINT(1)      NOT NULL DEFAULT 1,
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY variant_id (variant_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}shop_otp (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        phone       VARCHAR(15)     NOT NULL,
        code        VARCHAR(6)      NOT NULL,
        expires_at  DATETIME        NOT NULL,
        verified    TINYINT(1)      NOT NULL DEFAULT 0,
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY phone (phone),
        KEY expires_at (expires_at)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}arag_orders (
        id               BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        order_key        VARCHAR(60)     NOT NULL,
        order_number     VARCHAR(30)     NOT NULL,
        user_id          BIGINT UNSIGNED NOT NULL DEFAULT 0,
        items_json       LONGTEXT        NOT NULL,
        subtotal         DECIMAL(14,0)   NOT NULL DEFAULT 0,
        shipping_method  VARCHAR(20)     NOT NULL DEFAULT 'post',
        shipping_cod     TINYINT(1)      NOT NULL DEFAULT 1,
        payment_method   VARCHAR(30)     NOT NULL DEFAULT 'zarinpal',
        payment_status   VARCHAR(20)     NOT NULL DEFAULT 'pending',
        order_status     VARCHAR(20)     NOT NULL DEFAULT 'pending',
        authority        VARCHAR(80)     NULL,
        ref_id           VARCHAR(80)     NULL,
        billing_name     VARCHAR(120)    NULL,
        billing_phone    VARCHAR(20)     NULL,
        billing_address  VARCHAR(500)    NULL,
        billing_city     VARCHAR(80)     NULL,
        customer_note    TEXT            NULL,
        created_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY order_key (order_key),
        KEY user_id (user_id),
        KEY order_status (order_status),
        KEY payment_status (payment_status)
    ) {$charset};");

    if ( function_exists( 'wp_next_scheduled' ) && function_exists( 'wp_schedule_event' ) ) {
        if ( ! wp_next_scheduled( 'acc_svc_pm_daily' ) ) {
            wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', 'acc_svc_pm_daily' );
        }
    }

    // -------------------------------------------------------
    // GIS Performance v17.5 — ایندکس‌های محلات و شمارش پارسل
    // -------------------------------------------------------
    $gis_v175_idx = [
        ['gis_neighborhoods', 'idx_district_name',   '(district_id, name)'],
        ['gis_neighborhoods', 'idx_name',            '(name)'],
        ['gis_parcels',       'idx_neighborhood_status', '(neighborhood_id, status)'],
        ['gis_parcels',       'idx_district_status',     '(district_id, status)'],
        ['gis_districts',     'idx_city_name',       '(city, name)'],
        ['gis_districts',     'idx_name',            '(name)'],
    ];
    foreach ( $gis_v175_idx as $ck ) {
        [ $table, $idx_name, $cols ] = $ck;
        $tbl = $wpdb->prefix . $table;
        $exists_tbl = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $tbl ) );
        if ( ! $exists_tbl ) continue;
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT COUNT(*) FROM information_schema.STATISTICS
             WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s",
            $tbl, $idx_name
        ) );
        if ( ! $exists ) {
            $wpdb->query( "ALTER TABLE {$tbl} ADD INDEX {$idx_name} {$cols}" );
        }
    }

    update_option('acc_engine_db_version', '17.5');

    // -------------------------------------------------------
    // اقتصاد ملک v17.6 — پورتال همکاری بنگاه
    // -------------------------------------------------------
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}melk_org_assignments (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        unit_type   VARCHAR(20)     NOT NULL DEFAULT 'district',
        unit_id     INT             NOT NULL,
        user_id     BIGINT UNSIGNED NOT NULL,
        role_slug   VARCHAR(50)     NOT NULL DEFAULT 'district_lead',
        user_name   VARCHAR(120)    NULL,
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_unit_role (unit_type, unit_id, role_slug),
        KEY idx_user (user_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}melk_cases (
        id               BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        case_key         VARCHAR(40)     NOT NULL,
        parcel_id        INT             DEFAULT NULL,
        title            VARCHAR(200)    NOT NULL,
        status           VARCHAR(30)     NOT NULL DEFAULT 'open',
        district_id      INT             DEFAULT NULL,
        neighborhood_id  INT             DEFAULT NULL,
        block_code       VARCHAR(40)     NULL,
        created_by       BIGINT UNSIGNED NOT NULL DEFAULT 0,
        created_by_name  VARCHAR(120)    NULL,
        assigned_to      BIGINT UNSIGNED NOT NULL DEFAULT 0,
        assigned_name    VARCHAR(120)    NULL,
        summary          TEXT            NULL,
        created_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at       DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY case_key (case_key),
        KEY idx_parcel (parcel_id),
        KEY idx_district (district_id),
        KEY idx_neighborhood (neighborhood_id),
        KEY idx_status (status)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}melk_case_participants (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        case_id     BIGINT UNSIGNED NOT NULL,
        user_id     BIGINT UNSIGNED NOT NULL,
        user_name   VARCHAR(120)    NULL,
        role_type   VARCHAR(40)     NOT NULL DEFAULT 'agent',
        joined_at   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY idx_case_user (case_id, user_id),
        KEY idx_user (user_id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}melk_case_notes (
        id          BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        case_id     BIGINT UNSIGNED NOT NULL,
        user_id     BIGINT UNSIGNED NOT NULL DEFAULT 0,
        user_name   VARCHAR(120)    NULL,
        body        TEXT            NOT NULL,
        note_type   VARCHAR(30)     NOT NULL DEFAULT 'note',
        created_at  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY idx_case (case_id)
    ) {$charset};");

    // نقش‌ها و مجوزهای v17.6 (idempotent)
    $melk_roles = [
        ['district_lead',      'سرگروه منطقه',       'مدیریت منطقه و هماهنگی بنگاه',   12, 1],
        ['neighborhood_manager','مدیر محله',        'مدیریت محله و بلوک‌های ملک',      13, 1],
        ['melk_specialist',    'کارشناس مشترک ملک',  'حقوقی، شهرداری، ثبت و ...',       14, 1],
        ['melk_coordinator',   'هماهنگ‌کننده ملک',   'نظارت سراسری شبکه بنگاه',        15, 1],
    ];
    foreach ( $melk_roles as $r ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = %s", $r[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_roles", [
                'slug' => $r[0], 'name' => $r[1], 'description' => $r[2],
                'sort_order' => $r[3], 'is_system' => $r[4],
            ] );
        }
    }

    $melk_perms = [
        ['melk.view',              'مشاهده پورتال اقتصاد ملک',     'melk', 'portal',  'view'],
        ['melk.manage',            'مدیریت سازمان بنگاه',          'melk', 'org',     'manage'],
        ['melk.cases.create',      'ایجاد پرونده ملک',             'melk', 'cases',   'create'],
        ['melk.cases.edit',        'ویرایش پرونده ملک',             'melk', 'cases',   'edit'],
        ['melk.cases.participate', 'مشارکت در پرونده (کارشناس)',   'melk', 'cases',   'participate'],
    ];
    foreach ( $melk_perms as $p ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_permissions WHERE permission_key = %s", $p[0]
        ) );
        if ( ! $exists ) {
            $wpdb->insert( "{$wpdb->prefix}acl_permissions", [
                'permission_key' => $p[0], 'label' => $p[1],
                'module' => $p[2], 'resource' => $p[3], 'action' => $p[4], 'is_system' => 1,
            ] );
        }
    }

    $melk_role_perms = [
        'district_lead'        => ['melk.view','melk.manage','melk.cases.create','melk.cases.edit','gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.lock'],
        'neighborhood_manager' => ['melk.view','melk.cases.create','melk.cases.edit','gis.view','gis.parcels.view','gis.parcels.edit','gis.parcels.lock'],
        'melk_specialist'      => ['melk.view','melk.cases.participate','gis.view','gis.parcels.view'],
        'melk_coordinator'     => ['melk.view','melk.manage','melk.cases.create','melk.cases.edit','melk.cases.participate','gis.view','gis.parcels.view','gis.parcels.edit'],
    ];
    foreach ( $melk_role_perms as $slug => $perms ) {
        $role_id = (int) $wpdb->get_var( $wpdb->prepare(
            "SELECT id FROM {$wpdb->prefix}acl_roles WHERE slug = %s", $slug
        ) );
        if ( ! $role_id ) continue;
        foreach ( $perms as $pk ) {
            $wpdb->query( $wpdb->prepare(
                "INSERT IGNORE INTO {$wpdb->prefix}acl_role_permissions (role_id, permission_key) VALUES (%d, %s)",
                $role_id, $pk
            ) );
        }
    }

    $melk_menu = $wpdb->get_var( $wpdb->prepare(
        "SELECT id FROM {$wpdb->prefix}acl_menus WHERE menu_key = %s", 'eqtesad_melk'
    ) );
    if ( ! $melk_menu ) {
        $wpdb->insert( "{$wpdb->prefix}acl_menus", [
            'menu_key' => 'eqtesad_melk', 'parent_key' => '',
            'title' => '🏛️ اقتصاد ملک', 'icon' => '🏛️',
            'route' => 'eqtesad_melk', 'required_permission' => 'melk.view',
            'sort_order' => 18, 'is_active' => 0,
        ] );
    }

    update_option('acc_engine_db_version', '17.6');

    // v17.7 — اقتصاد ملک: پورتال مستقل، حذف از منوی ERP
    $wpdb->update(
        "{$wpdb->prefix}acl_menus",
        ['is_active' => 0],
        ['menu_key' => 'eqtesad_melk']
    );

    update_option('acc_engine_db_version', '17.7');

    // v17.8 — HVAC Reward Engine
    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_reward_accounts (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        actor_type VARCHAR(20) NOT NULL DEFAULT 'customer',
        user_id BIGINT UNSIGNED NULL,
        phone VARCHAR(20) NULL,
        display_name VARCHAR(200) NULL,
        tier VARCHAR(30) NOT NULL DEFAULT 'normal',
        points_total BIGINT NOT NULL DEFAULT 0,
        points_available BIGINT NOT NULL DEFAULT 0,
        hvac_credits BIGINT NOT NULL DEFAULT 0,
        lifetime_earned BIGINT NOT NULL DEFAULT 0,
        rank_score BIGINT NOT NULL DEFAULT 0,
        referral_code VARCHAR(12) NOT NULL DEFAULT '',
        referred_by BIGINT UNSIGNED NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY referral_code (referral_code),
        KEY actor_user (actor_type, user_id),
        KEY actor_phone (actor_type, phone),
        KEY rank_score (rank_score)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_reward_ledger (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        account_id BIGINT UNSIGNED NOT NULL,
        direction VARCHAR(10) NOT NULL DEFAULT 'earn',
        event_key VARCHAR(60) NOT NULL DEFAULT '',
        points INT NOT NULL DEFAULT 0,
        credits INT NOT NULL DEFAULT 0,
        order_value DECIMAL(14,2) NOT NULL DEFAULT 0,
        tier_mult DECIMAL(5,2) NOT NULL DEFAULT 1,
        quality_mult DECIMAL(5,2) NOT NULL DEFAULT 1,
        ref_type VARCHAR(40) NULL,
        ref_id BIGINT UNSIGNED NULL,
        dedupe_key VARCHAR(80) NULL,
        note VARCHAR(255) NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY account_id (account_id),
        KEY dedupe (account_id, event_key, dedupe_key)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_reward_badges (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        account_id BIGINT UNSIGNED NOT NULL,
        badge_key VARCHAR(40) NOT NULL,
        earned_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        UNIQUE KEY account_badge (account_id, badge_key)
    ) {$charset};");

    update_option('acc_engine_db_version', '17.8');

    // v17.9 — الگوریتم جامع خدمات + تکمیل پاداش
    $v179_cols = [
        [ 'svc_requests', 'priority', "VARCHAR(20) NOT NULL DEFAULT 'normal'" ],
        [ 'svc_requests', 'extra_data', 'JSON NULL' ],
        [ 'svc_requests', 'cost_estimate', 'DECIMAL(15,2) NOT NULL DEFAULT 0' ],
        [ 'svc_requests', 'cost_approved', 'TINYINT(1) NOT NULL DEFAULT 0' ],
        [ 'svc_requests', 'cost_approved_at', 'DATETIME NULL' ],
        [ 'svc_reports', 'photos_before', 'JSON NULL' ],
        [ 'svc_reports', 'photos_after', 'JSON NULL' ],
        [ 'svc_reports', 'cost_approval_status', "VARCHAR(20) NOT NULL DEFAULT 'none'" ],
        [ 'svc_reports', 'cost_approval_at', 'DATETIME NULL' ],
        [ 'svc_pm_contracts', 'operating_hours', 'INT UNSIGNED NOT NULL DEFAULT 0' ],
        [ 'svc_pm_contracts', 'visit_result', 'JSON NULL' ],
        [ 'svc_reward_accounts', 'active_benefits', 'JSON NULL' ],
    ];
    foreach ( $v179_cols as [ $tbl, $col, $def ] ) {
        if ( ! $wpdb->get_var( "SHOW COLUMNS FROM {$wpdb->prefix}{$tbl} LIKE '{$col}'" ) ) {
            $wpdb->query( "ALTER TABLE {$wpdb->prefix}{$tbl} ADD COLUMN {$col} {$def}" );
        }
    }

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_parts_orders (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        request_id BIGINT UNSIGNED NULL,
        tracking_code VARCHAR(6) NULL,
        customer_name VARCHAR(200) NOT NULL DEFAULT '',
        phone VARCHAR(20) NOT NULL DEFAULT '',
        part_number VARCHAR(100) NOT NULL DEFAULT '',
        part_qty INT NOT NULL DEFAULT 1,
        status VARCHAR(30) NOT NULL DEFAULT 'pending',
        supplier_id BIGINT UNSIGNED NULL,
        quote_price DECIMAL(15,2) NOT NULL DEFAULT 0,
        deposit_amount DECIMAL(15,2) NOT NULL DEFAULT 0,
        eta_days INT NOT NULL DEFAULT 5,
        in_stock TINYINT(1) NOT NULL DEFAULT 0,
        notes TEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    dbDelta("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}svc_equipment_inquiries (
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        request_id BIGINT UNSIGNED NULL,
        tracking_code VARCHAR(6) NULL,
        customer_name VARCHAR(200) NOT NULL DEFAULT '',
        phone VARCHAR(20) NOT NULL DEFAULT '',
        project_city VARCHAR(100) NOT NULL DEFAULT '',
        application VARCHAR(100) NOT NULL DEFAULT '',
        required_capacity VARCHAR(100) NOT NULL DEFAULT '',
        budget_range VARCHAR(100) NOT NULL DEFAULT '',
        status VARCHAR(30) NOT NULL DEFAULT 'inquiry',
        quote_options JSON NULL,
        selected_option VARCHAR(50) NULL,
        notes TEXT NULL,
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) {$charset};");

    acc_engine_upgrade_core_v19();
}

/**
 * ارتقای هسته حسابداری v19 — schema، snapshot، reversal
 */
function acc_engine_upgrade_core_v19(): void {
    global $wpdb;
    $pre = $wpdb->prefix;

    require_once ACC_PATH . 'includes/helpers/acc-schema.php';
    Acc_Schema::ensure_core_tables();

    $alters = [
        'posted_mapping_json'  => "ALTER TABLE {$pre}ai_acc_events ADD COLUMN posted_mapping_json LONGTEXT NULL COMMENT 'snapshot mapping at post time'",
        'reverses_event_id'    => "ALTER TABLE {$pre}ai_acc_events ADD COLUMN reverses_event_id BIGINT UNSIGNED NULL",
        'reversed_by_event_id' => "ALTER TABLE {$pre}ai_acc_events ADD COLUMN reversed_by_event_id BIGINT UNSIGNED NULL",
        'source_module'        => "ALTER TABLE {$pre}ai_acc_events ADD COLUMN source_module VARCHAR(40) NULL",
    ];
    foreach ( $alters as $col => $sql ) {
        $exists = $wpdb->get_var( $wpdb->prepare(
            'SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = %s',
            $pre . 'ai_acc_events', $col
        ) );
        if ( ! $exists ) {
            $wpdb->query( $sql );
        }
    }

    if ( ! $wpdb->get_var( $wpdb->prepare(
        "SELECT id FROM {$pre}ai_acc_event_templates WHERE event_type=%s AND version=1", 'REVERSAL'
    ) ) ) {
        $wpdb->insert( "{$pre}ai_acc_event_templates", [
            'event_type'    => 'REVERSAL',
            'version'       => 1,
            'template_name' => 'سند برگشتی',
            'mapping_logic' => wp_json_encode( [ 'pairs' => [] ] ),
            'description'   => 'از manual_entries در payload استفاده می‌شود',
            'is_active'     => 1,
        ] );
    }

    update_option( 'acc_engine_db_version', '19.0' );

    require_once ACC_PATH . 'includes/helpers/pc-service.php';
    PC_Service::ensure_performance_indexes();
}

/** Seed قالب‌های پیش‌فرض workflow */
class ACC_WF_Seeder {

    public static function run(): void {
        global $wpdb;

        $templates = [
            [
                'code' => 'building_residential',
                'name' => '🏢 ساختمان مسکونی',
                'description' => 'ساخت ساختمان مسکونی از زمین تا تحویل',
                'icon' => '🏢', 'color' => '#3b82f6',
                'stages' => [
                    ['code'=>'land',       'name'=>'تملک زمین',      'icon'=>'🏞️','color'=>'#10b981', 'days'=>30,  'budget_pct'=>15,
                        'tasks' => ['بررسی سند مالکیت', 'استعلام شهرداری', 'پیش‌خرید زمین', 'تنظیم سند', 'انتقال رسمی'],
                        'gates' => ['سند مالکیت تأیید شد', 'انتقال رسمی انجام شد']],
                    ['code'=>'design',     'name'=>'طراحی و نقشه',   'icon'=>'📐','color'=>'#8b5cf6', 'days'=>60,  'budget_pct'=>5,
                        'tasks' => ['طراحی معماری', 'محاسبات سازه', 'نقشه تأسیسات مکانیکی', 'نقشه برق', 'تأیید مهندس ناظر'],
                        'gates' => ['نقشه‌های اجرایی آماده', 'مهندس ناظر مشخص شد']],
                    ['code'=>'permit',     'name'=>'اخذ مجوزها',     'icon'=>'📋','color'=>'#f59e0b', 'days'=>45,  'budget_pct'=>3,
                        'tasks' => ['درخواست پروانه شهرداری', 'مفاصاحساب آب/برق/گاز', 'تأیید سازمان نظام مهندسی', 'صدور پروانه ساخت'],
                        'gates' => ['پروانه ساخت صادر شد', 'مفاصاحساب‌ها دریافت شد']],
                    ['code'=>'foundation', 'name'=>'خاکبرداری و پی', 'icon'=>'⛏️','color'=>'#dc2626', 'days'=>30,  'budget_pct'=>8,
                        'tasks' => ['تخریب بنای قدیمی', 'خاکبرداری', 'اجرای پی', 'تست بتن پی', 'گزارش پیشرفت'],
                        'gates' => ['پی تأیید مهندس ناظر', 'تست بتن قبول شد']],
                    ['code'=>'structure',  'name'=>'اسکلت',           'icon'=>'🏗️','color'=>'#ef4444', 'days'=>90,  'budget_pct'=>25,
                        'tasks' => ['آرماتوربندی', 'قالب‌بندی', 'بتن‌ریزی طبقات', 'سقف‌سازی', 'تست مقاومت', 'تأیید سازه'],
                        'gates' => ['همه طبقات اجرا شد', 'تست استحکام قبول', 'گزارش پیشرفت سازه']],
                    ['code'=>'rough_work', 'name'=>'سفت‌کاری',        'icon'=>'🧱','color'=>'#f97316', 'days'=>60,  'budget_pct'=>15,
                        'tasks' => ['دیوارچینی', 'سقف کاذب', 'پنجره و درب فلزی', 'بام‌سازی', 'عایق رطوبتی'],
                        'gates' => ['دیوارها تمام شد', 'بام عایق شد']],
                    ['code'=>'mep',        'name'=>'تأسیسات',         'icon'=>'⚡','color'=>'#06b6d4', 'days'=>45,  'budget_pct'=>12,
                        'tasks' => ['لوله‌کشی آب', 'لوله‌کشی فاضلاب', 'سیم‌کشی برق', 'لوله‌کشی گاز', 'سیستم سرمایش/گرمایش', 'تست تأسیسات'],
                        'gates' => ['تست آب و فاضلاب قبول', 'تأیید برق', 'تست گاز قبول']],
                    ['code'=>'finishing',  'name'=>'نازک‌کاری',       'icon'=>'🎨','color'=>'#a855f7', 'days'=>75,  'budget_pct'=>12,
                        'tasks' => ['گچ‌کاری', 'سرامیک کف و دیوار', 'کابینت آشپزخانه', 'نقاشی', 'نصب کلید و پریز', 'نصب درب چوبی', 'نما'],
                        'gates' => ['نازک‌کاری همه واحدها تمام', 'نما اجرا شد']],
                    ['code'=>'completion', 'name'=>'پایان‌کار',       'icon'=>'✅','color'=>'#10b981', 'days'=>30,  'budget_pct'=>3,
                        'tasks' => ['درخواست پایان‌کار', 'بازرسی نهایی شهرداری', 'صدور پایان‌کار', 'تحویل واحدها به مالکین'],
                        'gates' => ['پایان‌کار صادر شد', 'همه واحدها تحویل شد']],
                    ['code'=>'handover',   'name'=>'فروش و تحویل',    'icon'=>'🔑','color'=>'#059669', 'days'=>60,  'budget_pct'=>2,
                        'tasks' => ['تبلیغات و بازاریابی', 'قرارداد فروش', 'دریافت وجه', 'انتقال سند', 'تحویل کلید'],
                        'gates' => ['همه واحدها فروخته شد', 'پروژه بسته شد']],
                ],
            ],
            [
                'code' => 'renovation',
                'name' => '🔨 بازسازی',
                'description' => 'بازسازی و نوسازی ساختمان موجود',
                'icon' => '🔨', 'color' => '#f59e0b',
                'stages' => [
                    ['code'=>'assess', 'name'=>'بازدید و ارزیابی', 'icon'=>'🔍','color'=>'#3b82f6', 'days'=>7,  'budget_pct'=>3,
                        'tasks' => ['بازدید اولیه', 'برآورد هزینه', 'ارائه پیشنهاد', 'عقد قرارداد'],
                        'gates' => ['قرارداد امضا شد']],
                    ['code'=>'demolish', 'name'=>'تخریب و آماده‌سازی', 'icon'=>'⚒️','color'=>'#dc2626', 'days'=>10, 'budget_pct'=>10,
                        'tasks' => ['تخریب کف و دیوار', 'برچیدن کابینت قدیمی', 'تخلیه نخاله', 'پاکسازی'],
                        'gates' => ['محل آماده‌سازی شد']],
                    ['code'=>'rebuild', 'name'=>'بازسازی', 'icon'=>'🔧','color'=>'#f97316', 'days'=>45, 'budget_pct'=>60,
                        'tasks' => ['تأسیسات جدید', 'گچ‌کاری', 'سرامیک', 'نقاشی', 'کابینت', 'لوازم برقی'],
                        'gates' => ['اجرا تأیید مالک شد']],
                    ['code'=>'handover', 'name'=>'تحویل', 'icon'=>'✅','color'=>'#10b981', 'days'=>5, 'budget_pct'=>2,
                        'tasks' => ['نظافت', 'بازدید نهایی مالک', 'تحویل کلید'],
                        'gates' => ['مالک تأیید کرد']],
                ],
            ],
            [
                'code' => 'land_purchase',
                'name' => '🏞️ خرید زمین',
                'description' => 'فرآیند کامل خرید زمین',
                'icon' => '🏞️', 'color' => '#10b981',
                'stages' => [
                    ['code'=>'search', 'name'=>'جستجو و انتخاب', 'icon'=>'🔍','color'=>'#3b82f6', 'days'=>15, 'budget_pct'=>0,
                        'tasks' => ['جستجوی زمین مناسب', 'بازدید چند گزینه', 'مقایسه قیمت‌ها', 'انتخاب نهایی'],
                        'gates' => ['زمین انتخاب شد']],
                    ['code'=>'inquiry', 'name'=>'استعلام و بررسی', 'icon'=>'📋','color'=>'#f59e0b', 'days'=>10, 'budget_pct'=>1,
                        'tasks' => ['استعلام سند از اداره ثبت', 'استعلام شهرداری', 'بررسی طرح تفصیلی', 'بررسی بدهی‌ها'],
                        'gates' => ['سند پاک تأیید شد', 'استعلام شهرداری مثبت']],
                    ['code'=>'transfer', 'name'=>'انتقال', 'icon'=>'📝','color'=>'#10b981', 'days'=>10, 'budget_pct'=>99,
                        'tasks' => ['تنظیم مبایعه‌نامه', 'پرداخت وجه', 'تنظیم سند رسمی', 'دریافت سند نهایی'],
                        'gates' => ['سند انتقال یافت']],
                ],
            ],
        ];

        foreach ( $templates as $tpl ) {
            $exists = $wpdb->get_var( $wpdb->prepare(
                "SELECT id FROM {$wpdb->prefix}wf_templates WHERE code = %s", $tpl['code']
            ) );
            if ( $exists ) continue;

            $wpdb->insert( "{$wpdb->prefix}wf_templates", [
                'code' => $tpl['code'], 'name' => $tpl['name'], 'description' => $tpl['description'],
                'icon' => $tpl['icon'], 'color' => $tpl['color'], 'is_system' => 1,
            ] );
            $tpl_id = (int) $wpdb->insert_id;

            foreach ( $tpl['stages'] as $idx => $st ) {
                $wpdb->insert( "{$wpdb->prefix}wf_template_stages", [
                    'template_id'    => $tpl_id,
                    'code'           => $st['code'],
                    'name'           => $st['name'],
                    'icon'           => $st['icon'],
                    'color'          => $st['color'],
                    'sort_order'     => $idx,
                    'estimated_days' => $st['days'],
                    'budget_pct'     => $st['budget_pct'],
                ] );
                $stage_id = (int) $wpdb->insert_id;

                foreach ( ($st['tasks'] ?? []) as $tidx => $ttl ) {
                    $wpdb->insert( "{$wpdb->prefix}wf_template_tasks", [
                        'stage_id' => $stage_id, 'title' => $ttl, 'sort_order' => $tidx,
                    ] );
                }
                foreach ( ($st['gates'] ?? []) as $gtl ) {
                    $wpdb->insert( "{$wpdb->prefix}wf_template_gates", [
                        'stage_id' => $stage_id, 'gate_type' => 'manual_check', 'title' => $gtl,
                    ] );
                }
            }
        }
    }
}


/**
 * Auto-run install when DB version is outdated (no deactivation needed).
 */
function acc_engine_maybe_upgrade(): void {
    $ver = (string) get_option( 'acc_engine_db_version', '0' );
    if ( version_compare( $ver, '17.9', '<' ) ) {
        acc_engine_install();
        return;
    }
    if ( version_compare( $ver, '19.0', '<' ) ) {
        acc_engine_upgrade_core_v19();
    }
}
add_action('plugins_loaded', 'acc_engine_maybe_upgrade');

register_activation_hook(ACC_PATH . '../acc-engine.php', 'acc_engine_install');
