-- ============================================================================
-- EN: PostgreSQL Row-Level Security (RLS) Policies for Multi-Tenant Isolation
-- VI: PostgreSQL Row-Level Security (RLS) Policies cho Cách Ly Đa Tenant
-- ============================================================================
--
-- EN: These policies provide defense-in-depth for tenant isolation.
--     Primary mechanism: EF Core global query filters (application level).
--     Secondary mechanism: PostgreSQL RLS (database level).
--
--     The application sets session variables via:
--       SET LOCAL app.current_shop_id = '<uuid>';
--       SET LOCAL app.current_merchant_id = '<uuid>';
--
--     RLS policies use current_setting() to read these session variables.
--     When no session variable is set, the policy defaults to allowing access
--     (to support migrations, health checks, and admin operations).
--
-- VI: Các policies này cung cấp phòng thủ theo chiều sâu cho cách ly tenant.
--     Cơ chế chính: EF Core global query filters (cấp ứng dụng).
--     Cơ chế phụ: PostgreSQL RLS (cấp cơ sở dữ liệu).
-- ============================================================================

-- EN: Helper function to safely get current_setting with fallback
-- VI: Hàm trợ giúp để lấy current_setting an toàn với giá trị mặc định
CREATE OR REPLACE FUNCTION get_current_shop_id() RETURNS uuid AS $$
BEGIN
    RETURN current_setting('app.current_shop_id', true)::uuid;
EXCEPTION
    WHEN OTHERS THEN
        RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE;

CREATE OR REPLACE FUNCTION get_current_merchant_id() RETURNS uuid AS $$
BEGIN
    RETURN current_setting('app.current_merchant_id', true)::uuid;
EXCEPTION
    WHEN OTHERS THEN
        RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE;

CREATE OR REPLACE FUNCTION get_current_user_id() RETURNS uuid AS $$
BEGIN
    RETURN current_setting('app.current_user_id', true)::uuid;
EXCEPTION
    WHEN OTHERS THEN
        RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE;

-- ============================================================================
-- ORDER SERVICE DATABASE (order_service_db)
-- ============================================================================
-- EN: Connect to order service database before running these
-- VI: Kết nối đến database order service trước khi chạy các lệnh này

-- \connect order_service_db;

-- EN: Enable RLS on orders table
-- VI: Bật RLS trên bảng orders
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- EN: Policy: users can only see orders belonging to their shop
-- VI: Policy: users chỉ thấy orders thuộc shop của họ
CREATE POLICY tenant_isolation_orders ON orders
    USING (
        get_current_shop_id() IS NULL  -- EN: No tenant set = allow (admin/migration)
        OR shop_id = get_current_shop_id()
    );

-- EN: Enable RLS on order_items table
-- VI: Bật RLS trên bảng order_items
ALTER TABLE order_items ENABLE ROW LEVEL SECURITY;

-- EN: Policy: order_items are isolated via their parent order's shop_id
-- VI: Policy: order_items được cách ly qua shop_id của order cha
CREATE POLICY tenant_isolation_order_items ON order_items
    USING (
        get_current_shop_id() IS NULL
        OR order_id IN (SELECT id FROM orders WHERE shop_id = get_current_shop_id())
    );


-- ============================================================================
-- FNB ENGINE DATABASE (fnb_engine_db)
-- ============================================================================
-- \connect fnb_engine_db;

-- EN: Tables
ALTER TABLE tables ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_tables ON tables
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );

-- EN: Sessions
ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_sessions ON sessions
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );

-- EN: Reservations
ALTER TABLE reservations ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_reservations ON reservations
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );

-- EN: Kitchen tickets (isolated via session's shop)
ALTER TABLE kitchen_tickets ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_kitchen_tickets ON kitchen_tickets
    USING (
        get_current_shop_id() IS NULL
        OR session_id IN (SELECT id FROM sessions WHERE shop_id = get_current_shop_id())
    );


-- ============================================================================
-- INVENTORY SERVICE DATABASE (inventory_service_db)
-- ============================================================================
-- \connect inventory_service_db;

ALTER TABLE inventory_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_inventory_items ON inventory_items
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );

-- EN: Inventory transactions are isolated via their parent item's shop
-- VI: Inventory transactions được cách ly qua shop của item cha
ALTER TABLE inventory_transactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_inventory_transactions ON inventory_transactions
    USING (
        get_current_shop_id() IS NULL
        OR inventory_item_id IN (SELECT id FROM inventory_items WHERE shop_id = get_current_shop_id())
    );


-- ============================================================================
-- CATALOG SERVICE DATABASE (catalog_service_db)
-- ============================================================================
-- \connect catalog_service_db;

ALTER TABLE products ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_products ON products
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );

ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_categories ON categories
    USING (
        get_current_shop_id() IS NULL
        OR shop_id = get_current_shop_id()
    );


-- ============================================================================
-- WALLET SERVICE DATABASE (wallet_service_db)
-- ============================================================================
-- EN: Wallet uses user_id as tenant key (not shop_id)
-- VI: Wallet sử dụng user_id làm tenant key (không phải shop_id)
-- \connect wallet_service_db;

ALTER TABLE wallets ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_wallets ON wallets
    USING (
        get_current_user_id() IS NULL
        OR user_id = get_current_user_id()
    );

-- EN: Payments don't have direct user_id, accessed via wallet or order
-- VI: Payments không có user_id trực tiếp, truy cập qua wallet hoặc order

-- ============================================================================
-- EN: IMPORTANT NOTES
-- ============================================================================
-- 1. RLS policies apply to the database user running queries.
--    The superuser (postgres) bypasses RLS by default.
--    Use ALTER ROLE <app_user> NOBYPASSRLS; for the application user.
--
-- 2. To bypass RLS for migrations and admin operations:
--    - Run as superuser (postgres), OR
--    - Don't set app.current_shop_id session variable (policy allows NULL)
--
-- 3. To test RLS:
--    SET LOCAL app.current_shop_id = '<shop-uuid>';
--    SELECT * FROM orders;  -- Should only return rows for that shop
--
-- 4. These helper functions need to be created in EACH service database.
--    Run the CREATE FUNCTION statements per database.
-- ============================================================================
