# EventBookings Plugin Developer Guide

Comprehensive technical documentation for developers working with the EventBookings WordPress plugin.

## Table of Contents

1. [Architecture Overview](#architecture-overview)
2. [Class Structure](#class-structure)
3. [Database Design](#database-design)
4. [API Integration](#api-integration)
5. [Frontend Architecture](#frontend-architecture)
6. [Security Implementation](#security-implementation)
7. [Caching Strategy](#caching-strategy)
8. [Extension Points](#extension-points)
9. [Development Workflow](#development-workflow)
10. [Testing Guidelines](#testing-guidelines)

## Architecture Overview

The EventBookings plugin follows a modular architecture with clear separation of concerns:

```
eventbookings/
├── eventbookings.php                 # Main plugin file
├── includes/
│   ├── class-eventbookings.php      # Main plugin class
│   ├── class-eventbookings-settings.php    # Admin interface
│   ├── class-eventbookings-api.php         # API communication
│   ├── class-eventbookings-shortcode.php   # Frontend display
│   ├── class-eventbookings-customized-events.php
│   ├── css/                         # Stylesheets
│   └── js/                          # JavaScript assets
│       ├── src/                     # Source components
│       └── dist/                    # Compiled assets
├── assets/                          # Static assets
└── uninstall.php                    # Cleanup script
```

### Design Patterns

- **Singleton Pattern**: Main plugin class instantiation
- **Factory Pattern**: Component creation and configuration
- **Observer Pattern**: WordPress hooks and filters
- **Repository Pattern**: API data access layer
- **MVC Pattern**: Clear separation of model, view, controller logic

## Class Structure

### Core Classes Hierarchy

```php
EVENTBOOKINGS_Plugin
├── Eventbookings_Settings (Admin Interface)
├── Eventbookings_Plugin_Shortcode (Frontend Display)
└── Eventbookings_Api_Service (API Communication)
```

### EVENTBOOKINGS_Plugin

**Responsibility**: Main plugin initialization and dependency management

**Key Methods**:
- `__construct()`: Initialize plugin components
- `eventbookings_includes()`: Load required files
- Hook registration for admin and frontend

**Dependencies**:
- WordPress Plugin API
- Eventbookings_Settings
- Eventbookings_Plugin_Shortcode

### Eventbookings_Settings

**Responsibility**: Admin interface and settings management

**Key Features**:
- Admin menu creation
- Settings form rendering
- Credential management
- Environment configuration

**Security Considerations**:
- Nonce verification for all form submissions
- Input sanitization using WordPress functions
- Capability checks for admin access

### Eventbookings_Api_Service

**Responsibility**: External API communication and token management

**Key Features**:
- HTTP request handling with wp_remote_*
- Automatic token refresh
- Error handling and logging
- Request/response caching

**Authentication Flow**:
```php
1. Check stored access_token
2. Make API request with Bearer token
3. If 401 response, attempt token refresh
4. Retry original request with new token
5. Update stored credentials
```

### Eventbookings_Plugin_Shortcode

**Responsibility**: Frontend event display and user interaction

**Key Features**:
- Multiple shortcode handlers
- Dynamic asset loading
- AJAX endpoint management
- Template rendering

## Database Design

### Entity Relationship Diagram

```
wp_eb_plugin_users (1) ──── (1) Organisation
                              │
                              ├── (1:n) wp_events
                              ├── (1:n) wp_eb_feature_events
                              └── (1:1) wp_customized_events
```

### Data Access Patterns

#### Reading Events
```php
// Get published events for organization
$events = $wpdb->get_results($wpdb->prepare(
    "SELECT * FROM {$wpdb->prefix}events
     WHERE organisation_uuid = %s
     AND is_published = 1
     AND is_ended = 0
     ORDER BY created_at DESC",
    $org_uuid
));
```

#### Updating Settings
```php
// Update display settings atomically
$wpdb->update(
    $wpdb->prefix . 'customized_events',
    $sanitized_settings,
    ['organisation_uuid' => $org_uuid],
    '%s', // format for SET values
    '%s'  // format for WHERE values
);
```

### Index Strategy

**Performance Indexes**:
- `idx_organisation_uuid`: Fast organization filtering
- `idx_published`: Published event queries
- `idx_ended`: Active event filtering
- `idx_created_at`: Date-based sorting

**Composite Indexes**:
- `(organisation_uuid, is_published, is_ended)`: Complex queries
- `(created_at, is_published)`: Recent published events

## API Integration

### Request/Response Lifecycle

```php
1. Client Request → WordPress AJAX
2. Nonce Validation
3. Parameter Sanitization
4. Database Query (if needed)
5. API Request to EventBookings
6. Response Processing
7. Cache Storage
8. JSON Response to Client
```

### Error Handling Strategy

```php
class APIErrorHandler {
    public static function handleResponse($response) {
        // Network errors
        if (is_wp_error($response)) {
            return self::networkError($response);
        }

        // HTTP status errors
        $status = wp_remote_retrieve_response_code($response);
        if ($status >= 400) {
            return self::httpError($status, $response);
        }

        // API-specific errors
        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (isset($body['error'])) {
            return self::apiError($body);
        }

        return $body;
    }
}
```

### Rate Limiting

```php
// Implement exponential backoff
function retry_api_request($callback, $max_retries = 3) {
    $attempt = 0;
    while ($attempt < $max_retries) {
        $result = $callback();
        if (!is_error($result)) {
            return $result;
        }

        $delay = pow(2, $attempt); // 1s, 2s, 4s
        sleep($delay);
        $attempt++;
    }
    return new WP_Error('api_failed', 'Max retries exceeded');
}
```

## Frontend Architecture

### Component-Based Design

The frontend uses a modular component architecture:

```javascript
// Component Structure
src/
├── component/
│   ├── event-card/
│   │   ├── EventCard.js
│   │   └── style.css
│   ├── checkout/
│   │   ├── Checkout.js
│   │   ├── component/
│   │   │   └── SingleAttendeeFormComponent.js
│   │   └── style.css
│   └── event-details/
│       ├── EventDetails.js
│       └── TicketPopup.js
├── utils/
│   ├── helpers.js
│   ├── component.js
│   └── constants.js
└── index.js
```

### Component Communication

```javascript
// Event-driven communication
class EventBus {
    constructor() {
        this.events = {};
    }

    on(event, callback) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(callback);
    }

    emit(event, data) {
        if (this.events[event]) {
            this.events[event].forEach(callback => callback(data));
        }
    }
}

// Usage
const eventBus = new EventBus();
eventBus.on('ticket:selected', (ticketData) => {
    // Update cart display
    updateCartDisplay(ticketData);
});
```

### State Management

```javascript
// Simple state management for components
class ComponentState {
    constructor(initialState = {}) {
        this.state = { ...initialState };
        this.subscribers = [];
    }

    setState(updates) {
        this.state = { ...this.state, ...updates };
        this.notifySubscribers();
    }

    getState() {
        return { ...this.state };
    }

    subscribe(callback) {
        this.subscribers.push(callback);
        return () => {
            this.subscribers = this.subscribers.filter(sub => sub !== callback);
        };
    }

    notifySubscribers() {
        this.subscribers.forEach(callback => callback(this.state));
    }
}
```

## Security Implementation

### Input Validation

```php
// Comprehensive input sanitization
function sanitize_event_input($input) {
    $sanitized = [];

    // String fields
    $string_fields = ['event_name', 'description'];
    foreach ($string_fields as $field) {
        if (isset($input[$field])) {
            $sanitized[$field] = sanitize_text_field($input[$field]);
        }
    }

    // Email fields
    if (isset($input['email'])) {
        $sanitized['email'] = sanitize_email($input['email']);
    }

    // URL fields
    if (isset($input['url'])) {
        $sanitized['url'] = esc_url_raw($input['url']);
    }

    // Integer fields
    $int_fields = ['ticket_quantity', 'price'];
    foreach ($int_fields as $field) {
        if (isset($input[$field])) {
            $sanitized[$field] = absint($input[$field]);
        }
    }

    return $sanitized;
}
```

### SQL Injection Prevention

```php
// Always use prepared statements
function get_events_by_organization($org_uuid, $limit = 10, $offset = 0) {
    global $wpdb;

    return $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}events
         WHERE organisation_uuid = %s
         AND is_published = 1
         ORDER BY created_at DESC
         LIMIT %d OFFSET %d",
        $org_uuid,
        $limit,
        $offset
    ));
}
```

### Cross-Site Request Forgery (CSRF) Protection

```php
// Nonce generation and verification
function generate_form_nonce($action = 'eventbookings_form') {
    return wp_create_nonce($action);
}

function verify_form_nonce($nonce, $action = 'eventbookings_form') {
    if (!wp_verify_nonce($nonce, $action)) {
        wp_die('Security check failed');
    }
}

// AJAX nonce verification
function verify_ajax_nonce() {
    if (!wp_verify_nonce($_REQUEST['ajax_nonce'] ?? '', 'eventbookings_wp')) {
        wp_send_json_error(['message' => 'Invalid nonce']);
        wp_die();
    }
}
```

### Data Sanitization for Output

```php
// Context-aware output escaping
function safe_output($data, $context = 'html') {
    switch ($context) {
        case 'html':
            return esc_html($data);
        case 'attribute':
            return esc_attr($data);
        case 'url':
            return esc_url($data);
        case 'textarea':
            return esc_textarea($data);
        case 'js':
            return esc_js($data);
        default:
            return esc_html($data);
    }
}
```

## Caching Strategy

### Multi-Level Caching

```php
class EventbookingsCacheManager {
    private $cache_group = 'eventbookings';
    private $cache_expiry = 1800; // 30 minutes

    public function get($key, $default = false) {
        // Try WordPress object cache first
        $value = wp_cache_get($key, $this->cache_group);
        if ($value !== false) {
            return $value;
        }

        // Try transients for persistent cache
        $transient_key = $this->cache_group . '_' . $key;
        $value = get_transient($transient_key);
        if ($value !== false) {
            // Store in object cache for this request
            wp_cache_set($key, $value, $this->cache_group, $this->cache_expiry);
            return $value;
        }

        return $default;
    }

    public function set($key, $value, $expiry = null) {
        $expiry = $expiry ?: $this->cache_expiry;

        // Store in object cache
        wp_cache_set($key, $value, $this->cache_group, $expiry);

        // Store in transients for persistence
        $transient_key = $this->cache_group . '_' . $key;
        set_transient($transient_key, $value, $expiry);
    }

    public function delete($key) {
        wp_cache_delete($key, $this->cache_group);

        $transient_key = $this->cache_group . '_' . $key;
        delete_transient($transient_key);
    }

    public function flush() {
        wp_cache_flush_group($this->cache_group);

        // Clear all related transients
        global $wpdb;
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$wpdb->options}
             WHERE option_name LIKE %s",
            '_transient_' . $this->cache_group . '_%'
        ));
    }
}
```

### Cache Invalidation Strategy

```php
// Event-based cache invalidation
add_action('eventbookings_event_updated', function($event_uuid) {
    $cache_keys = [
        'events_list_' . get_org_uuid(),
        'featured_events_' . get_org_uuid(),
        'event_details_' . $event_uuid
    ];

    $cache_manager = new EventbookingsCacheManager();
    foreach ($cache_keys as $key) {
        $cache_manager->delete($key);
    }
});
```

## Extension Points

### Hooks and Filters

#### Action Hooks

```php
// Event lifecycle hooks
do_action('eventbookings_before_event_display', $event_data);
do_action('eventbookings_after_event_display', $event_data);
do_action('eventbookings_event_booked', $order_data);
do_action('eventbookings_settings_updated', $old_settings, $new_settings);

// API interaction hooks
do_action('eventbookings_before_api_request', $endpoint, $method, $body);
do_action('eventbookings_after_api_request', $endpoint, $response);
```

#### Filter Hooks

```php
// Data modification filters
$event_data = apply_filters('eventbookings_event_data', $event_data, $context);
$api_headers = apply_filters('eventbookings_api_headers', $headers, $endpoint);
$cache_key = apply_filters('eventbookings_cache_key', $key, $data);

// Display modification filters
$shortcode_output = apply_filters('eventbookings_shortcode_output', $html, $atts);
$event_card_html = apply_filters('eventbookings_event_card_html', $html, $event);
```

### Custom Implementation Examples

#### Adding Custom Event Fields

```php
// Add custom field to event data
add_filter('eventbookings_event_data', function($event_data, $context) {
    if ($context === 'display') {
        $event_data['custom_field'] = get_event_custom_field($event_data['uuid']);
    }
    return $event_data;
}, 10, 2);

// Modify event display template
add_filter('eventbookings_event_card_html', function($html, $event) {
    if (isset($event['custom_field'])) {
        $custom_html = '<div class="custom-field">' . esc_html($event['custom_field']) . '</div>';
        $html = str_replace('</div>', $custom_html . '</div>', $html);
    }
    return $html;
}, 10, 2);
```

#### Custom API Endpoints

```php
// Add custom AJAX endpoint
add_action('wp_ajax_custom_eventbookings_action', 'handle_custom_action');
add_action('wp_ajax_nopriv_custom_eventbookings_action', 'handle_custom_action');

function handle_custom_action() {
    // Verify nonce
    if (!wp_verify_nonce($_POST['nonce'], 'eventbookings_wp')) {
        wp_send_json_error('Invalid nonce');
    }

    // Custom logic here
    $result = perform_custom_operation($_POST['data']);

    wp_send_json_success($result);
}
```

## Development Workflow

### Environment Setup

1. **Local Development**:
```bash
# Clone repository
git clone [repository-url]
cd eventbookings

# Install dependencies
npm install

# Set up development environment
cp wp-config-sample.php wp-config.php
# Edit wp-config.php with local database settings
```

2. **Development Constants**:
```php
// In wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', true);

// EventBookings specific
define('EVENTBOOKINGS_ENV', 'development');
define('EVENTBOOKINGS_DEBUG', true);
```

### Code Standards

#### PHP Standards (WordPress Coding Standards)

```php
// Class naming: PascalCase with descriptive names
class Eventbookings_Custom_Feature {

    // Method naming: snake_case
    public function process_event_data($event_id) {
        // Variable naming: snake_case
        $processed_data = [];

        // Array formatting
        $config = [
            'api_url'    => EVENTBOOKINGS_BASE_URL,
            'timeout'    => 30,
            'retry'      => true,
        ];

        return $processed_data;
    }
}

// Hook callbacks: descriptive function names
function eventbookings_handle_custom_action() {
    // Implementation
}
add_action('wp_ajax_custom_action', 'eventbookings_handle_custom_action');
```

#### JavaScript Standards (ES6+)

```javascript
// Use ES6+ features
class EventManager {
    constructor(config = {}) {
        this.config = {
            apiUrl: '/wp-admin/admin-ajax.php',
            timeout: 5000,
            ...config
        };
    }

    async fetchEvents(params = {}) {
        const response = await fetch(this.config.apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
                action: 'eventbookings_wp_get_events_public',
                ...params
            })
        });

        return response.json();
    }
}

// Module exports
export { EventManager };
```

### Build Process

```json
// package.json
{
  "scripts": {
    "dev": "webpack --mode development --watch",
    "build": "webpack --mode production",
    "test": "jest",
    "lint:php": "phpcs --standard=WordPress .",
    "lint:js": "eslint src/",
    "lint:css": "stylelint '**/*.css'"
  },
  "devDependencies": {
    "webpack": "^5.0.0",
    "babel-loader": "^8.0.0",
    "css-loader": "^6.0.0",
    "eslint": "^8.0.0",
    "jest": "^27.0.0"
  }
}
```

## Testing Guidelines

### Unit Testing

#### PHP Unit Tests (PHPUnit)

```php
class EventbookingsApiTest extends WP_UnitTestCase {

    public function setUp(): void {
        parent::setUp();

        // Set up test data
        $this->api_service = new Eventbookings_Api_Service();
        $this->test_org_uuid = 'test-org-uuid';
    }

    public function test_api_request_handler_success() {
        // Mock successful API response
        $mock_response = [
            'events' => [
                ['uuid' => 'test-event-1', 'name' => 'Test Event']
            ]
        ];

        // Test the method
        $result = $this->api_service->eventbookings_api_request_handler(
            'GET',
            'https://api.example.com/events'
        );

        $this->assertIsArray($result);
        $this->assertArrayHasKey('events', $result);
    }

    public function test_api_request_handler_authentication_failure() {
        // Test 401 response and token refresh
        // Implementation details...
    }
}
```

#### JavaScript Unit Tests (Jest)

```javascript
// EventManager.test.js
import { EventManager } from '../src/EventManager';

describe('EventManager', () => {
    let eventManager;

    beforeEach(() => {
        eventManager = new EventManager({
            apiUrl: 'http://test.com/wp-admin/admin-ajax.php'
        });

        // Mock fetch
        global.fetch = jest.fn();
    });

    afterEach(() => {
        jest.restoreAllMocks();
    });

    test('should fetch events successfully', async () => {
        const mockResponse = {
            success: true,
            data: {
                events: [{ id: 1, name: 'Test Event' }]
            }
        };

        fetch.mockResolvedValueOnce({
            ok: true,
            json: async () => mockResponse
        });

        const result = await eventManager.fetchEvents({ position: 1 });

        expect(result.success).toBe(true);
        expect(result.data.events).toHaveLength(1);
    });
});
```

### Integration Testing

```php
class EventbookingsIntegrationTest extends WP_UnitTestCase {

    public function test_complete_event_booking_flow() {
        // 1. Create test event
        $event_id = $this->factory->post->create([
            'post_type' => 'event',
            'post_status' => 'publish'
        ]);

        // 2. Test event display
        $shortcode_output = do_shortcode('[eventbookings_events_shortcode]');
        $this->assertStringContains('eb-wp-event-lists', $shortcode_output);

        // 3. Test booking process
        $_POST = [
            'action' => 'eventbookings_wp_ticket_purchase',
            'ajax_nonce' => wp_create_nonce('eventbookings_wp'),
            'order' => json_encode([
                'event_uuid' => $event_id,
                'tickets' => [['id' => 1, 'quantity' => 2]]
            ])
        ];

        // Simulate AJAX request
        ob_start();
        do_action('wp_ajax_eventbookings_wp_ticket_purchase');
        $response = ob_get_clean();

        $result = json_decode($response, true);
        $this->assertTrue($result['success']);
    }
}
```

### Performance Testing

```php
class EventbookingsPerformanceTest extends WP_UnitTestCase {

    public function test_api_request_caching() {
        $start_time = microtime(true);

        // First request (should hit API)
        $result1 = Eventbookings_Api_Service::eventbookings_api_request_handler(
            'GET',
            EVENTBOOKINGS_BASE_URL . '/events'
        );

        $first_request_time = microtime(true) - $start_time;

        // Second request (should hit cache)
        $start_time = microtime(true);
        $result2 = Eventbookings_Api_Service::eventbookings_api_request_handler(
            'GET',
            EVENTBOOKINGS_BASE_URL . '/events'
        );
        $cached_request_time = microtime(true) - $start_time;

        // Cache should be significantly faster
        $this->assertLessThan($first_request_time * 0.1, $cached_request_time);
        $this->assertEquals($result1, $result2);
    }
}
```

### Running Tests

```bash
# PHP Unit Tests
./vendor/bin/phpunit tests/

# JavaScript Tests
npm test

# Integration Tests
npm run test:integration

# Performance Tests
npm run test:performance

# All Tests
npm run test:all
```

This developer guide provides comprehensive information for developers to understand, extend, and maintain the EventBookings WordPress plugin effectively.