-- ============================================================================
-- Migration: Add Multi-Client Access & Client Hierarchy Support
-- Version: 1.0
-- Date: 2025-09-30
-- Description: Menambahkan support untuk parent-child clients dan 
--              multi-client access per user
-- ============================================================================

-- ----------------------------------------------------------------------------
-- STEP 1: Update Clients Table
-- ----------------------------------------------------------------------------

-- Add new columns to clients table
ALTER TABLE clients 
ADD COLUMN IF NOT EXISTS description TEXT,
ADD COLUMN IF NOT EXISTS client_type VARCHAR DEFAULT 'sub_client',
ADD COLUMN IF NOT EXISTS parent_client_id UUID,
ADD COLUMN IF NOT EXISTS settings JSONB,
ADD COLUMN IF NOT EXISTS max_users INT4,
ADD COLUMN IF NOT EXISTS max_storage INT8;

-- Add index for parent_client_id
CREATE INDEX IF NOT EXISTS idx_clients_parent_client_id 
ON clients(parent_client_id);

-- Add foreign key constraint
ALTER TABLE clients 
ADD CONSTRAINT fk_clients_parent_client 
FOREIGN KEY (parent_client_id) 
REFERENCES clients(id) 
ON DELETE SET NULL;

-- Update existing clients to 'standalone' type
UPDATE clients 
SET client_type = 'standalone' 
WHERE client_type IS NULL OR client_type = '';

COMMENT ON COLUMN clients.client_type IS 'parent_client, sub_client, or standalone';
COMMENT ON COLUMN clients.parent_client_id IS 'Reference to parent client for hierarchy';
COMMENT ON COLUMN clients.settings IS 'JSONB custom settings for the client';

-- ----------------------------------------------------------------------------
-- STEP 2: Update Users Table
-- ----------------------------------------------------------------------------

-- Add super admin flag
ALTER TABLE users 
ADD COLUMN IF NOT EXISTS is_super_admin BOOLEAN DEFAULT FALSE;

-- Create index for super admin lookups
CREATE INDEX IF NOT EXISTS idx_users_is_super_admin 
ON users(is_super_admin) 
WHERE is_super_admin = TRUE;

COMMENT ON COLUMN users.is_super_admin IS 'Platform super admin with access to all clients';
COMMENT ON COLUMN users.client_id IS 'Primary/default client for the user';

-- ----------------------------------------------------------------------------
-- STEP 3: Create UserClientAccess Table (Many-to-Many)
-- ----------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS user_client_access (
    id SERIAL PRIMARY KEY,
    user_id INT4 NOT NULL,
    client_id UUID NOT NULL,
    
    -- Access control
    access_type VARCHAR DEFAULT 'read',
    can_manage BOOLEAN DEFAULT FALSE,
    can_delegate BOOLEAN DEFAULT FALSE,
    include_sub_clients BOOLEAN DEFAULT FALSE,
    
    -- Audit
    granted_by_id INT4,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    
    -- Constraints
    CONSTRAINT fk_user_client_access_user 
        FOREIGN KEY (user_id) 
        REFERENCES users(id) 
        ON DELETE CASCADE,
    
    CONSTRAINT fk_user_client_access_client 
        FOREIGN KEY (client_id) 
        REFERENCES clients(id) 
        ON DELETE CASCADE,
    
    CONSTRAINT fk_user_client_access_granted_by 
        FOREIGN KEY (granted_by_id) 
        REFERENCES users(id) 
        ON DELETE SET NULL,
    
    -- Prevent duplicate access entries
    CONSTRAINT uk_user_client_access_unique 
        UNIQUE (user_id, client_id)
);

-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_user_client_access_user_id 
ON user_client_access(user_id);

CREATE INDEX IF NOT EXISTS idx_user_client_access_client_id 
ON user_client_access(client_id);

CREATE INDEX IF NOT EXISTS idx_user_client_access_user_client 
ON user_client_access(user_id, client_id);

CREATE INDEX IF NOT EXISTS idx_user_client_access_active 
ON user_client_access(user_id, is_active) 
WHERE is_active = TRUE;

-- Add comments
COMMENT ON TABLE user_client_access IS 'Many-to-many relationship between users and clients for multi-tenant access';
COMMENT ON COLUMN user_client_access.access_type IS 'Access level: read, write, admin, or owner';
COMMENT ON COLUMN user_client_access.can_manage IS 'Can manage client settings';
COMMENT ON COLUMN user_client_access.can_delegate IS 'Can grant access to other users';
COMMENT ON COLUMN user_client_access.include_sub_clients IS 'Automatically grant access to all sub-clients';

-- ----------------------------------------------------------------------------
-- STEP 4: Data Migration (Optional but Recommended)
-- ----------------------------------------------------------------------------

-- Create UserClientAccess entries for existing users with client_id
-- This is optional - only if you want existing users to have explicit access records
INSERT INTO user_client_access (
    user_id, 
    client_id, 
    access_type, 
    can_manage, 
    is_active, 
    created_at, 
    updated_at
)
SELECT 
    id as user_id,
    client_id,
    'admin' as access_type,  -- Default existing users as admin
    TRUE as can_manage,
    TRUE as is_active,
    NOW(),
    NOW()
FROM users 
WHERE client_id IS NOT NULL 
  AND (is_super_admin IS NULL OR is_super_admin = FALSE)
  AND NOT EXISTS (
      SELECT 1 FROM user_client_access uca 
      WHERE uca.user_id = users.id 
        AND uca.client_id = users.client_id
  );

-- ----------------------------------------------------------------------------
-- STEP 5: Create Helper Functions (Optional)
-- ----------------------------------------------------------------------------

-- Function to get all accessible client IDs for a user (including sub-clients)
CREATE OR REPLACE FUNCTION get_user_accessible_client_ids(p_user_id INT4)
RETURNS TABLE(client_id UUID) AS $$
BEGIN
    RETURN QUERY
    WITH RECURSIVE client_hierarchy AS (
        -- Base case: direct client access
        SELECT uca.client_id, uca.include_sub_clients
        FROM user_client_access uca
        WHERE uca.user_id = p_user_id 
          AND uca.is_active = TRUE
        
        UNION
        
        -- User's primary client
        SELECT u.client_id, FALSE as include_sub_clients
        FROM users u
        WHERE u.id = p_user_id 
          AND u.client_id IS NOT NULL
        
        UNION
        
        -- Recursive case: sub-clients if include_sub_clients is true
        SELECT c.id, FALSE
        FROM clients c
        INNER JOIN client_hierarchy ch ON c.parent_client_id = ch.client_id
        WHERE ch.include_sub_clients = TRUE
          AND c.is_active = TRUE
    )
    SELECT DISTINCT ch.client_id 
    FROM client_hierarchy ch
    WHERE ch.client_id IS NOT NULL;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION get_user_accessible_client_ids IS 'Returns all client IDs accessible by a user including sub-clients';

-- Function to check if client is a parent (has sub-clients)
CREATE OR REPLACE FUNCTION is_parent_client(p_client_id UUID)
RETURNS BOOLEAN AS $$
DECLARE
    v_count INT;
BEGIN
    SELECT COUNT(*) INTO v_count
    FROM clients
    WHERE parent_client_id = p_client_id
      AND is_active = TRUE;
    
    RETURN v_count > 0;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION is_parent_client IS 'Check if a client has sub-clients';

-- Function to get all sub-client IDs recursively
CREATE OR REPLACE FUNCTION get_sub_client_ids(p_parent_client_id UUID)
RETURNS TABLE(client_id UUID) AS $$
BEGIN
    RETURN QUERY
    WITH RECURSIVE sub_clients AS (
        -- Base case: direct children
        SELECT id as client_id
        FROM clients
        WHERE parent_client_id = p_parent_client_id
          AND is_active = TRUE
        
        UNION
        
        -- Recursive case: children of children
        SELECT c.id
        FROM clients c
        INNER JOIN sub_clients sc ON c.parent_client_id = sc.client_id
        WHERE c.is_active = TRUE
    )
    SELECT sc.client_id FROM sub_clients sc;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION get_sub_client_ids IS 'Get all sub-client IDs recursively for a parent client';

-- ----------------------------------------------------------------------------
-- STEP 6: Create Views for Common Queries (Optional)
-- ----------------------------------------------------------------------------

-- View: User Client Access with Details
CREATE OR REPLACE VIEW v_user_client_access_details AS
SELECT 
    uca.id,
    uca.user_id,
    u.username,
    u.fullname,
    u.email,
    uca.client_id,
    c.name as client_name,
    c.client_type,
    c.parent_client_id,
    pc.name as parent_client_name,
    uca.access_type,
    uca.can_manage,
    uca.can_delegate,
    uca.include_sub_clients,
    uca.granted_by_id,
    gb.username as granted_by_username,
    uca.is_active,
    uca.created_at,
    uca.updated_at
FROM user_client_access uca
INNER JOIN users u ON uca.user_id = u.id
INNER JOIN clients c ON uca.client_id = c.id
LEFT JOIN clients pc ON c.parent_client_id = pc.id
LEFT JOIN users gb ON uca.granted_by_id = gb.id;

COMMENT ON VIEW v_user_client_access_details IS 'User client access with user and client details';

-- View: Client Hierarchy
CREATE OR REPLACE VIEW v_client_hierarchy AS
SELECT 
    c.id,
    c.name,
    c.description,
    c.client_type,
    c.parent_client_id,
    pc.name as parent_client_name,
    (SELECT COUNT(*) FROM clients WHERE parent_client_id = c.id AND is_active = TRUE) as sub_client_count,
    c.max_users,
    c.max_storage,
    (SELECT COUNT(*) FROM users WHERE client_id = c.id AND is_active = TRUE) as current_users,
    c.is_active,
    c.created_at,
    c.updated_at
FROM clients c
LEFT JOIN clients pc ON c.parent_client_id = pc.id;

COMMENT ON VIEW v_client_hierarchy IS 'Client hierarchy with parent info and statistics';

-- ----------------------------------------------------------------------------
-- STEP 7: Create Triggers (Optional - for audit/auto-update)
-- ----------------------------------------------------------------------------

-- Trigger to update updated_at on user_client_access
CREATE OR REPLACE FUNCTION update_user_client_access_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_update_user_client_access_timestamp ON user_client_access;
CREATE TRIGGER trg_update_user_client_access_timestamp
    BEFORE UPDATE ON user_client_access
    FOR EACH ROW
    EXECUTE FUNCTION update_user_client_access_timestamp();

-- ----------------------------------------------------------------------------
-- STEP 8: Grant Permissions (Adjust based on your DB user)
-- ----------------------------------------------------------------------------

-- Grant permissions to application user (adjust user name as needed)
-- GRANT SELECT, INSERT, UPDATE, DELETE ON user_client_access TO your_app_user;
-- GRANT USAGE, SELECT ON SEQUENCE user_client_access_id_seq TO your_app_user;
-- GRANT SELECT ON v_user_client_access_details TO your_app_user;
-- GRANT SELECT ON v_client_hierarchy TO your_app_user;

-- ----------------------------------------------------------------------------
-- VERIFICATION QUERIES
-- ----------------------------------------------------------------------------

-- Verify clients table structure
SELECT column_name, data_type, column_default 
FROM information_schema.columns 
WHERE table_name = 'clients' 
ORDER BY ordinal_position;

-- Verify users table has is_super_admin
SELECT column_name, data_type, column_default 
FROM information_schema.columns 
WHERE table_name = 'users' AND column_name = 'is_super_admin';

-- Verify user_client_access table
SELECT column_name, data_type, column_default 
FROM information_schema.columns 
WHERE table_name = 'user_client_access' 
ORDER BY ordinal_position;

-- Check indexes
SELECT indexname, tablename, indexdef 
FROM pg_indexes 
WHERE tablename IN ('clients', 'users', 'user_client_access')
ORDER BY tablename, indexname;

-- ============================================================================
-- ROLLBACK SCRIPT (Use with caution!)
-- ============================================================================

/*
-- To rollback this migration (WARNING: Will lose data!)

-- Drop functions
DROP FUNCTION IF EXISTS get_user_accessible_client_ids(INT4);
DROP FUNCTION IF EXISTS is_parent_client(UUID);
DROP FUNCTION IF EXISTS get_sub_client_ids(UUID);
DROP FUNCTION IF EXISTS update_user_client_access_timestamp();

-- Drop views
DROP VIEW IF EXISTS v_user_client_access_details;
DROP VIEW IF EXISTS v_client_hierarchy;

-- Drop table
DROP TABLE IF EXISTS user_client_access;

-- Remove columns from users
ALTER TABLE users DROP COLUMN IF EXISTS is_super_admin;

-- Remove columns from clients
ALTER TABLE clients DROP COLUMN IF EXISTS description;
ALTER TABLE clients DROP COLUMN IF EXISTS client_type;
ALTER TABLE clients DROP COLUMN IF EXISTS parent_client_id;
ALTER TABLE clients DROP COLUMN IF EXISTS settings;
ALTER TABLE clients DROP COLUMN IF EXISTS max_users;
ALTER TABLE clients DROP COLUMN IF EXISTS max_storage;

*/
