<?php
// Publish Generators - Generate different file formats for publishing
require_once '../includes/db.php';

// Generate Static HTML Dashboard with Full API Compatibility
function generateStaticHTMLDashboard($event_id, $temp_dir, $pdo, $data_types = null) {
    error_log("Starting generateStaticHTMLDashboard for event $event_id in $temp_dir");
    $files = [];
    
    // Get event data
    $event_stmt = $pdo->prepare("SELECT * FROM events WHERE id = ?");
    $event_stmt->execute([$event_id]);
    $event = $event_stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$event) {
        error_log("Event not found for ID: $event_id");
        throw new Exception('Event not found');
    }
    
    error_log("Found event: " . $event['name']);
    
    // Create the static HTML dashboard based on public_event_dashboard.php
    $dashboard_content = generateStaticDashboardHTML($event, $event_id);
    
    // Save the main dashboard HTML
    $dashboard_file = $temp_dir . '/index.html';
    error_log("Saving dashboard to: $dashboard_file");
    
    $bytes_written = file_put_contents($dashboard_file, $dashboard_content);
    if ($bytes_written === false) {
        error_log("Failed to write dashboard file");
        throw new Exception('Failed to write dashboard file');
    }
    
    error_log("Dashboard file written successfully, $bytes_written bytes");
    $files[] = ['local' => $dashboard_file, 'remote' => 'index.html'];
    
    // Generate all static API endpoints that the dashboard needs
    error_log("Generating static API endpoints...");
    $api_files = generateStaticAPIFiles($event_id, $temp_dir, $pdo);
    error_log("Generated " . count($api_files) . " API endpoint files");
    $files = array_merge($files, $api_files);
    
    // Include participant photos only if requested
    if ($data_types === null || in_array('participant_photos', $data_types)) {
        error_log("Copying participant photos...");
        $photo_files = copyParticipantPhotos($event_id, $temp_dir, $pdo);
        error_log("Copied " . count($photo_files) . " photo files");
        $files = array_merge($files, $photo_files);
    } else {
        error_log("Skipping participant photos (not in data_types)");
    }
    
    // Include asset files (CSS, JS, logos, etc.)
    error_log("Copying asset files...");
    $asset_files = copyStaticAssetFiles($temp_dir);
    error_log("Copied " . count($asset_files) . " asset files");
    $files = array_merge($files, $asset_files);
    
    error_log("Total files generated: " . count($files));
    return $files;
}

// Generate HTML Dashboard (Legacy function)
function generateHTMLDashboard($event_id, $temp_dir, $pdo, $data_types = null) {
    error_log("Starting generateHTMLDashboard for event $event_id in $temp_dir");
    $files = [];
    
    // Get event data
    $event_stmt = $pdo->prepare("SELECT * FROM events WHERE id = ?");
    $event_stmt->execute([$event_id]);
    $event = $event_stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$event) {
        error_log("Event not found for ID: $event_id");
        throw new Exception('Event not found');
    }
    
    error_log("Found event: " . $event['name']);
    
    // Copy the remote dashboard template (prefer new static variant if present)
    $static_template_path = dirname(__FILE__) . '/templates/remote_dashboard_static.html';
    $template_path = file_exists($static_template_path) ? $static_template_path : dirname(__FILE__) . '/templates/remote_dashboard.html';
    error_log("Using template at: $template_path");
    
    if (!file_exists($template_path)) {
        error_log("Remote dashboard template not found at: $template_path");
        throw new Exception('Remote dashboard template not found');
    }
    
    error_log("Template found, reading content...");
    $dashboard_content = file_get_contents($template_path);
    
    if ($dashboard_content === false) {
        error_log("Failed to read template content");
        throw new Exception('Failed to read template content');
    }
    
    error_log("Template content read successfully, length: " . strlen($dashboard_content));
    
    // Customize the template with event-specific data
    $dashboard_content = str_replace(
        '<span id="event-title">StyleScore Competition</span>',
        '<span id="event-title">' . htmlspecialchars($event['name']) . '</span>',
        $dashboard_content
    );
    
    $subtitle = ($event['location'] ? $event['location'] . ' • ' : '') . 
                ($event['sport_discipline'] ? $event['sport_discipline'] : 'Competition');
    $dashboard_content = str_replace(
        '<p class="lead mb-0" id="event-subtitle">Live Results Dashboard</p>',
        '<p class="lead mb-0" id="event-subtitle">' . htmlspecialchars($subtitle) . '</p>',
        $dashboard_content
    );
    
    // Save the main dashboard HTML
    $dashboard_file = $temp_dir . '/index.html';
    error_log("Saving dashboard to: $dashboard_file");
    
    $bytes_written = file_put_contents($dashboard_file, $dashboard_content);
    if ($bytes_written === false) {
        error_log("Failed to write dashboard file");
        throw new Exception('Failed to write dashboard file');
    }
    
    error_log("Dashboard file written successfully, $bytes_written bytes");
    $files[] = ['local' => $dashboard_file, 'remote' => 'index.html'];
    
    // Generate data JSON files that the dashboard will load
    error_log("Generating JSON data files...");
    $data_files = generateJSONData($event_id, $temp_dir, $pdo, true);
    error_log("Generated " . count($data_files) . " JSON data files");
    $files = array_merge($files, $data_files);
    
    // Include participant photos only if requested
    if ($data_types === null || in_array('participant_photos', $data_types)) {
        error_log("Copying participant photos...");
        $photo_files = copyParticipantPhotos($event_id, $temp_dir, $pdo);
        error_log("Copied " . count($photo_files) . " photo files");
        $files = array_merge($files, $photo_files);
    } else {
        error_log("Skipping participant photos (not in data_types)");
    }
    
    // Include asset files (CSS, logos, etc.)
    error_log("Copying asset files...");
    $asset_files = copyAssetFiles($temp_dir);
    error_log("Copied " . count($asset_files) . " asset files");
    $files = array_merge($files, $asset_files);
    
    error_log("Total files generated: " . count($files));
    return $files;
}

/**
 * Generate a set of static JSON files that mimic the responses of the dynamic
 * endpoints used by public_event_dashboard.php so the remote site can run fully
 * static without hitting PHP APIs.
 *
 * Files produced (all at publish root):
 *  - events_overview.json              (GET ?action=get_events_overview)
 *  - heats.json                       (GET ?action=get_event_heats&event_id=...)
 *  - configurations.json              (GET ?action=get_configurations&event_id=...)
 *  - active_heats.json                (GET ?action=get_active_heat_details&event_id=...)
 *  - heat_rows.json                   (GET ?action=get_heat_rows&event_id=...)
 *  - participants_by_bib.json         (GET ?action=get_participant_by_bib&event_id=&bib_number=...)
 *  - latest_updates.json              (GET ?action=get_event_latest_updates&event_id=...)
 *  - summary_table.json               (summary_table_api.php?event_id=&format=json...)
 *  - start_list.json                  (start_list_api.php?event_id=...)
 */
function generateStaticAPIData($event_id, $temp_dir, $pdo) {
    $files = [];
    // 1. events_overview.json (single event list – could be extended for multi-event later)
    $overview_stmt = $pdo->prepare("SELECT id, name, location, start_date, end_date FROM events WHERE id = ?");
    $overview_stmt->execute([$event_id]);
    $event = $overview_stmt->fetch(PDO::FETCH_ASSOC);
    $events_payload = ['success' => true, 'events' => $event ? [ $event ] : []];
    $file = $temp_dir . '/events_overview.json';
    file_put_contents($file, json_encode($events_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'events_overview.json'];

    // 2. heats.json – reuse getHeatsData structure
    try {
        $heats_data = getHeatsData($event_id, $pdo); // returns ['success'=>true,'heats'=>...]
    } catch (Exception $e) {
        $heats_data = ['success'=>false,'message'=>$e->getMessage(),'heats'=>[]];
    }
    $file = $temp_dir . '/heats.json';
    file_put_contents($file, json_encode($heats_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'heats.json'];

    // 3. configurations.json – minimal fields
    $config_stmt = $pdo->prepare("SELECT id, name, view_type, heat_number, category, status, created_at FROM result_configurations WHERE event_id = ? AND status = 'active' ORDER BY created_at DESC");
    $config_stmt->execute([$event_id]);
    $configs = $config_stmt->fetchAll(PDO::FETCH_ASSOC);
    $configs_payload = ['success'=>true,'configurations'=>$configs];
    $file = $temp_dir . '/configurations.json';
    file_put_contents($file, json_encode($configs_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'configurations.json'];

    // 4. active_heats.json – derive from event_heat_settings
    $active_stmt = $pdo->prepare("SELECT heat_number, heat_name, scoring_type, runs_count, is_active, bib_on_start, bib_latest_on_run, bib_performing FROM event_heat_settings WHERE event_id = ? ORDER BY heat_number");
    $active_stmt->execute([$event_id]);
    $active_rows = $active_stmt->fetchAll(PDO::FETCH_ASSOC);
    // index by heat_number for parity with dynamic endpoint
    $indexed = [];
    foreach ($active_rows as $row) { $indexed[$row['heat_number']] = $row; }
    $active_payload = ['success'=>true,'heats'=>$indexed];
    $file = $temp_dir . '/active_heats.json';
    file_put_contents($file, json_encode($active_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'active_heats.json'];

    // 5. heat_rows.json – simplified scoreboard rows: latest approved score per participant
    $rows_stmt = $pdo->prepare("SELECT ep.heat_number, ep.bib_number, p.first_name, p.last_name, p.country, p.club, MAX(s.score_value) as best_score FROM event_participants ep LEFT JOIN participants p ON ep.participant_id=p.id LEFT JOIN runs r ON ep.id = r.event_participant_id LEFT JOIN scores s ON r.id = s.run_id AND s.is_approved=1 WHERE ep.event_id=? GROUP BY ep.id ORDER BY ep.heat_number, ep.bib_number");
    $rows_stmt->execute([$event_id]);
    $rows = $rows_stmt->fetchAll(PDO::FETCH_ASSOC);
    $heat_rows_payload = ['success'=>true,'rows'=>$rows];
    $file = $temp_dir . '/heat_rows.json';
    file_put_contents($file, json_encode($heat_rows_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'heat_rows.json'];

    // 6. participants_by_bib.json – map for quick lookup
    $bib_stmt = $pdo->prepare("SELECT ep.bib_number, p.id as participant_id, p.first_name, p.last_name, p.photo, p.country, p.club FROM event_participants ep JOIN participants p ON ep.participant_id=p.id WHERE ep.event_id=?");
    $bib_stmt->execute([$event_id]);
    $map = [];
    while ($r = $bib_stmt->fetch(PDO::FETCH_ASSOC)) {
        if ($r['bib_number'] !== null) { 
            // Update photo filename to match copied format
            if (!empty($r['photo'])) {
                $photo_extension = pathinfo($r['photo'], PATHINFO_EXTENSION);
                $r['photo'] = 'photos/participant_' . $r['participant_id'] . '.' . $photo_extension;
            }
            unset($r['participant_id']); // Remove the temporary ID field
            $map[$r['bib_number']] = $r; 
        }
    }
    $bib_payload = ['success'=>true,'participants'=>$map];
    $file = $temp_dir . '/participants_by_bib.json';
    file_put_contents($file, json_encode($bib_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'participants_by_bib.json'];

    // 7. latest_updates.json – reuse existing helper if present else basic
    try {
        $latest = getLatestUpdatesData($event_id, $pdo);
    } catch (Exception $e) {
        $latest = ['success'=>false,'message'=>$e->getMessage()];
    }
    $file = $temp_dir . '/latest_updates.json';
    file_put_contents($file, json_encode($latest, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'latest_updates.json'];

    // 8. summary_table.json – basic ranking by best approved score (desc)
    $rank_stmt = $pdo->prepare("SELECT ep.bib_number, p.first_name, p.last_name, p.country, MAX(s.score_value) as best_score FROM event_participants ep JOIN participants p ON ep.participant_id = p.id LEFT JOIN runs r ON ep.id = r.event_participant_id LEFT JOIN scores s ON r.id = s.run_id AND s.is_approved=1 WHERE ep.event_id = ? GROUP BY ep.id ORDER BY best_score DESC NULLS LAST");
    $rank_stmt->execute([$event_id]);
    $rank_rows = $rank_stmt->fetchAll(PDO::FETCH_ASSOC);
    $rank = 1;
    foreach ($rank_rows as &$rr) { $rr['rank'] = $rr['best_score'] !== null ? $rank++ : null; }
    $summary_payload = ['success'=>true,'rows'=>$rank_rows];
    $file = $temp_dir . '/summary_table.json';
    file_put_contents($file, json_encode($summary_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'summary_table.json'];

    // 9. start_list.json – order by heat then bib
    $start_stmt = $pdo->prepare("SELECT ep.heat_number, ep.bib_number, p.first_name, p.last_name, p.country, p.club FROM event_participants ep JOIN participants p ON ep.participant_id=p.id WHERE ep.event_id=? ORDER BY ep.heat_number, ep.bib_number");
    $start_stmt->execute([$event_id]);
    $start_rows = $start_stmt->fetchAll(PDO::FETCH_ASSOC);
    $start_payload = ['success'=>true,'participants'=>$start_rows];
    $file = $temp_dir . '/start_list.json';
    file_put_contents($file, json_encode($start_payload, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'start_list.json'];

    return $files;
}

// Generate CSV Summary - wrapper for generateCSVData
function generateCSVSummary($event_id, $temp_dir, $pdo) {
    return generateCSVData($event_id, $temp_dir, $pdo);
}

// Generate CSV Start List
function generateCSVStartList($event_id, $temp_dir, $pdo) {
    $files = [];
    
    try {
        $csv_content = generateParticipantsCSV($event_id, $pdo);
        $csv_file = $temp_dir . '/start_list.csv';
        file_put_contents($csv_file, $csv_content);
        $files[] = ['local' => $csv_file, 'remote' => 'start_list.csv'];
        
        // Also generate start list by category
        $categories_stmt = $pdo->prepare("
            SELECT DISTINCT c.id, c.name 
            FROM categories c
            JOIN event_participants ep ON c.id = ep.category_id 
            WHERE ep.event_id = ?
            ORDER BY c.name
        ");
        $categories_stmt->execute([$event_id]);
        $categories = $categories_stmt->fetchAll(PDO::FETCH_ASSOC);
        
        foreach ($categories as $category) {
            $participants_stmt = $pdo->prepare("
                SELECT 
                    ep.bib_number,
                    p.name,
                    p.club,
                    ep.category_id,
                    c.name as category_name,
                    ep.heat_number,
                    ep.sort_order
                FROM event_participants ep
                LEFT JOIN participants p ON ep.participant_id = p.id
                LEFT JOIN categories c ON ep.category_id = c.id
                WHERE ep.event_id = ? AND ep.category_id = ?
                ORDER BY ep.heat_number, ep.sort_order
            ");
            $participants_stmt->execute([$event_id, $category['id']]);
            $participants = $participants_stmt->fetchAll(PDO::FETCH_ASSOC);
            
            if (!empty($participants)) {
                $csv_content = "Bib Number,Name,Club,Category,Heat Number,Sort Order\n";
                foreach ($participants as $participant) {
                    $csv_content .= sprintf(
                        "%s,%s,%s,%s,%s,%s\n",
                        escapeCsvField($participant['bib_number']),
                        escapeCsvField($participant['name']),
                        escapeCsvField($participant['club']),
                        escapeCsvField($participant['category_name']),
                        escapeCsvField($participant['heat_number']),
                        escapeCsvField($participant['sort_order'])
                    );
                }
                
                $filename = 'start_list_' . sanitizeFilename($category['name']) . '.csv';
                $csv_file = $temp_dir . '/' . $filename;
                file_put_contents($csv_file, $csv_content);
                $files[] = ['local' => $csv_file, 'remote' => $filename];
            }
        }
        
    } catch (Exception $e) {
        error_log("Error generating CSV start list: " . $e->getMessage());
    }
    
    return $files;
}

// Generate PDF Results
function generatePDFResults($event_id, $temp_dir, $pdo) {
    $files = [];
    
    // Generate summary table PDFs for each configuration (if table exists)
    $configs = [];
    try {
        $configs_stmt = $pdo->prepare("
            SELECT * FROM result_configurations 
            WHERE event_id = ? AND view_type = 'summary_table'
            ORDER BY name
        ");
        $configs_stmt->execute([$event_id]);
        $configs = $configs_stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        // result_configurations table doesn't exist, skip this data
        error_log("result_configurations table not found in PDF generator: " . $e->getMessage());
    }
    
    foreach ($configs as $config) {
        $pdf_content = generateSummaryPDF($config['id'], $pdo);
        $pdf_file = $temp_dir . '/results_' . sanitizeFilename($config['name']) . '.pdf';
        file_put_contents($pdf_file, $pdf_content);
        $files[] = ['local' => $pdf_file, 'remote' => 'results_' . sanitizeFilename($config['name']) . '.pdf'];
    }
    
    return $files;
}

// Generate CSV Data
function generateCSVData($event_id, $temp_dir, $pdo) {
    $files = [];
    
    // Generate summary table CSVs for each configuration (if table exists)
    $configs = [];
    try {
        $configs_stmt = $pdo->prepare("
            SELECT * FROM result_configurations 
            WHERE event_id = ? AND view_type = 'summary_table'
            ORDER BY name
        ");
        $configs_stmt->execute([$event_id]);
        $configs = $configs_stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        // result_configurations table doesn't exist, skip this data
        error_log("result_configurations table not found in CSV generator: " . $e->getMessage());
    }
    
    foreach ($configs as $config) {
        $csv_content = generateSummaryCSV($config['id'], $pdo);
        $csv_file = $temp_dir . '/results_' . sanitizeFilename($config['name']) . '.csv';
        file_put_contents($csv_file, $csv_content);
        $files[] = ['local' => $csv_file, 'remote' => 'results_' . sanitizeFilename($config['name']) . '.csv'];
    }
    
    // Generate participants CSV
    $participants_csv = generateParticipantsCSV($event_id, $pdo);
    $participants_file = $temp_dir . '/participants.csv';
    file_put_contents($participants_file, $participants_csv);
    $files[] = ['local' => $participants_file, 'remote' => 'participants.csv'];
    
    return $files;
}

// Data retrieval functions
function getEventOverviewData($event_id, $pdo) {
    $stmt = $pdo->prepare("SELECT * FROM events WHERE id = ?");
    $stmt->execute([$event_id]);
    $event = $stmt->fetch(PDO::FETCH_ASSOC);
    
    // Get participant count
    $count_stmt = $pdo->prepare("
        SELECT COUNT(*) as count 
        FROM event_participants ep 
        WHERE ep.event_id = ?
    ");
    $count_stmt->execute([$event_id]);
    $participant_count = $count_stmt->fetchColumn();
    
    // Get heat count
    $heat_stmt = $pdo->prepare("
        SELECT COUNT(DISTINCT ep.heat_number) as count 
        FROM event_participants ep 
        WHERE ep.event_id = ?
    ");
    $heat_stmt->execute([$event_id]);
    $heat_count = $heat_stmt->fetchColumn();
    
    // Get latest scores count
    $score_stmt = $pdo->prepare("
        SELECT COUNT(*) as count 
        FROM scores s
        JOIN runs r ON s.run_id = r.id
        JOIN event_participants ep ON r.event_participant_id = ep.id
        WHERE ep.event_id = ?
    ");
    $score_stmt->execute([$event_id]);
    $score_count = $score_stmt->fetchColumn();
    
    return [
        'event' => $event,
        'participant_count' => $participant_count,
        'heat_count' => $heat_count,
        'score_count' => $score_count,
        'generated_at' => date('c')
    ];
}

function getLatestUpdatesData($event_id, $pdo) {
    try {
        // Get latest score - simplified query
        $latest_stmt = $pdo->prepare("
            SELECT s.*, p.first_name, p.last_name, p.name, p.club, p.country, 
                   ep.category_id, ep.bib_number, ep.heat_number,
                   j.name as judge_name
            FROM scores s
            JOIN event_participants ep ON s.event_participant_id = ep.id
            JOIN participants p ON ep.participant_id = p.id
            LEFT JOIN judges j ON s.judge_id = j.id
            WHERE ep.event_id = ?
            ORDER BY s.created_at DESC
            LIMIT 1
        ");
        $latest_stmt->execute([$event_id]);
        $latest_score = $latest_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Get performing now - find participant with most recent score
        $performing_stmt = $pdo->prepare("
            SELECT p.*, ep.category_id, ep.bib_number, ep.heat_number, 'performing' as status,
                   s.created_at as last_score_time
            FROM participants p
            JOIN event_participants ep ON p.id = ep.participant_id
            JOIN scores s ON ep.id = s.event_participant_id
            WHERE ep.event_id = ? AND s.created_at >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)
            ORDER BY s.created_at DESC
            LIMIT 1
        ");
        $performing_stmt->execute([$event_id]);
        $performing_now = $performing_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Get next on start (simplified)
        $next_stmt = $pdo->prepare("
            SELECT p.*, ep.category_id, ep.bib_number, ep.heat_number, 'waiting' as status
            FROM participants p
            JOIN event_participants ep ON p.id = ep.participant_id
            WHERE ep.event_id = ? AND ep.id NOT IN (
                SELECT DISTINCT s.event_participant_id FROM scores s
                JOIN event_participants ep2 ON s.event_participant_id = ep2.id
                WHERE ep2.event_id = ?
            )
            ORDER BY ep.heat_number, ep.sort_order
            LIMIT 1
        ");
        $next_stmt->execute([$event_id, $event_id]);
        $next_on_start = $next_stmt->fetch(PDO::FETCH_ASSOC);
        
        return [
            'latest_score' => $latest_score,
            'performing_now' => $performing_now,
            'next_on_start' => $next_on_start,
            'generated_at' => date('c')
        ];
        
    } catch (Exception $e) {
        error_log("Error in getLatestUpdatesData: " . $e->getMessage());
        return [
            'latest_score' => null,
            'performing_now' => null,
            'next_on_start' => null,
            'generated_at' => date('c'),
            'error' => $e->getMessage()
        ];
    }
}

function getHeatsData($event_id, $pdo) {
    try {
        // Use exact structure from public_dashboard_api.php
        $stmt = $pdo->prepare("
            SELECT 
                hs.heat_number,
                hs.heat_name,
                hs.scoring_type,
                hs.runs_count,
                hs.is_active,
                hs.bib_on_start,
                CONCAT(p_start.first_name, ' ', p_start.last_name) as bib_start_name,
                hs.bib_latest_on_run,
                CONCAT(p_latest.first_name, ' ', p_latest.last_name) as bib_latest_name,
                hs.bib_performing,
                COUNT(DISTINCT ep.id) as participants,
                COUNT(DISTINCT CASE WHEN s.is_approved = 1 THEN s.id END) as total_scores
            FROM event_heat_settings hs
            LEFT JOIN event_participants ep_start ON (hs.event_id = ep_start.event_id AND hs.heat_number = ep_start.heat_number AND ep_start.bib_number = hs.bib_on_start)
            LEFT JOIN participants p_start ON ep_start.participant_id = p_start.id
            LEFT JOIN event_participants ep_latest ON (hs.event_id = ep_latest.event_id AND hs.heat_number = ep_latest.heat_number AND ep_latest.bib_number = hs.bib_latest_on_run)
            LEFT JOIN participants p_latest ON ep_latest.participant_id = p_latest.id
            LEFT JOIN event_participants ep ON hs.event_id = ep.event_id AND hs.heat_number = ep.heat_number
            LEFT JOIN runs r ON ep.id = r.event_participant_id
            LEFT JOIN scores s ON r.id = s.run_id
            WHERE hs.event_id = ?
            GROUP BY hs.heat_number, hs.heat_name, hs.scoring_type, hs.runs_count, hs.is_active, hs.bib_on_start, hs.bib_latest_on_run, hs.bib_performing, p_start.first_name, p_start.last_name, p_latest.first_name, p_latest.last_name
            ORDER BY hs.heat_number
        ");
        $stmt->execute([$event_id]);
        $heats = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Format data as key-value by heat_number like the API does
        $heats_data = [];
        foreach ($heats as $heat) {
            $heats_data[$heat['heat_number']] = $heat;
        }
        
        return [
            'success' => true,
            'heats' => $heats_data,
            'generated_at' => date('c')
        ];
        
    } catch (Exception $e) {
        error_log("Error in getHeatsData: " . $e->getMessage());
        // Return empty data structure as fallback
        return [
            'success' => false,
            'heats' => [],
            'generated_at' => date('c'),
            'error' => $e->getMessage()
        ];
    }
}

function getScoresData($event_id, $pdo) {
    try {
        // Use structure similar to public_dashboard_api.php latest score query
        $stmt = $pdo->prepare("
            SELECT 
                s.score_value, 
                r.run_number,
                r.id as run_id,
                p.first_name, 
                p.last_name, 
                p.club, 
                p.country, 
                p.photo,
                ep.bib_number, 
                ep.category_id,
                ep.heat_number,
                ec.category_name as category,
                ehs.heat_name
            FROM scores s
            JOIN runs r ON s.run_id = r.id
            JOIN event_participants ep ON r.event_participant_id = ep.id
            JOIN participants p ON ep.participant_id = p.id
            LEFT JOIN event_categories ec ON ep.category_id = ec.id
            LEFT JOIN event_heat_settings ehs ON ep.event_id = ehs.event_id AND ehs.heat_number = ep.heat_number
            WHERE ep.event_id = ?
            ORDER BY s.id DESC
        ");
        $stmt->execute([$event_id]);
        $scores = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Update photo filenames to match copied files
        foreach ($scores as &$score) {
            if (!empty($score['photo'])) {
                // Get the participant ID to match the copied filename format
                $participant_id = null;
                
                // Get participant ID for this participant
                $participant_stmt = $pdo->prepare("
                    SELECT p.id
                    FROM participants p
                    JOIN event_participants ep ON p.id = ep.participant_id
                    JOIN runs r ON ep.id = r.event_participant_id
                    WHERE r.id = ?
                ");
                $participant_stmt->execute([$score['run_id']]);
                $participant_result = $participant_stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($participant_result) {
                    // Update photo filename to match copied format: participant_ID.extension
                    $photo_extension = pathinfo($score['photo'], PATHINFO_EXTENSION);
                    $score['photo'] = 'participant_' . $participant_result['id'] . '.' . $photo_extension;
                    error_log("Updated photo filename for run {$score['run_id']}: participant_{$participant_result['id']}.$photo_extension");
                }
            }
        }
        
        return [
            'success' => true,
            'scores' => $scores,
            'generated_at' => date('c')
        ];
        
    } catch (Exception $e) {
        error_log("Error in getScoresData: " . $e->getMessage());
        
        // Fallback with minimal data structure
        return [
            'success' => false,
            'scores' => [],
            'generated_at' => date('c'),
            'error' => $e->getMessage()
        ];
    }
}

function getSummaryTableData($config_id, $pdo) {
    // This would integrate with your existing summary table API
    $summary_api_url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . 
                      dirname(dirname($_SERVER['REQUEST_URI'])) . '/api/summary_table_api.php?config_id=' . $config_id . '&format=json';
    
    $json_content = @file_get_contents($summary_api_url);
    if ($json_content === false) {
        return ['error' => 'Could not fetch summary table data'];
    }
    
    return json_decode($json_content, true) ?: ['error' => 'Invalid JSON data'];
}

// HTML generation functions
function generateDashboardHTML($event, $pdo) {
    $event_name = htmlspecialchars($event['name']);
    
    return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Results - {$event_name}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
    <link href="dashboard.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-dark bg-dark sticky-top">
        <div class="container">
            <span class="navbar-brand">
                <i class="fas fa-trophy me-2"></i>
                Live Results - {$event_name}
            </span>
            <span class="navbar-text" id="lastUpdate">
                <i class="fas fa-clock me-1"></i>
                Loading...
            </span>
        </div>
    </nav>
    
    <div class="container-fluid py-4">
        <div id="dashboard-content">
            <div class="text-center py-5">
                <div class="spinner-border text-primary" role="status">
                    <span class="visually-hidden">Loading...</span>
                </div>
                <p class="mt-3">Loading live results...</p>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="dashboard.js"></script>
</body>
</html>
HTML;
}

function generateDashboardCSS($event, $pdo) {
    return <<<CSS
/* Published Dashboard Styles */
body {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.card {
    border: none;
    border-radius: 15px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.1);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.card:hover {
    transform: translateY(-5px);
    box-shadow: 0 20px 40px rgba(0,0,0,0.15);
}

.participant-photo {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid #fff;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.participant-photo-placeholder {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    font-size: 16px;
    border: 3px solid #fff;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.facts-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
    color: #333;
    font-size: 12px;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.facts-icon:hover {
    transform: scale(1.1);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}

.badge {
    font-size: 0.85em;
    padding: 0.5em 0.75em;
}

.table {
    background: white;
    border-radius: 10px;
    overflow: hidden;
}

.table th {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border: none;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.9em;
}

.update-time {
    background: rgba(255,255,255,0.1);
    backdrop-filter: blur(10px);
    border-radius: 20px;
    padding: 0.5rem 1rem;
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

.loading {
    animation: pulse 2s infinite;
}

/* Responsive adjustments */
@media (max-width: 768px) {
    .participant-photo,
    .participant-photo-placeholder {
        width: 40px;
        height: 40px;
        font-size: 14px;
    }
    
    .container-fluid {
        padding: 1rem;
    }
}
CSS;
}

function generateDashboardJS($event, $pdo) {
    $event_id = $event['id'];
    
    return <<<JS
// Published Dashboard JavaScript
class LiveDashboard {
    constructor() {
        this.eventId = {$event_id};
        this.refreshInterval = 30000; // 30 seconds
        this.lastUpdate = null;
        
        this.init();
    }
    
    init() {
        this.loadData();
        this.startAutoRefresh();
        this.updateLastUpdateTime();
    }
    
    async loadData() {
        try {
            // Load combined data
            const response = await fetch('data_all.json?t=' + Date.now());
            const data = await response.json();
            
            if (data) {
                this.renderDashboard(data);
                this.lastUpdate = new Date();
                this.updateLastUpdateTime();
            }
        } catch (error) {
            console.error('Error loading data:', error);
            this.showError('Failed to load live data');
        }
    }
    
    renderDashboard(data) {
        const container = document.getElementById('dashboard-content');
        
        let html = `
            <div class="row mb-4">
                <div class="col-12">
                    <div class="card bg-primary text-white">
                        <div class="card-body">
                            <div class="row align-items-center">
                                <div class="col-md-8">
                                    <h2 class="mb-1">\${data.event.event.name}</h2>
                                    <p class="mb-0 opacity-75">Live Results Dashboard</p>
                                </div>
                                <div class="col-md-4 text-end">
                                    <div class="row g-2">
                                        <div class="col-4">
                                            <div class="text-center">
                                                <div class="h4 mb-0">\${data.event.participant_count}</div>
                                                <small>Athletes</small>
                                            </div>
                                        </div>
                                        <div class="col-4">
                                            <div class="text-center">
                                                <div class="h4 mb-0">\${data.event.heat_count}</div>
                                                <small>Heats</small>
                                            </div>
                                        </div>
                                        <div class="col-4">
                                            <div class="text-center">
                                                <div class="h4 mb-0">\${data.event.score_count}</div>
                                                <small>Scores</small>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        `;
        
        // Latest updates section
        if (data.latest) {
            html += this.renderLatestUpdates(data.latest);
        }
        
        // Heats section
        if (data.heats) {
            html += this.renderHeats(data.heats);
        }
        
        container.innerHTML = html;
    }
    
    renderLatestUpdates(latest) {
        let html = `
            <div class="row mb-4">
                <div class="col-12">
                    <h3 class="text-white mb-3">
                        <i class="fas fa-bolt me-2"></i>
                        Latest Updates
                    </h3>
                </div>
            </div>
            <div class="row g-3 mb-4">
        `;
        
        // Latest Score
        if (latest.latest_score) {
            const score = latest.latest_score;
            html += `
                <div class="col-md-6">
                    <div class="card bg-success bg-opacity-10">
                        <div class="card-body">
                            <h5 class="text-success mb-3">
                                <i class="fas fa-trophy me-2"></i>
                                Latest Score
                            </h5>
                            <div class="d-flex align-items-center">
                                <div class="me-3">
                                    \${this.createParticipantPhoto(score)}
                                </div>
                                <div class="flex-grow-1">
                                    <h6 class="mb-1">\${score.first_name} \${score.last_name}</h6>
                                    <div class="d-flex gap-2 mb-2">
                                        <span class="badge bg-primary">BIB \${score.bib_number}</span>
                                        \${score.country ? `<span class="badge bg-secondary">\${score.country}</span>` : ''}
                                    </div>
                                    <div class="h4 text-success">\${parseFloat(score.score_value).toFixed(2)}</div>
                                    <small class="text-muted">
                                        <i class="fas fa-clock me-1"></i>
                                        \${new Date(score.created_at).toLocaleTimeString()}
                                    </small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `;
        }
        
        // Next on Start
        if (latest.next_on_start) {
            const next = latest.next_on_start;
            html += `
                <div class="col-md-6">
                    <div class="card bg-primary bg-opacity-10">
                        <div class="card-body">
                            <h5 class="text-primary mb-3">
                                <i class="fas fa-flag me-2"></i>
                                Next on Start
                            </h5>
                            <div class="d-flex align-items-center">
                                <div class="me-3">
                                    \${this.createParticipantPhoto(next)}
                                </div>
                                <div class="flex-grow-1">
                                    <h6 class="mb-1">\${next.first_name} \${next.last_name}</h6>
                                    <div class="d-flex gap-2 mb-2">
                                        <span class="badge bg-primary">BIB \${next.bib_number}</span>
                                        \${next.country ? `<span class="badge bg-secondary">\${next.country}</span>` : ''}
                                    </div>
                                    <small class="text-primary">
                                        <i class="fas fa-arrow-right me-1"></i>
                                        Ready to compete
                                    </small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            `;
        }
        
        html += '</div>';
        return html;
    }
    
    renderHeats(heatsData) {
        let html = `
            <div class="row mb-4">
                <div class="col-12">
                    <h3 class="text-white mb-3">
                        <i class="fas fa-users me-2"></i>
                        Heat Progress
                    </h3>
                </div>
            </div>
            <div class="row g-3">
        `;
        
        heatsData.heats.forEach(heat => {
            const completion = heat.participants > 0 ? Math.round((heat.scored_participants / heat.participants) * 100) : 0;
            const progressClass = completion >= 100 ? 'bg-success' : (completion >= 50 ? 'bg-warning' : 'bg-info');
            
            html += `
                <div class="col-md-6 col-lg-4">
                    <div class="card">
                        <div class="card-body">
                            <div class="d-flex justify-content-between align-items-center mb-3">
                                <h6 class="mb-0">Heat \${heat.heat_number}</h6>
                                <span class="badge \${completion >= 100 ? 'bg-success' : 'bg-secondary'}">
                                    \${completion >= 100 ? 'Complete' : 'In Progress'}
                                </span>
                            </div>
                            <div class="mb-2">
                                <div class="d-flex justify-content-between small text-muted mb-1">
                                    <span>Progress</span>
                                    <span>\${heat.scored_participants}/\${heat.participants}</span>
                                </div>
                                <div class="progress" style="height: 8px;">
                                    <div class="progress-bar \${progressClass}" style="width: \${completion}%"></div>
                                </div>
                            </div>
                            <div class="text-center small text-muted">
                                \${completion}% Complete
                            </div>
                        </div>
                    </div>
                </div>
            `;
        });
        
        html += '</div>';
        return html;
    }
    
    createParticipantPhoto(participant) {
        if (participant.photo && participant.photo.trim() !== '') {
            return `<img src="\${participant.photo}" alt="\${participant.first_name} \${participant.last_name}" class="participant-photo" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
                    <div class="participant-photo-placeholder" style="display:none;">\${(participant.first_name?.charAt(0) + participant.last_name?.charAt(0)).toUpperCase()}</div>`;
        } else {
            const initials = ((participant.first_name?.charAt(0) || '') + (participant.last_name?.charAt(0) || '')).toUpperCase() || '??';
            return `<div class="participant-photo-placeholder">\${initials}</div>`;
        }
    }
    
    startAutoRefresh() {
        setInterval(() => {
            this.loadData();
        }, this.refreshInterval);
    }
    
    updateLastUpdateTime() {
        const element = document.getElementById('lastUpdate');
        if (element && this.lastUpdate) {
            element.innerHTML = `
                <i class="fas fa-clock me-1"></i>
                Last updated: \${this.lastUpdate.toLocaleTimeString()}
            `;
        }
    }
    
    showError(message) {
        const container = document.getElementById('dashboard-content');
        container.innerHTML = `
            <div class="alert alert-danger">
                <h4><i class="fas fa-exclamation-triangle me-2"></i>Error</h4>
                <p>\${message}</p>
                <button class="btn btn-outline-danger" onclick="location.reload()">
                    <i class="fas fa-refresh me-2"></i>Reload
                </button>
            </div>
        `;
    }
}

// Initialize dashboard when page loads
document.addEventListener('DOMContentLoaded', function() {
    new LiveDashboard();
});
JS;
}

// PDF and CSV generation functions
function generateSummaryPDF($config_id, $pdo) {
    // This would integrate with a PDF library like TCPDF or mPDF
    // For now, return a placeholder
    return "PDF content for config {$config_id} would be generated here";
}

function generateSummaryCSV($config_id, $pdo) {
    $data = getSummaryTableData($config_id, $pdo);
    
    if (isset($data['error'])) {
        return "Error," . $data['error'] . "\n";
    }
    
    $csv = '';
    
    // Add headers if available
    if (isset($data['participants']) && !empty($data['participants'])) {
        $first_participant = $data['participants'][0];
        $headers = array_keys($first_participant);
        $csv .= implode(',', array_map(function($h) { return '"' . str_replace('"', '""', $h) . '"'; }, $headers)) . "\n";
        
        // Add data rows
        foreach ($data['participants'] as $participant) {
            $row = array_map(function($v) { return '"' . str_replace('"', '""', $v) . '"'; }, array_values($participant));
            $csv .= implode(',', $row) . "\n";
        }
    }
    
    return $csv;
}

function generateParticipantsCSV($event_id, $pdo) {
    $stmt = $pdo->prepare("
        SELECT ep.bib_number, p.first_name, p.last_name, p.club, p.country, ep.category, ep.heat_number
        FROM participants p
        JOIN event_participants ep ON p.id = ep.participant_id
        WHERE ep.event_id = ? 
        ORDER BY ep.heat_number, ep.sort_order
    ");
    $stmt->execute([$event_id]);
    $participants = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    $csv = "BIB,First Name,Last Name,Club,Country,Category,Heat\n";
    
    foreach ($participants as $participant) {
        $row = [
            $participant['bib_number'],
            $participant['first_name'],
            $participant['last_name'],
            $participant['club'],
            $participant['country'],
            $participant['category'],
            $participant['heat_number']
        ];
        $csv .= implode(',', array_map(function($v) { return '"' . str_replace('"', '""', $v) . '"'; }, $row)) . "\n";
    }
    
    return $csv;
}

function escapeCsvField($field) {
    // Escape CSV field - wrap in quotes if contains comma, quote, or newline
    if (strpos($field, ',') !== false || strpos($field, '"') !== false || strpos($field, "\n") !== false) {
        return '"' . str_replace('"', '""', $field) . '"';
    }
    return $field;
}

function sanitizeFilename($filename) {
    return preg_replace('/[^a-zA-Z0-9_-]/', '_', $filename);
}

function copyParticipantPhotos($event_id, $temp_dir, $pdo) {
    $files = [];
    
    // Create photos directory in temp
    $photos_dir = $temp_dir . '/photos';
    if (!file_exists($photos_dir)) {
        mkdir($photos_dir, 0755, true);
    }
    
    error_log("=== PHOTO COPY DEBUG START ===");
    error_log("Copying participant photos for event $event_id");
    error_log("Target photos directory: $photos_dir");
    error_log("Photos directory exists: " . (file_exists($photos_dir) ? 'YES' : 'NO'));
    
    // Get all participants for this event
    $stmt = $pdo->prepare("
        SELECT p.id, p.photo, p.first_name, p.last_name, ep.bib_number
        FROM participants p
        INNER JOIN event_participants ep ON p.id = ep.participant_id
        WHERE ep.event_id = ? AND p.photo IS NOT NULL AND p.photo != ''
    ");
    $stmt->execute([$event_id]);
    $participants = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    error_log("Found " . count($participants) . " participants with photos");
    
    if (count($participants) === 0) {
        error_log("No participants with photos found for event $event_id");
        error_log("=== PHOTO COPY DEBUG END (NO PHOTOS) ===");
        return $files;
    }
    
    $photos_copied = 0;
    foreach ($participants as $participant) {
        error_log("Processing participant: {$participant['first_name']} {$participant['last_name']} (ID: {$participant['id']})");
        error_log("Photo filename: '{$participant['photo']}'");
        
        // Smart path detection - check if photo already includes path structure
        $photo_filename = $participant['photo'];
        $possible_paths = [];
        
        // Case 1: Photo filename already includes full path (e.g., "uploads/profile_stills/f_7.png")
        if (strpos($photo_filename, 'uploads/') === 0) {
            error_log("Photo includes path structure: $photo_filename");
            $possible_paths[] = dirname(__DIR__) . '/' . $photo_filename;
            $possible_paths[] = 'C:\stylescore2025\htdocs\v2/' . $photo_filename;
            $possible_paths[] = dirname(dirname(__DIR__)) . '/v2/' . $photo_filename;
        } else {
            // Case 2: Photo is just filename (e.g., "f_7.png")
            error_log("Photo is filename only: $photo_filename");
            // Use the confirmed working path structure
            $possible_paths[] = 'C:\stylescore2025\htdocs\v2/uploads/profile_stills/' . $photo_filename;
            $possible_paths[] = dirname(__DIR__) . '/uploads/profile_stills/' . $photo_filename;
            $possible_paths[] = dirname(__DIR__) . '/uploads/' . $photo_filename;
            $possible_paths[] = dirname(__DIR__) . '/uploads/participants/' . $photo_filename;
        }
        
        // Additional fallback paths for edge cases
        $possible_paths[] = dirname(dirname(__DIR__)) . '/v2/uploads/profile_stills/' . $photo_filename;
        $possible_paths[] = '../uploads/profile_stills/' . $photo_filename;
        
        $photo_path = null;
        foreach ($possible_paths as $path) {
            error_log("Trying path: $path");
            $real_path = realpath($path);
            if ($real_path && file_exists($real_path) && is_readable($real_path)) {
                $photo_path = $real_path;
                error_log("SUCCESS: Found photo at: $path (resolved to: $real_path)");
                break;
            } else {
                error_log("Not found: $path" . ($real_path ? " (realpath: $real_path)" : " (realpath failed)"));
            }
        }
        
        if ($photo_path) {
            $photo_extension = pathinfo($participant['photo'], PATHINFO_EXTENSION);
            $safe_filename = 'participant_' . $participant['id'] . '.' . $photo_extension;
            $temp_photo_path = $photos_dir . '/' . $safe_filename;
            
            error_log("Attempting to copy: $photo_path -> $temp_photo_path");
            
            if (copy($photo_path, $temp_photo_path)) {
                $files[] = [
                    'local' => $temp_photo_path,
                    'remote' => 'photos/' . $safe_filename
                ];
                $photos_copied++;
                error_log("SUCCESS: Copied photo: " . $participant['photo'] . " -> photos/" . $safe_filename);
            } else {
                error_log("ERROR: Failed to copy photo: $photo_path -> $temp_photo_path");
                $error = error_get_last();
                if ($error) {
                    error_log("Copy error details: " . $error['message']);
                }
            }
        } else {
            error_log("ERROR: Photo not found for participant " . $participant['id'] . ": " . $participant['photo']);
            error_log("Checked paths:");
            foreach ($possible_paths as $path) {
                error_log("  - $path");
            }
        }
        error_log("--- End participant {$participant['id']} ---");
    }
    
    error_log("SUMMARY: Copied $photos_copied out of " . count($participants) . " participant photos for event $event_id");
    error_log("Total files to upload: " . count($files));
    error_log("=== PHOTO COPY DEBUG END ===");
    return $files;
}

// Generate static HTML dashboard page that mimics public_event_dashboard.php
function generateStaticDashboardHTML($event, $event_id) {
    $event_name = htmlspecialchars($event['name']);
    $event_location = htmlspecialchars($event['location'] ?? '');
    $event_date = $event['date'] ?? '';
    
    return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Event Dashboard - {$event_name}</title>
    
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
    
    <style>
        /* Modern gradient background for event header */
        .bg-gradient-primary {
            background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
        }
        
        /* Navbar customizations */
        .navbar-brand {
            font-size: 1.5rem;
        }
        
        .navbar-nav .nav-link {
            border-radius: 0.375rem;
            margin: 0 0.125rem;
            transition: all 0.2s ease;
        }
        
        .navbar-nav .nav-link:hover {
            background-color: rgba(255, 255, 255, 0.1);
            transform: translateY(-1px);
        }
        
        /* Enhanced shadow for cards */
        .shadow-sm {
            box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.1) !important;
        }
        
        /* Modern rounded elements */
        .rounded-3 {
            border-radius: 0.75rem !important;
        }
        
        /* Optimize layout for two-column design */
        .event-head-card {
            min-height: 400px;
            position: sticky;
            top: 20px;
            z-index: 10;
        }
        
        /* Participant Profile Photo Styles */
        .participant-photo {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            object-fit: cover;
            border: 2px solid #fff;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            transition: transform 0.2s ease;
        }

        .participant-photo:hover {
            transform: scale(1.1);
        }

        .participant-photo-placeholder {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            font-size: 14px;
            border: 2px solid #fff;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        /* Apply consistent table styling */
        .table th {
            white-space: nowrap;
            position: sticky;
            top: 0;
            z-index: 10;
            font-weight: 600;
            font-size: .85em;
            text-transform: uppercase;
        }
        
        .table td {
            border-color: #dee2e6;
            font-size: 0.875rem;
            padding: 0.5rem;
        }
        
        .data-table th, .data-table td {
            padding: 0.5em !important;
        }
        
        /* Table responsive with max height */
        .table-responsive {
            overflow-y: auto;
            border: none;
        }
        
        /* Summary table row movement indicators */
        .summary-table tbody tr.moved-up td {
            animation: flashUp 1.2s ease;
        }
        
        .summary-table tbody tr.moved-down td {
            animation: flashDown 1.2s ease;
        }
        
        .summary-table tbody tr.changed td {
            animation: flashChange 2.4s ease;
        }
        
        @keyframes flashUp {
            0% { background-color: rgba(40, 167, 69, 0.25); }
            100% { background-color: transparent; }
        }
        @keyframes flashDown {
            0% { background-color: rgba(220, 53, 69, 0.25); }
            100% { background-color: transparent; }
        }
        @keyframes flashChange {
            0% { background-color: rgba(255, 193, 7, 0.25); }
            25% { background-color: transparent; }
            50% { background-color: rgba(255, 193, 7, 0.25); }
            100% { background-color: transparent; }
        }
        
        .fullscreen .navbar, 
        .fullscreen .event-head-card, 
        .fullscreen .card-general-reports {
            display: none;
        }
        
        .navbar-brand-logo img {
            height: 35px;
        }
        
        .bg-aurora-bright {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }
    </style>
</head>

<body class="bg-aurora-bright">
    <!-- Sticky Navigation Bar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top shadow-sm">
        <div class="container">
            <a class="navbar-brand navbar-brand-logo" href="#">
                <img src="assets/logo-ss-_2.svg" alt="Logo" class="me-2" onerror="this.style.display='none';">
                StyleScore Live
            </a>
            
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav me-auto">
                    <li class="nav-item">
                        <a class="nav-link active" href="#">
                            <i class="fas fa-chart-line me-1"></i>
                            Live Dashboard
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="#" onclick="toggleDashboardFullscreen()">
                            <i class="fas fa-expand me-1"></i>
                            Fullscreen
                        </a>
                    </li>
                </ul>
                
                <ul class="navbar-nav">
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle" href="#" id="refreshDropdown" role="button" data-bs-toggle="dropdown">
                            <i class="fas fa-cog me-1"></i>
                            Settings
                        </a>
                        <ul class="dropdown-menu dropdown-menu-end">
                            <li><h6 class="dropdown-header">Auto-Refresh</h6></li>
                            <li>
                                <div class="dropdown-item-text">
                                    <div class="form-check form-switch">
                                        <input class="form-check-input" type="checkbox" id="autoRefreshToggle" checked>
                                        <label class="form-check-label" for="autoRefreshToggle">
                                            Enable Auto-Refresh
                                        </label>
                                    </div>
                                </div>
                            </li>
                            <li><hr class="dropdown-divider"></li>
                            <li>
                                <div class="dropdown-item-text">
                                    <label for="refreshIntervalSelect" class="form-label small">Refresh Interval:</label>
                                    <select class="form-select form-select-sm" id="refreshIntervalSelect">
                                        <option value="5000">5 seconds</option>
                                        <option value="10000">10 seconds</option>
                                        <option value="30000" selected>30 seconds</option>
                                        <option value="60000">1 minute</option>
                                        <option value="300000">5 minutes</option>
                                    </select>
                                </div>
                            </li>
                        </ul>
                    </li>
                    <li class="nav-item">
                        <span class="navbar-text small" id="refreshIndicator">
                            <i class="fas fa-sync-alt text-success me-1"></i>
                            <span class="text-success">Auto-refresh: ON</span>
                        </span>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
    
    <!-- Toast container for notifications -->
    <div id="toast-container" class="position-fixed top-0 end-0 p-3" style="z-index: 9999;">
        <!-- Toast notifications will be dynamically added here -->
    </div>
    
    <!-- Main content with top padding to account for sticky navbar -->
    <div style="padding-top: 20px;"></div>

    <div class="container container-StyleScore p-4">
        <div class="row mb-3">
            <div class="col-12">
                <div class="border-0 shadow-none card text-white bg-transparent">
                    <div class="border-0 shadow-none card-body p-3">
                        <div class="d-flex justify-content-between align-items-center">
                            <div>
                                <h4 class="card-title mb-1"><i class="fas fa-tv me-2"></i>Live Event Dashboard</h4>
                                <p class="card-text mb-0 small opacity-75">Real-time event monitoring and results</p>
                            </div>
                            <div class="d-flex align-items-center gap-2">
                                <div class="d-flex align-items-center gap-2">
                                    <label for="refreshIntervalSelectMain" class="form-label mb-0 small text-white">Refresh every</label>
                                    <select id="refreshIntervalSelectMain" class="form-select form-select-sm" style="width: auto;">
                                        <option value="5000">5s</option>
                                        <option value="10000">10s</option>
                                        <option value="30000" selected>30s</option>
                                        <option value="60000">1 min</option>
                                        <option value="300000">5 min</option>
                                    </select>
                                </div>
                                <div class="btn-group" role="group">
                                    <button type="button" class="btn btn-outline-light btn-sm" id="toggleRefresh">
                                        <i class="fas fa-pause me-1"></i>Pause Updates
                                    </button>
                                    <button type="button" class="btn btn-outline-light btn-sm" onclick="runRefreshCycle()">
                                        <i class="fas fa-sync-alt me-1"></i>Refresh Now
                                    </button>
                                    <button type="button" class="btn btn-outline-light btn-sm" onclick="toggleDashboardFullscreen()">
                                        <i class="fas fa-expand me-1"></i>Fullscreen
                                    </button>
                                </div>
                                <small class="text-white">Last updated: <span id="lastUpdate">Loading...</span></small>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div id="eventsContainer">
            <div class="col-12 text-center py-5">
                <div class="spinner-border text-light" role="status"><span class="visually-hidden">Loading events...</span></div>
                <p class="mt-3 text-white">Loading events...</p>
            </div>
        </div>

        <div class="modal fade" id="summaryTableModal" tabindex="-1">
            <div class="modal-dialog modal-xl">
                <div class="modal-content">
                    <div class="modal-header py-2">
                        <h6 class="modal-title" id="modalSummaryTitle">Summary Table</h6>
                        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
                    </div>
                    <div class="modal-body p-2">
                        <div class="summary-display" id="summaryTableDisplay"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Scripts -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="dashboard.js"></script>
</body>
</html>
HTML;
}

// Generate all static API files that match the public dashboard endpoints
function generateStaticAPIFiles($event_id, $temp_dir, $pdo) {
    $files = [];
    
    // Create api directory
    $api_dir = $temp_dir . '/api';
    if (!file_exists($api_dir)) {
        mkdir($api_dir, 0755, true);
    }
    
    // 1. Events overview endpoint (GET ?action=get_events_overview)
    $events_data = generateEventsOverviewAPI($event_id, $pdo);
    $file = $api_dir . '/events_overview.json';
    file_put_contents($file, json_encode($events_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'api/events_overview.json'];
    
    // 2. Event heats endpoint (GET ?action=get_event_heats&event_id=...)
    $heats_data = generateEventHeatsAPI($event_id, $pdo);
    $file = $api_dir . '/event_heats.json';
    file_put_contents($file, json_encode($heats_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'api/event_heats.json'];
    
    // 3. Configurations endpoint (GET ?action=get_configurations&event_id=...)
    $configs_data = generateConfigurationsAPI($event_id, $pdo);
    $file = $api_dir . '/configurations.json';
    file_put_contents($file, json_encode($configs_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'api/configurations.json'];
    
    // 4. Latest updates endpoint (GET ?action=get_event_latest_updates&event_id=...)
    $latest_data = generateLatestUpdatesAPI($event_id, $pdo);
    $file = $api_dir . '/latest_updates.json';
    file_put_contents($file, json_encode($latest_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'api/latest_updates.json'];
    
    // 5. Active heat details endpoint (GET ?action=get_active_heat_details&event_id=...)
    $active_data = generateActiveHeatDetailsAPI($event_id, $pdo);
    $file = $api_dir . '/active_heat_details.json';
    file_put_contents($file, json_encode($active_data, JSON_PRETTY_PRINT));
    $files[] = ['local' => $file, 'remote' => 'api/active_heat_details.json'];
    
    // 6. Summary table endpoints (POST load_summary_table for each config)
    $summary_files = generateSummaryTableAPIs($event_id, $api_dir, $pdo);
    $files = array_merge($files, $summary_files);
    
    // 7. Start list endpoints (POST load_start_list)
    $startlist_files = generateStartListAPIs($event_id, $api_dir, $pdo);
    $files = array_merge($files, $startlist_files);
    
    // 8. Main static API dispatcher
    $dispatcher_js = generateStaticAPIDispatcher($event_id);
    $file = $temp_dir . '/dashboard.js';
    file_put_contents($file, $dispatcher_js);
    $files[] = ['local' => $file, 'remote' => 'dashboard.js'];
    
    return $files;
}

function copyStaticAssetFiles($temp_dir) {
    $files = [];
    
    // Copy CSS files if they exist
    $assets_dir = $temp_dir . '/assets';
    if (!file_exists($assets_dir)) {
        mkdir($assets_dir, 0755, true);
    }
    
    // Copy custom CSS if it exists - try multiple paths
    $possible_css_paths = [
        dirname(__DIR__) . '/assets/css/custom-dashboard.css',
        dirname(dirname(__DIR__)) . '/assets/css/custom-dashboard.css',
        '../assets/css/custom-dashboard.css',
        '../../assets/css/custom-dashboard.css'
    ];
    
    foreach ($possible_css_paths as $custom_css_path) {
        $real_path = realpath($custom_css_path);
        if ($real_path && file_exists($real_path)) {
            $temp_css_path = $assets_dir . '/custom-dashboard.css';
            if (copy($real_path, $temp_css_path)) {
                $files[] = [
                    'local' => $temp_css_path,
                    'remote' => 'assets/custom-dashboard.css'
                ];
                error_log("Copied CSS from: $custom_css_path");
            }
            break;
        }
    }
    
    // Copy logo if it exists - try multiple paths
    $possible_logo_paths = [
        dirname(__DIR__) . '/assets/img/SVG/logo-ss-_2.svg',
        dirname(dirname(__DIR__)) . '/assets/img/SVG/logo-ss-_2.svg',
        '../assets/img/SVG/logo-ss-_2.svg',
        '../../assets/img/SVG/logo-ss-_2.svg'
    ];
    
    foreach ($possible_logo_paths as $logo_path) {
        $real_path = realpath($logo_path);
        if ($real_path && file_exists($real_path)) {
            $temp_logo_path = $assets_dir . '/logo-ss-_2.svg';
            if (copy($real_path, $temp_logo_path)) {
                $files[] = [
                    'local' => $temp_logo_path,
                    'remote' => 'assets/logo-ss-_2.svg'
                ];
                error_log("Copied logo from: $logo_path");
            }
            break;
        }
    }
    
    return $files;
}

// Generate events overview API response
function generateEventsOverviewAPI($event_id, $pdo) {
    try {
        // Use the same query structure as public_dashboard_api.php
        $events_stmt = $pdo->prepare("
            SELECT 
                e.id, e.name, e.date, e.heats_total, e.runs_per_heat, e.status,
                ehs.heat_number as current_heat, ehs.active_run as current_run,
                COUNT(DISTINCT ep.id) as total_participants,
                COUNT(DISTINCT ja.judge_id) as total_judges
            FROM events e
            LEFT JOIN event_participants ep ON e.id = ep.event_id
            LEFT JOIN judge_assignments ja ON e.id = ja.event_id
            LEFT JOIN event_heat_settings ehs ON e.id = ehs.event_id AND ehs.is_active = 1
            WHERE e.id = ?
            GROUP BY e.id, e.name, e.date, e.heats_total, e.runs_per_heat, e.status, ehs.heat_number, ehs.active_run
        ");
        $events_stmt->execute([$event_id]);
        $event = $events_stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$event) {
            return ['success' => false, 'message' => 'Event not found', 'events' => []];
        }
        
        // Get latest score data
        $latest_score_stmt = $pdo->prepare("
            SELECT s.score_value, s.created_at, s.run_id,
                   p.first_name, p.last_name, p.club, p.country, p.photo,
                   ep.bib_number, ep.category_id,
                   ec.category_name as category,
                   r.run_number,
                   ehs.heat_name, ehs.heat_number
            FROM scores s
            JOIN runs r ON s.run_id = r.id
            JOIN event_participants ep ON r.event_participant_id = ep.id
            JOIN participants p ON ep.participant_id = p.id
            LEFT JOIN event_categories ec ON ep.category_id = ec.id
            LEFT JOIN event_heat_settings ehs ON ep.event_id = ehs.event_id AND ehs.heat_number = ep.heat_number
            WHERE ep.event_id = ? AND s.is_approved = 1
            ORDER BY s.created_at DESC
            LIMIT 1
        ");
        $latest_score_stmt->execute([$event_id]);
        $latest_score = $latest_score_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Calculate ranking if latest score exists
        if ($latest_score) {
            // Update photo filename to match copied format
            if (!empty($latest_score['photo'])) {
                // Get participant ID for photo filename correction
                $participant_id_stmt = $pdo->prepare("
                    SELECT p.id FROM participants p 
                    JOIN event_participants ep ON p.id = ep.participant_id 
                    JOIN runs r ON ep.id = r.event_participant_id 
                    WHERE r.id = ?
                ");
                $participant_id_stmt->execute([$latest_score['run_id']]);
                $participant_id_result = $participant_id_stmt->fetch(PDO::FETCH_ASSOC);
                if ($participant_id_result) {
                    $photo_extension = pathinfo($latest_score['photo'], PATHINFO_EXTENSION);
                    $latest_score['photo'] = 'photos/participant_' . $participant_id_result['id'] . '.' . $photo_extension;
                }
            }
            
            $rank_stmt = $pdo->prepare("
                SELECT COUNT(*) + 1 as rank
                FROM scores s2 
                JOIN runs r2 ON r2.id = s2.run_id 
                JOIN event_participants ep2 ON ep2.id = r2.event_participant_id 
                WHERE ep2.event_id = ? 
                AND s2.is_approved = 1 
                AND s2.score_value > ?
            ");
            $rank_stmt->execute([$event_id, $latest_score['score_value']]);
            $rank_result = $rank_stmt->fetch(PDO::FETCH_ASSOC);
            $latest_score['current_rank'] = $rank_result['rank'];
        }
        
        $event['latest_score'] = $latest_score ?: null;
        
        // Get next BIB data
        $judge_count_stmt = $pdo->prepare("SELECT COUNT(DISTINCT judge_id) FROM judge_assignments WHERE event_id = ?");
        $judge_count_stmt->execute([$event_id]);
        $judge_count = (int)$judge_count_stmt->fetchColumn();

        $next_bib_stmt = $pdo->prepare("
            SELECT ep.bib_number, ep.category_id, p.id as participant_id,
                   p.first_name, p.last_name, p.club, p.country, p.photo,
                   ec.category_name as category,
                   ehs.heat_name
            FROM event_participants ep
            JOIN participants p ON ep.participant_id = p.id
            LEFT JOIN event_categories ec ON ep.category_id = ec.id
            LEFT JOIN event_heat_settings ehs ON ep.event_id = ehs.event_id AND ehs.heat_number = ep.heat_number
            WHERE ep.event_id = ? AND ep.heat_number = ?
            AND ep.id NOT IN (
                SELECT DISTINCT r.event_participant_id
                FROM runs r
                JOIN scores s ON r.id = s.run_id
                WHERE r.run_number = ?
                GROUP BY r.event_participant_id
                HAVING COUNT(s.id) >= ?
            )
            ORDER BY ep.bib_number
            LIMIT 1
        ");
        $next_bib_stmt->execute([
            $event_id,
            $event['current_heat'] ?? 1,
            $event['current_run'] ?? 1,
            $judge_count
        ]);
        $next_bib_data = $next_bib_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Update photo filename for next_bib data
        if ($next_bib_data && !empty($next_bib_data['photo'])) {
            $photo_extension = pathinfo($next_bib_data['photo'], PATHINFO_EXTENSION);
            $next_bib_data['photo'] = 'photos/participant_' . $next_bib_data['participant_id'] . '.' . $photo_extension;
            unset($next_bib_data['participant_id']); // Remove the temporary ID field
        }
        
        $event['next_bib'] = $next_bib_data ?: null;
        
        return ['success' => true, 'events' => [$event]];
        
    } catch (Exception $e) {
        error_log("Error in generateEventsOverviewAPI: " . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage(), 'events' => []];
    }
}

// Generate event heats API response
function generateEventHeatsAPI($event_id, $pdo) {
    try {
        // Get category names for all categories in this event
        $categories_stmt = $pdo->prepare("
            SELECT id, category_name FROM event_categories 
            WHERE event_id = ?
        ");
        $categories_stmt->execute([$event_id]);
        $category_map = [];
        while ($cat = $categories_stmt->fetch(PDO::FETCH_ASSOC)) {
            $category_map[$cat['id']] = $cat['category_name'];
        }
        
        // Get judge count
        $judges_count_stmt = $pdo->prepare("SELECT COUNT(*) FROM judge_assignments WHERE event_id = ?");
        $judges_count_stmt->execute([$event_id]);
        $judges_count = (int)$judges_count_stmt->fetchColumn();

        // Get heats data
        $heats_stmt = $pdo->prepare("
            SELECT 
                ehs.id, ehs.event_id, ehs.heat_number, ehs.heat_name, 
                ehs.scoring_type, ehs.runs_count, ehs.categories, ehs.time_start,
                ehs.flow_type, ehs.flow_source_heat, ehs.flow_participants_per_category,
                ehs.is_active, ehs.active_run, ehs.bib_on_start, ehs.bib_latest_on_run,
                COUNT(DISTINCT ep.id) as participants,
                COUNT(DISTINCT CASE WHEN s.is_approved = 1 THEN s.id END) as total_scores,
                (COUNT(DISTINCT ep.id) * ehs.runs_count * ?) as expected_scores
            FROM event_heat_settings ehs
            LEFT JOIN event_participants ep ON ehs.event_id = ep.event_id AND ehs.heat_number = ep.heat_number
            LEFT JOIN runs r ON ep.id = r.event_participant_id
            LEFT JOIN scores s ON r.id = s.run_id
            WHERE ehs.event_id = ?
            GROUP BY ehs.id, ehs.event_id, ehs.heat_number, ehs.heat_name, 
                     ehs.scoring_type, ehs.runs_count, ehs.time_start, ehs.categories,
                     ehs.flow_type, ehs.flow_source_heat, ehs.flow_participants_per_category,
                     ehs.is_active, ehs.active_run, ehs.bib_on_start, ehs.bib_latest_on_run
            ORDER BY ehs.heat_number
        ");
        $heats_stmt->execute([$judges_count, $event_id]);
        $heats = $heats_stmt->fetchAll(PDO::FETCH_ASSOC);

        // Add category names to each heat
        foreach ($heats as &$heat) {
            if (!empty($heat['categories'])) {
                try {
                    $category_ids = json_decode($heat['categories'], true);
                    if (is_array($category_ids)) {
                        $category_names = [];
                        foreach ($category_ids as $id) {
                            if (isset($category_map[$id])) {
                                $category_names[] = $category_map[$id];
                            } else {
                                $category_names[] = "ID: $id";
                            }
                        }
                        $heat['category_names'] = $category_names;
                    }
                } catch (Exception $e) {
                    // Keep original categories if there's an error
                }
            }
        }
        
        // Get configurations
        $configs_stmt = $pdo->prepare("
            SELECT id, name, view_type, heat_number, category, display_order
            FROM result_configurations
            WHERE event_id = ? AND status = 'active'
            ORDER BY heat_number, display_order, name
        ");
        $configs_stmt->execute([$event_id]);
        $configurations = $configs_stmt->fetchAll(PDO::FETCH_ASSOC);

        return ['success' => true, 'heats' => $heats, 'configurations' => $configurations];
        
    } catch (Exception $e) {
        error_log("Error in generateEventHeatsAPI: " . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage(), 'heats' => [], 'configurations' => []];
    }
}

// Generate configurations API response
function generateConfigurationsAPI($event_id, $pdo) {
    try {
        $configs_stmt = $pdo->prepare("
            SELECT id, name, view_type, heat_number, category, display_order
            FROM result_configurations
            WHERE event_id = ? AND status = 'active'
            ORDER BY display_order, name
        ");
        $configs_stmt->execute([$event_id]);
        $configurations = $configs_stmt->fetchAll(PDO::FETCH_ASSOC);

        return ['success' => true, 'configurations' => $configurations];
        
    } catch (Exception $e) {
        error_log("Error in generateConfigurationsAPI: " . $e->getMessage());
        
        // Provide default configurations if table doesn't exist
        $default_configurations = [
            [
                'id' => 1,
                'name' => 'Event Start List',
                'view_type' => 'start_list',
                'heat_number' => null,
                'category' => null,
                'display_order' => 1
            ],
            [
                'id' => 2,
                'name' => 'Event Results',
                'view_type' => 'summary_table',
                'heat_number' => null,
                'category' => null,
                'display_order' => 2
            ]
        ];
        
        return ['success' => true, 'configurations' => $default_configurations];
    }
}

// Generate latest updates API response
function generateLatestUpdatesAPI($event_id, $pdo) {
    try {
        // Get latest score (most recent approved score)
        $latest_score_stmt = $pdo->prepare("
            SELECT 
                ep.bib_number, 
                p.id as participant_id,
                p.first_name, 
                p.last_name,
                p.club, 
                p.country,
                p.photo,
                s.score_value, 
                s.created_at,
                ec.category_name as category,
                ehs.heat_name,
                ehs.heat_number,
                (
                    SELECT COUNT(*) + 1 
                    FROM scores s2 
                    JOIN runs r2 ON r2.id = s2.run_id 
                    JOIN event_participants ep2 ON ep2.id = r2.event_participant_id 
                    WHERE ep2.event_id = ep.event_id 
                    AND s2.is_approved = 1 
                    AND s2.score_value > s.score_value
                ) as current_rank
            FROM scores s
            JOIN runs r ON r.id = s.run_id
            JOIN event_participants ep ON ep.id = r.event_participant_id
            JOIN participants p ON p.id = ep.participant_id
            LEFT JOIN event_categories ec ON ec.id = ep.category_id
            LEFT JOIN event_heat_settings ehs ON ehs.event_id = ep.event_id AND ehs.heat_number = ep.heat_number
            WHERE ep.event_id = ? AND s.is_approved = 1
            ORDER BY s.created_at DESC
            LIMIT 1
        ");
        $latest_score_stmt->execute([$event_id]);
        $latest_score = $latest_score_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Update photo filename for latest_score
        if ($latest_score && !empty($latest_score['photo'])) {
            $photo_extension = pathinfo($latest_score['photo'], PATHINFO_EXTENSION);
            $latest_score['photo'] = 'photos/participant_' . $latest_score['participant_id'] . '.' . $photo_extension;
            unset($latest_score['participant_id']); // Remove the temporary ID field
        }

        // Get next participant on start
        $next_bib_stmt = $pdo->prepare("
            SELECT 
                ep.bib_number, 
                p.id as participant_id,
                p.first_name, 
                p.last_name,
                p.club, 
                p.country,
                p.photo,
                ec.category_name as category,
                ehs.heat_name,
                ehs.heat_number
            FROM event_participants ep
            JOIN participants p ON p.id = ep.participant_id
            LEFT JOIN event_categories ec ON ec.id = ep.category_id
            LEFT JOIN event_heat_settings ehs ON ehs.event_id = ep.event_id AND ehs.heat_number = ep.heat_number
            LEFT JOIN runs r ON r.event_participant_id = ep.id
            LEFT JOIN scores s ON s.run_id = r.id AND s.is_approved = 1
            WHERE ep.event_id = ? 
            AND ehs.is_active = 1
            AND s.id IS NULL
            ORDER BY ep.sort_order ASC, ep.bib_number ASC
            LIMIT 1
        ");
        $next_bib_stmt->execute([$event_id]);
        $next_bib = $next_bib_stmt->fetch(PDO::FETCH_ASSOC);
        
        // Update photo filename for next_bib
        if ($next_bib && !empty($next_bib['photo'])) {
            $photo_extension = pathinfo($next_bib['photo'], PATHINFO_EXTENSION);
            $next_bib['photo'] = 'photos/participant_' . $next_bib['participant_id'] . '.' . $photo_extension;
            unset($next_bib['participant_id']); // Remove the temporary ID field
        }

        return [
            'success' => true,
            'latest_score' => $latest_score ?: null,
            'next_bib' => $next_bib ?: null,
            'performing_now' => null,
            'heat_status' => null
        ];
        
    } catch (Exception $e) {
        error_log("Error in generateLatestUpdatesAPI: " . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage()];
    }
}

// Generate active heat details API response
function generateActiveHeatDetailsAPI($event_id, $pdo) {
    try {
        $heats_stmt = $pdo->prepare("
            SELECT 
                hs.heat_number, 
                hs.is_active, 
                hs.active_run, 
                hs.bib_on_start,
                CONCAT(p_start.first_name, ' ', p_start.last_name) as bib_start_name,
                hs.bib_latest_on_run,
                CONCAT(p_latest.first_name, ' ', p_latest.last_name) as bib_latest_name
            FROM event_heat_settings hs
            LEFT JOIN event_participants ep_start ON (hs.event_id = ep_start.event_id AND hs.heat_number = ep_start.heat_number AND ep_start.bib_number = hs.bib_on_start)
            LEFT JOIN participants p_start ON ep_start.participant_id = p_start.id
            LEFT JOIN event_participants ep_latest ON (hs.event_id = ep_latest.event_id AND hs.heat_number = ep_latest.heat_number AND ep_latest.bib_number = hs.bib_latest_on_run)
            LEFT JOIN participants p_latest ON ep_latest.participant_id = p_latest.id
            WHERE hs.event_id = ? AND hs.is_active = 1
        ");
        $heats_stmt->execute([$event_id]);
        $active_heats = $heats_stmt->fetchAll(PDO::FETCH_ASSOC);
        
        // Format data as key-value by heat_number
        $heats_data = [];
        foreach ($active_heats as $heat) {
            $heats_data[$heat['heat_number']] = $heat;
        }
        
        return [
            'success' => true,
            'heats' => $heats_data
        ];
        
    } catch (Exception $e) {
        error_log("Error in generateActiveHeatDetailsAPI: " . $e->getMessage());
        return ['success' => false, 'message' => $e->getMessage(), 'heats' => []];
    }
}

// Generate summary table API files for all configurations
function generateSummaryTableAPIs($event_id, $api_dir, $pdo) {
    $files = [];
    
    try {
        $configs_stmt = $pdo->prepare("
            SELECT * FROM result_configurations 
            WHERE event_id = ? AND view_type = 'summary_table' AND status = 'active'
            ORDER BY name
        ");
        $configs_stmt->execute([$event_id]);
        $configs = $configs_stmt->fetchAll(PDO::FETCH_ASSOC);
        
        foreach ($configs as $config) {
            // Generate summary table data by calling the existing API
            $summary_data = getSummaryTableData($config['id'], $pdo);
            
            $file = $api_dir . '/summary_table_' . $config['id'] . '.json';
            file_put_contents($file, json_encode($summary_data, JSON_PRETTY_PRINT));
            $files[] = ['local' => $file, 'remote' => 'api/summary_table_' . $config['id'] . '.json'];
        }
        
    } catch (Exception $e) {
        error_log("Error generating summary table APIs: " . $e->getMessage());
    }
    
    return $files;
}

// Generate start list API files
function generateStartListAPIs($event_id, $api_dir, $pdo) {
    $files = [];
    
    try {
        // Generate overall start list
        $participants_stmt = $pdo->prepare("
            SELECT 
                ep.bib_number, 
                p.first_name, 
                p.last_name,
                p.club, 
                p.country,
                p.photo,
                ec.category_name as category,
                ep.heat_number
            FROM event_participants ep
            JOIN participants p ON p.id = ep.participant_id
            LEFT JOIN event_categories ec ON ec.id = ep.category_id
            WHERE ep.event_id = ?
            ORDER BY ep.heat_number, ep.sort_order, ep.bib_number
        ");
        $participants_stmt->execute([$event_id]);
        $participants = $participants_stmt->fetchAll(PDO::FETCH_ASSOC);
        
        $start_list_data = [
            'success' => true,
            'data' => ['participants' => $participants],
            'heat_number' => null,
            'type' => 'start_list'
        ];
        
        $file = $api_dir . '/start_list_all.json';
        file_put_contents($file, json_encode($start_list_data, JSON_PRETTY_PRINT));
        $files[] = ['local' => $file, 'remote' => 'api/start_list_all.json'];
        
        // Generate start lists by heat
        $heats_stmt = $pdo->prepare("
            SELECT DISTINCT heat_number 
            FROM event_participants 
            WHERE event_id = ? 
            ORDER BY heat_number
        ");
        $heats_stmt->execute([$event_id]);
        $heats = $heats_stmt->fetchAll(PDO::FETCH_COLUMN);
        
        foreach ($heats as $heat_number) {
            $heat_participants = array_filter($participants, function($p) use ($heat_number) {
                return $p['heat_number'] == $heat_number;
            });
            
            $heat_start_list_data = [
                'success' => true,
                'data' => ['participants' => array_values($heat_participants)],
                'heat_number' => $heat_number,
                'type' => 'start_list'
            ];
            
            $file = $api_dir . '/start_list_heat_' . $heat_number . '.json';
            file_put_contents($file, json_encode($heat_start_list_data, JSON_PRETTY_PRINT));
            $files[] = ['local' => $file, 'remote' => 'api/start_list_heat_' . $heat_number . '.json'];
        }
        
    } catch (Exception $e) {
        error_log("Error generating start list APIs: " . $e->getMessage());
    }
    
    return $files;
}

// Generate the static API dispatcher JavaScript
function generateStaticAPIDispatcher($event_id) {
    return <<<JS
// Static Dashboard JavaScript for Event ID: {$event_id}
class StaticLiveDashboard {
    constructor() {
        this.eventId = {$event_id};
        this.refreshInterval = 30000; // 30 seconds
        this.lastUpdate = null;
        this.autoRefresh = true;
        this.refreshMs = 30000;
        this.activeReports = new Map();
        this.openHeatContainers = new Set();
        
        this.init();
    }
    
    init() {
        this.loadData();
        this.startAutoRefresh();
        this.updateLastUpdateTime();
        this.initializeControls();
    }
    
    initializeControls() {
        // Initialize refresh controls
        const refreshSelect = document.getElementById('refreshIntervalSelectMain');
        if (refreshSelect) {
            refreshSelect.addEventListener('change', (e) => {
                this.refreshMs = parseInt(e.target.value) || 30000;
                localStorage.setItem('dashboardRefreshMs', String(this.refreshMs));
                if (this.autoRefresh) {
                    this.startAutoRefresh();
                }
            });
        }
        
        // Initialize toggle refresh button
        const toggleBtn = document.getElementById('toggleRefresh');
        if (toggleBtn) {
            toggleBtn.addEventListener('click', () => this.toggleAutoRefresh());
        }
        
        // Add fullscreen support
        window.toggleDashboardFullscreen = () => this.toggleFullscreen();
        window.runRefreshCycle = () => this.loadData();
    }
    
    async loadData() {
        try {
            // Load events overview
            const eventsResponse = await fetch('api/events_overview.json?t=' + Date.now());
            const eventsData = await eventsResponse.json();
            
            if (eventsData.success && eventsData.events.length > 0) {
                await this.displayEvents(eventsData.events);
                this.lastUpdate = new Date();
                this.updateLastUpdateTime();
            } else {
                this.showError('No events found');
            }
        } catch (error) {
            console.error('Error loading data:', error);
            this.showError('Failed to load live data');
        }
    }
    
    async displayEvents(events) {
        const container = document.getElementById('eventsContainer');
        
        if (events.length === 0) {
            container.innerHTML = '<div class="col-12 py-2"><div class="alert alert-info">No active events found</div></div>';
            return;
        }

        let html = '';
        
        for (const event of events) {
            const isLive = ['in_progress','live'].includes(event.status);
            const nextBib = event.next_bib;
            
            // Auto-open heat containers for all events
            this.openHeatContainers.add(event.id);
            
            html += `
            <div class="row mb-3">
                <div class="col-4">
                    <div class="event-head-card card mb-0 border-0 shadow-sm bg-white" id="event-\${event.id}">
                        <div class="card-header bg-gradient-primary text-white border-0 position-relative overflow-hidden py-2">
                            <div class="position-absolute top-0 end-0 opacity-10" style="font-size: 4rem; line-height: 1; color: rgba(255,255,255,0.1);">
                                <i class="fas fa-trophy"></i>
                            </div>
                            <div class="row align-items-center position-relative">
                                <div class="col-md-8">
                                    <h5 class="mb-1 fw-bold">
                                        <i class="fas fa-calendar-check me-2"></i>
                                        \${event.name}
                                    </h5>
                                    <p class="mb-0 text-white-50 small">
                                        <i class="fas fa-calendar me-1"></i>
                                        \${new Date(event.date).toLocaleDateString('en-US', { 
                                            weekday: 'long', 
                                            year: 'numeric', 
                                            month: 'long', 
                                            day: 'numeric' 
                                        })}
                                    </p>
                                </div>
                                <div class="col-md-4 text-end">
                                    <div class="d-flex flex-column align-items-end gap-1">
                                        \${isLive ? '<span class="badge bg-danger px-2 py-1 rounded-pill"><i class="fas fa-broadcast-tower me-1"></i>LIVE EVENT</span>' : ''}
                                        <span class="badge bg-light text-dark px-2 py-1 rounded-pill">
                                            <i class="fas fa-info-circle me-1"></i>
                                            \${event.status.replace('_',' ').toUpperCase()}
                                        </span>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="card-body p-3">
                            <!-- Event Statistics Row -->
                            <div class="row g-2 mb-3">
                                <div class="col-6 col-lg-4">
                                    <div class="d-flex align-items-center p-2 bg-primary bg-opacity-10 rounded">
                                        <div class="bg-primary text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                            <i class="fas fa-fire"></i>
                                        </div>
                                        <div>
                                            <div class="fw-bold text-primary">\${event.current_heat || '—'}</div>
                                            <small class="text-muted">Active Heat</small>
                                        </div>
                                    </div>
                                </div>
                                <div class="col-6 col-lg-4">
                                    <div class="d-flex align-items-center p-2 bg-success bg-opacity-10 rounded">
                                        <div class="bg-success text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                            <i class="fas fa-play"></i>
                                        </div>
                                        <div>
                                            <div class="fw-bold text-success">\${event.current_run || '—'}</div>
                                            <small class="text-muted">Current Run</small>
                                        </div>
                                    </div>
                                </div>
                                <div class="col-6 col-lg-4">
                                    <div class="d-flex align-items-center p-2 bg-info bg-opacity-10 rounded">
                                        <div class="bg-info text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                            <i class="fas fa-users"></i>
                                        </div>
                                        <div>
                                            <div class="fw-bold text-info">\${event.total_participants || 0}</div>
                                            <small class="text-muted">Athletes</small>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            
                            <!-- Latest Updates Row -->
                            <div class="row g-2 mb-3" id="latest-updates-\${event.id}">
                                <!-- Latest Score Section -->
                                <div class="col-md-12" id="latest-score-\${event.id}">
                                    \${event.latest_score ? this.renderLatestScore(event.latest_score) : this.renderNoScore()}
                                </div>
                                
                                <!-- Next on Start Section -->
                                <div class="col-md-12" id="next-on-start-\${event.id}">
                                    \${nextBib ? this.renderNextOnStart(nextBib) : this.renderNoNext()}
                                </div>
                            </div>
                            
                            <!-- Action Button -->
                            <div class="text-center">
                                <button class="btn btn-primary px-2 py-1 rounded-pill shadow-sm" onclick="window.dashboard.loadEventHeats(\${event.id}, '\${event.name.replace(/'/g,"&apos;")}')">
                                    <i class="fas fa-fire me-1"></i>
                                    View Heat Details
                                </button>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="col-8">
                    <!-- General Reports Section -->
                    <div class="col-12">
                        <div class="card h-100">
                            <div class="card-header bg-primary text-white d-flex align-items-center">
                                <i class="fas fa-chart-bar me-2"></i>
                                <span class="fw-bold">General Reports</span>
                            </div>
                            <div class="card-body p-3">
                                <div id="general-reports-\${event.id}" class="general-reports-container">
                                    <div class="text-center text-muted">
                                        <div class="spinner-border spinner-border-sm me-2" role="status">
                                            <span class="visually-hidden">Loading...</span>
                                        </div>
                                        Loading general reports...
                                    </div>
                                </div>
                                <!-- General Report Cards Container -->
                                <div class="general-report-cards mt-3" id="general-report-cards-\${event.id}"></div>
                            </div>
                        </div>
                    </div>
                    <!-- Heats Wrapper -->
                    <div id="heats-\${event.id}" class="heat-wrapper" style="display:block;">
                        <div class='text-center py-2'><div class='spinner-border spinner-border-sm text-primary' role='status'></div><p class='mt-1 text-muted small'>Loading heat details...</p></div>
                    </div>
                </div>
            </div>`;
        }

        container.innerHTML = html;

        // Auto-load heat details for all events
        for (const event of events) {
            await this.loadEventHeatsData(event.id, event.name);
            // Load general reports for each event
            await this.refreshGeneralReports(event.id);
        }
        
        // Set global reference
        window.dashboard = this;
        
        // Make functions available globally for onclick handlers
        window.refreshGeneralReports = (eventId) => this.refreshGeneralReports(eventId);
        window.loadStartListCard = (eventId, heatNumber) => this.loadStartListCard(eventId, heatNumber);
        window.loadSummaryTableCard = (eventId, heatNumber, configId, configName) => this.loadSummaryTableCard(eventId, heatNumber, configId, configName);
    }
    
    renderLatestScore(score) {
        return `
            <div class="card border-0 bg-success bg-opacity-10 h-100">
                <div class="card-body p-2">
                    <div class="d-flex align-items-center mb-2">
                        <div class="bg-success text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                            <i class="fas fa-trophy"></i>
                        </div>
                        <h6 class="mb-0 text-success fw-bold">Latest Score</h6>
                    </div>
                    <div class="d-flex align-items-center">
                        <div class="me-3">
                            \${this.createParticipantPhoto(score)}
                        </div>
                        <div class="flex-grow-1">
                            <div class="fw-bold">\${score.first_name} \${score.last_name}</div>
                            <div class="d-flex gap-1 mb-1">
                                <span class="badge bg-dark">BIB \${score.bib_number}</span>
                                \${score.current_rank ? '<span class="badge bg-warning text-dark">#' + score.current_rank + '</span>' : ''}
                            </div>
                            <div class="h5 text-success mb-1">\${parseFloat(score.score_value).toFixed(2)}</div>
                            <small class="text-muted">
                                <i class="fas fa-clock me-1"></i>
                                \${new Date(score.created_at).toLocaleTimeString()}
                            </small>
                        </div>
                    </div>
                </div>
            </div>
        `;
    }
    
    renderNoScore() {
        return `
            <div class="card border-0 bg-success bg-opacity-10 h-100">
                <div class="card-body p-2">
                    <div class="d-flex align-items-center mb-2">
                        <div class="bg-success text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                            <i class="fas fa-trophy"></i>
                        </div>
                        <h6 class="mb-0 text-success fw-bold">Latest Score</h6>
                    </div>
                    <div class="text-center py-3 text-muted">
                        <i class="fas fa-clock fs-2 mb-2"></i>
                        <p class="mb-0 small">No scores yet</p>
                    </div>
                </div>
            </div>
        `;
    }
    
    renderNextOnStart(next) {
        return `
            <div class="card border-0 bg-primary bg-opacity-10 h-100">
                <div class="card-body p-2">
                    <div class="d-flex align-items-center mb-2">
                        <div class="bg-primary text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                            <i class="fas fa-play"></i>
                        </div>
                        <h6 class="mb-0 text-primary fw-bold">Next on Start</h6>
                    </div>
                    <div class="d-flex align-items-center">
                        <div class="me-3">
                            \${this.createParticipantPhoto(next)}
                        </div>
                        <div class="flex-grow-1">
                            <div class="fw-bold">\${next.first_name} \${next.last_name}</div>
                            <div class="d-flex gap-1 mb-1">
                                <span class="badge bg-dark">BIB \${next.bib_number}</span>
                                \${next.category ? '<span class="badge bg-info">' + next.category + '</span>' : ''}
                            </div>
                            <small class="text-primary">
                                <i class="fas fa-arrow-right me-1"></i>
                                Ready to compete
                            </small>
                        </div>
                    </div>
                </div>
            </div>
        `;
    }
    
    renderNoNext() {
        return `
            <div class="card border-0 bg-primary bg-opacity-10 h-100">
                <div class="card-body p-2">
                    <div class="d-flex align-items-center mb-2">
                        <div class="bg-primary text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                            <i class="fas fa-play"></i>
                        </div>
                        <h6 class="mb-0 text-primary fw-bold">Next on Start</h6>
                    </div>
                    <div class="text-center py-3 text-muted">
                        <i class="fas fa-users fs-2 mb-2"></i>
                        <p class="mb-0 small">No participants waiting</p>
                    </div>
                </div>
            </div>
        `;
    }
    
    createParticipantPhoto(participant) {
        if (participant.photo && participant.photo.trim() !== '') {
            // Check if photo path needs adjustment for static files
            let photoPath = participant.photo;
            if (!photoPath.startsWith('http') && !photoPath.startsWith('photos/')) {
                // Assume it's a participant photo that was copied to photos directory
                if (photoPath.includes('participant_')) {
                    photoPath = 'photos/' + photoPath;
                } else {
                    // Skip showing photo if we can't determine the correct path
                    console.warn('Could not determine photo path for:', participant);
                    const initials = ((participant.first_name?.charAt(0) || '') + (participant.last_name?.charAt(0) || '')).toUpperCase() || '??';
                    return `<div class="participant-photo-placeholder">\${initials}</div>`;
                }
            }
            
            return `<img src="\${photoPath}" alt="\${participant.first_name} \${participant.last_name}" class="participant-photo" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
                    <div class="participant-photo-placeholder" style="display:none;">\${((participant.first_name?.charAt(0) || '') + (participant.last_name?.charAt(0) || '')).toUpperCase() || '??'}</div>`;
        } else {
            const initials = ((participant.first_name?.charAt(0) || '') + (participant.last_name?.charAt(0) || '')).toUpperCase() || '??';
            return `<div class="participant-photo-placeholder">\${initials}</div>`;
        }
    }
    
    async loadEventHeats(eventId, eventName) {
        const container = document.getElementById(`heats-\${eventId}`);
        if (container.style.display === 'none') {
            this.openHeatContainers.add(eventId);
            container.style.display = 'block';
            await this.loadEventHeatsData(eventId, eventName);
        } else {
            this.openHeatContainers.delete(eventId);
            this.activeReports.delete(`\${eventId}`);
            container.style.display = 'none';
        }
    }
    
    async loadEventHeatsData(eventId, eventName) {
        const container = document.getElementById(`heats-\${eventId}`);
        container.innerHTML = '<div class="text-center py-2"><div class="spinner-border spinner-border-sm" role="status"></div><p class="mt-1 small">Loading heat details...</p></div>';
        
        try {
            const response = await fetch('api/event_heats.json?t=' + Date.now());
            const data = await response.json();
            
            if (data.success) {
                this.displayHeats(eventId, eventName, data.heats, data.configurations);
            } else {
                container.innerHTML = '<div class="alert alert-danger">Error loading heats: ' + (data.message || 'Unknown') + '</div>';
            }
        } catch (error) {
            console.error('Error:', error);
            container.innerHTML = '<div class="alert alert-danger">Network error loading heats</div>';
        }
    }
    
    displayHeats(eventId, eventName, heats, configurations) {
        const container = document.getElementById(`heats-\${eventId}`);
        if (!container) return;

        if (!heats || heats.length === 0) {
            container.innerHTML = `
                <div class="card mb-2">
                    <div class="card-header py-2"><strong>Heat Details</strong></div>
                    <div class="card-body p-2"><div class="alert alert-info mb-0 py-2">No heats configured for this event</div></div>
                </div>
            `;
            return;
        }

        let html = `
            <div class="card">
                <div class="card-header py-2"><strong>Heat Details</strong></div>
                <div class="card-body p-2">
                `;
        
        // Render each heat
        heats.forEach(heat => {
            const completion = heat.expected_scores > 0
                ? Math.round((Number(heat.total_scores || 0) / Number(heat.expected_scores)) * 100)
                : 0;
            const progressClass = completion >= 100 ? 'bg-success' : (completion >= 50 ? 'bg-warning' : 'bg-primary');
            const heatStatusClass = Number(heat.is_active) === 1 ? 'bg-danger' : (completion >= 100 ? 'bg-success' : 'bg-secondary');
            const heatStatusText = Number(heat.is_active) === 1 ? 'Live' : (completion >= 100 ? 'Completed' : 'Upcoming');

            html += `
                <div class="border rounded mb-3 p-3" id="event-\${eventId}-heat-\${heat.heat_number}">
                    <div class="row align-items-center">
                        <div class="col-md-6">
                            <div class="d-flex align-items-center">
                                <span class="badge \${heatStatusClass} me-2 px-2 py-1">\${heatStatusText}</span>
                                <div>
                                    <strong class="h6 mb-0">Heat \${heat.heat_number}\${heat.heat_name ? ': ' + heat.heat_name : ''}</strong>
                                    <div class="text-muted small">
                                        <i class="fas fa-clock me-1"></i>\${heat.time_start || 'No time set'}
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="col-md-2 text-center">
                            <div class="bg-primary text-white rounded-circle p-2 mx-auto" style="width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;">
                                <span class="text-white">\${heat.participants || 0}</span>
                            </div>
                            <small class="text-muted">Athletes</small>
                        </div>
                        <div class="col-md-4">
                            <div class="text-center">
                                <div class="progress mb-1" style="height:8px;">
                                    <div class="progress-bar \${progressClass}" style="width:\${completion}%;"></div>
                                </div>
                                <small class="fw-bold \${completion >= 100 ? 'text-success' : 'text-muted'}">\${completion}% Complete</small>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Available Reports Section -->
                    <div class="row mt-2 pt-2 border-top">
                        <div class="col-12">
                            <div class="d-flex align-items-center justify-content-between mb-3">
                                <small class="text-muted fw-bold">Available Reports:</small>
                            </div>
                            <div class="row g-3">
                                <!-- Start Lists Column -->
                                <div class="col-md-6">
                                    <div class="d-flex align-items-center mb-2">
                                        <i class="fas fa-users text-success me-2"></i>
                                        <small class="text-muted fw-bold">Start Lists</small>
                                    </div>
                                    <div class="d-flex flex-column gap-2">
                                        <button class="btn btn-outline-success btn-sm w-100 text-start" onclick="loadStartListCard(\${eventId}, \${heat.heat_number})" title="Heat \${heat.heat_number} Start List">
                                            <i class="fas fa-users me-2"></i>Heat \${heat.heat_number} Start List
                                            <span class="badge \${heatStatusClass} float-end">\${heatStatusText}</span>
                                        </button>
                                    </div>
                                </div>
                                
                                <!-- Summary Tables Column -->
                                <div class="col-md-6">
                                    <div class="d-flex align-items-center mb-2">
                                        <i class="fas fa-table text-info me-2"></i>
                                        <small class="text-muted fw-bold">Summary Tables</small>
                                    </div>
                                    <div class="d-flex flex-column gap-2">
                                        <button class="btn btn-outline-info btn-sm w-100 text-start" onclick="loadSummaryTableCard(\${eventId}, \${heat.heat_number}, 1, 'Heat \${heat.heat_number} Results')" title="Heat \${heat.heat_number} Results">
                                            <i class="fas fa-table me-2"></i>Heat \${heat.heat_number} Results
                                            <span class="badge \${heatStatusClass} float-end">\${heatStatusText}</span>
                                        </button>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Report Cards Container for this Heat -->
                    <div class="report-cards mt-3" id="report-cards-\${eventId}-\${heat.heat_number}"></div>
                </div>`;
        });

        html += `
                </div>
            </div>`;
        container.innerHTML = html;
    }
    
    startAutoRefresh() {
        if (this.refreshInterval) {
            clearInterval(this.refreshInterval);
        }
        this.refreshInterval = setInterval(() => {
            if (this.autoRefresh) {
                this.loadData();
            }
        }, this.refreshMs);
    }
    
    updateLastUpdateTime() {
        const element = document.getElementById('lastUpdate');
        if (element && this.lastUpdate) {
            element.textContent = this.lastUpdate.toLocaleTimeString();
        }
    }
    
    toggleAutoRefresh() {
        const toggleBtn = document.getElementById('toggleRefresh');
        if (!toggleBtn) return;
        
        if (this.autoRefresh) {
            this.autoRefresh = false;
            toggleBtn.innerHTML = '<i class="fas fa-play me-1"></i>Resume Updates';
        } else {
            this.autoRefresh = true;
            toggleBtn.innerHTML = '<i class="fas fa-pause me-1"></i>Pause Updates';
        }
    }
    
    // Report loading functions
    async refreshGeneralReports(eventId) {
        const container = document.getElementById('general-reports-' + eventId);
        if (!container) {
            console.error('General reports container not found for event', eventId);
            return;
        }
        
        container.innerHTML = '<div class="text-muted small">Loading general reports…</div>';
        
        try {
            // Load all configurations for this event
            const response = await fetch('api/configurations.json?t=' + Date.now());
            const data = await response.json();
            
            console.log('Configurations loaded:', data);
            
            if (data.success && data.configurations && data.configurations.length > 0) {
                // Group configurations by type
                const startListConfigs = data.configurations.filter(config => config.view_type === 'start_list');
                const summaryTableConfigs = data.configurations.filter(config => config.view_type === 'summary_table');
                
                console.log('Start lists found:', startListConfigs.length);
                console.log('Summary tables found:', summaryTableConfigs.length);
                
                let html = '';
                
                // Start Lists Section
                if (startListConfigs.length > 0) {
                    html += '<div class="mb-3">';
                    html += '<h6 class="text-success mb-2"><i class="fas fa-users me-2"></i>Start Lists</h6>';
                    html += '<div class="row g-2">';
                    startListConfigs.forEach(config => {
                        const heatText = config.heat_number ? ('H' + config.heat_number) : 'all';
                        const displayName = config.name;
                        const safeConfigName = config.name.replace(/'/g, "\\\\'");
                        html += '<div class="col-md-6">' +
                                  '<button class="btn btn-outline-success btn-sm w-100 text-start" onclick="window.dashboard.loadGeneralReport(' + this.eventId + ', ' + config.id + ', \\'' + config.view_type + '\\', \\'' + safeConfigName + '\\')">' +
                                    '<i class="fas fa-users me-2"></i>' + displayName +
                                    '<span class="badge bg-success float-end">' + heatText + '</span>' +
                                  '</button>' +
                                '</div>';
                    });
                    html += '</div></div>';
                }
                
                // Summary Tables Section  
                if (summaryTableConfigs.length > 0) {
                    html += '<div class="mb-3">';
                    html += '<h6 class="text-info mb-2"><i class="fas fa-table me-2"></i>Summary Tables</h6>';
                    html += '<div class="row g-2">';
                    summaryTableConfigs.forEach(config => {
                        const heatText = config.heat_number ? ('H' + config.heat_number) : 'all';
                        const displayName = config.name;
                        const safeConfigName = config.name.replace(/'/g, "\\\\'");
                        html += '<div class="col-md-6">' +
                                  '<button class="btn btn-outline-info btn-sm w-100 text-start" onclick="window.dashboard.loadGeneralReport(' + this.eventId + ', ' + config.id + ', \\'' + config.view_type + '\\', \\'' + safeConfigName + '\\')">' +
                                    '<i class="fas fa-table me-2"></i>' + displayName +
                                    '<span class="badge bg-info float-end">' + heatText + '</span>' +
                                  '</button>' +
                                '</div>';
                    });
                    html += '</div></div>';
                }
                
                if (html) {
                    container.innerHTML = html;
                } else {
                    container.innerHTML = '<div class="text-muted small">No reports configured</div>';
                }
            } else {
                // Show default reports even if no configurations found
                container.innerHTML = `
                    <div class="row g-2">
                        <div class="col-md-6">
                            <button class="btn btn-outline-success btn-sm w-100 text-start" onclick="window.dashboard.loadGeneralReport(${eventId}, 1, 'start_list', 'Event Start List')">
                                <i class="fas fa-users me-2"></i>Event Start List
                            </button>
                        </div>
                        <div class="col-md-6">
                            <button class="btn btn-outline-info btn-sm w-100 text-start" onclick="window.dashboard.loadGeneralReport(${eventId}, 2, 'summary_table', 'Event Results')">
                                <i class="fas fa-table me-2"></i>Event Results
                            </button>
                        </div>
                    </div>
                `;
            }
        } catch (error) {
            console.error('Error loading configurations:', error);
            container.innerHTML = '<div class="text-muted small">Error loading reports</div>';
        }
    }
    
    async loadGeneralReport(eventId, configId, viewType, configName) {
        // This would load the actual report content
        alert('Loading: ' + configName + ' (Event-level ' + viewType + ')');
    }
    
    async loadStartListCard(eventId, heatNumber) {
        // Load start list for specific heat
        try {
            const response = await fetch(`api/start_list_heat_\${heatNumber}.json?t=` + Date.now());
            const data = await response.json();
            
            if (data.success && data.data && data.data.participants) {
                this.displayStartListModal(eventId, heatNumber, data.data.participants);
            } else {
                alert('No start list data available for Heat ' + heatNumber);
            }
        } catch (error) {
            console.error('Error loading start list:', error);
            alert('Error loading start list for Heat ' + heatNumber);
        }
    }
    
    async loadSummaryTableCard(eventId, heatNumber, configId, configName) {
        // Load summary table for specific heat
        try {
            const response = await fetch(`api/summary_table_\${configId}.json?t=` + Date.now());
            const data = await response.json();
            
            if (data.success) {
                this.displaySummaryTableModal(eventId, heatNumber, configName, data);
            } else {
                alert('No summary table data available for ' + configName);
            }
        } catch (error) {
            console.error('Error loading summary table:', error);
            alert('Error loading summary table: ' + configName);
        }
    }
    
    displayStartListModal(eventId, heatNumber, participants) {
        // Simple display - in a real implementation this would show a modal
        let content = `Heat \${heatNumber} Start List:\\n\\n`;
        participants.forEach((p, index) => {
            content += `\${index + 1}. Bib \${p.bib_number}: \${p.first_name} \${p.last_name}`;
            if (p.club) content += ` (\${p.club})`;
            content += '\\n';
        });
        alert(content);
    }
    
    displaySummaryTableModal(eventId, heatNumber, configName, data) {
        // Simple display - in a real implementation this would show a modal
        let content = `\${configName}:\\n\\n`;
        if (data.rows && data.rows.length > 0) {
            data.rows.forEach((row, index) => {
                content += `\${index + 1}. \${row.first_name} \${row.last_name}: \${row.best_score || 'No score'}\\n`;
            });
        } else {
            content = 'No results available yet for ' + configName;
        }
        alert(content);
    }
    
    toggleFullscreen() {
        const container = document.getElementById('eventsContainer');
        if (!container) return;
        
        if (document.fullscreenElement) {
            document.exitFullscreen();
        } else {
            if (container.requestFullscreen) {
                container.requestFullscreen();
                container.classList.add('fullscreen');
            } else if (container.webkitRequestFullscreen) {
                container.webkitRequestFullscreen();
                container.classList.add('fullscreen');
            } else if (container.mozRequestFullScreen) {
                container.mozRequestFullScreen();
                container.classList.add('fullscreen');
            } else if (container.msRequestFullscreen) {
                container.msRequestFullscreen();
                container.classList.add('fullscreen');
            }
        }
    }
    
    showError(message) {
        const container = document.getElementById('eventsContainer');
        container.innerHTML = `
            <div class="alert alert-danger">
                <h4><i class="fas fa-exclamation-triangle me-2"></i>Error</h4>
                <p>\${message}</p>
                <button class="btn btn-outline-danger" onclick="location.reload()">
                    <i class="fas fa-refresh me-2"></i>Reload
                </button>
            </div>
        `;
    }
}

// Initialize dashboard when page loads
document.addEventListener('DOMContentLoaded', function() {
    new StaticLiveDashboard();
});

// Handle fullscreen changes
document.addEventListener('fullscreenchange', function() {
    const container = document.getElementById('eventsContainer');
    if (container && !document.fullscreenElement) {
        container.classList.remove('fullscreen');
    }
});

// Report management globals
const activeReports = new Map();
const refreshMs = 30000; // 30 seconds default

// Load Summary Table Card with FLIP animations
function loadSummaryTableCard(eventId, heatNumber, configId, configName, options = {}) {
    const refresh = options.refresh === true;
    const reportKey = `\${eventId}-\${heatNumber ?? 'all'}-summary`;
    const holderId = `report-cards-\${eventId}-\${heatNumber ?? 'all'}`;
    let holder = document.getElementById(holderId);
    if (!holder && heatNumber == null) {
        holder = document.getElementById(`general-report-cards-\${eventId}`) 
              || document.querySelector(`[id^="report-cards-\${eventId}-"]`);
    }
    if (!holder) return;

    // Toggle only on direct button click, not on refresh
    if (!refresh && activeReports.has(reportKey) && activeReports.get(reportKey).configId === configId) {
        activeReports.delete(reportKey);
        morphHtml(holder, '');
        return;
    }

    const hadContent = holder.innerHTML.trim().length > 0;
    if (hadContent) showReportUpdating(reportKey);
    else {
        morphHtml(holder, `
            <div class="border-0 shadow-none card mb-3">
                <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                    <small class="fw-bold"><i class="fas fa-table me-1"></i>\${configName}</small>
                    <div class="d-flex align-items-center gap-1 table-buttons">
                        <span class="badge bg-info"><i class="fas fa-sync-alt fa-spin me-1"></i>Loading…</span>
                        <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
                    </div>
                </div>
                <div class="border-0 shadow-none card-body p-2 text-center">
                    <div class="spinner-border spinner-border-sm text-primary"></div>
                    <small class="ms-2">Loading summary table…</small>
                </div>
            </div>`);
    }

    // Use static API endpoint instead of POST
    const apiUrl = `api/summary_table_\${configId}.json?t=\${Date.now()}`;
    
    fetch(apiUrl)
    .then(r => r.json())
    .then(res => {
        if (!res.success) throw new Error(res.message || 'Failed to load summary table');

        // FLIP: capture "First" positions BEFORE DOM update
        const prevRects = getRowRects(reportKey);

        const prev = activeReports.get(reportKey);
        const auto = prev ? (prev.auto !== false) : true;
        const html = convertSummaryTableToHTML(res.data, configName, reportKey, { auto });

        // Morph in new HTML (the order may change)
        morphHtml(holder, html);

        // FLIP: get "Last" and animate to new positions
        playFlipAnimation(reportKey, prevRects);

        // Track changes for animation
        let maps;
        if (refresh && prev && prev.type === 'summary_table') {
            maps = trackAndAnimateSummaryTable(reportKey, prev);
        } else {
            maps = buildSummaryRowMaps(reportKey);
        }

        activeReports.set(reportKey, {
            html,
            type: 'summary_table',
            configId,
            configName,
            auto,
            rowIndexMap: maps.indexMap,
            rowContentMap: maps.contentMap
        });
        
        console.log('Report loaded successfully:', configName);
    })
    .catch(err => {
        console.error('Error loading report:', err);
        if (!hadContent) {
            morphHtml(holder, `
                <div class="border-0 shadow-none card mb-3">
                    <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                        <small class="fw-bold"><i class="fas fa-table me-1"></i>\${configName}</small>
                        <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
                    </div>
                    <div class="border-0 shadow-none card-body p-2"><div class="alert alert-danger py-2 mb-0">\${err.message}</div></div>
                </div>`);
        }
    });
}

// Load Start List Card
function loadStartListCard(eventId, heatNumber) {
    const reportKey = `\${eventId}-\${heatNumber}-startlist`;
    const holderId = `report-cards-\${eventId}-\${heatNumber}`;
    const holder = document.getElementById(holderId);
    if (!holder) return;

    // Toggle if already loaded
    if (activeReports.has(reportKey)) {
        activeReports.delete(reportKey);
        morphHtml(holder, '');
        return;
    }

    morphHtml(holder, `
        <div class="border-0 shadow-none card mb-3">
            <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                <small class="fw-bold"><i class="fas fa-users me-1"></i>Heat \${heatNumber} Start List</small>
                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
            </div>
            <div class="border-0 shadow-none card-body p-2 text-center">
                <div class="spinner-border spinner-border-sm text-primary"></div>
                <small class="ms-2">Loading start list…</small>
            </div>
        </div>`);

    // Use static API endpoint
    const apiUrl = `api/start_list.json?t=\${Date.now()}`;
    
    fetch(apiUrl)
    .then(r => r.json())
    .then(res => {
        if (!res.success) throw new Error(res.message || 'Failed to load start list');
        
        const html = convertStartListToHTML(res.data, heatNumber, reportKey);
        morphHtml(holder, html);
        
        activeReports.set(reportKey, {
            html,
            type: 'start_list',
            heatNumber
        });
    })
    .catch(err => {
        console.error('Error loading start list:', err);
        morphHtml(holder, `
            <div class="border-0 shadow-none card mb-3">
                <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                    <small class="fw-bold"><i class="fas fa-users me-1"></i>Heat \${heatNumber} Start List</small>
                    <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
                </div>
                <div class="border-0 shadow-none card-body p-2"><div class="alert alert-danger py-2 mb-0">\${err.message}</div></div>
            </div>`);
    });
}

// Convert summary table API data to HTML
function convertSummaryTableToHTML(apiData, configName, reportKey, opts = {}) {
    const auto = opts.auto !== false;
    const table_badge_colors = apiData?.table_badge_colors || '';
    const tableHead = apiData?.table_head || '';
    const tableBody = apiData?.table_body || '';
    const participantCount = apiData?.participant_count ?? 0;
    const filterSummary = apiData?.filter_summary || '';

    const tableHTML = (tableHead || tableBody) ? `
        <div class="d-flex justify-content-between align-items-center mb-2">
            <div class="d-flex align-items-center gap-2">
                <span class="badge bg-primary">\${participantCount} Participants</span>
                \${auto ? `<span class="badge bg-success"><i class="fas fa-sync-alt me-1"></i>Auto \${getHumanIntervalLabel(refreshMs)}</span>` 
                       : `<span class="badge bg-secondary"><i class="fas fa-pause me-1"></i>Manual</span>`}
            </div>
            <div class="d-flex align-items-center gap-1 table-buttons">
                <button class="btn btn-sm btn-outline-secondary" onclick="refreshReport('\${reportKey}')" title="Refresh now">
                    <i class="fas fa-sync-alt"></i>
                </button>
                <button class="btn btn-sm \${auto ? 'btn-warning' : 'btn-success'}" onclick="toggleReportAuto('\${reportKey}')" title="\${auto ? 'Pause auto-refresh' : 'Resume auto-refresh'}">
                    <i class="fas \${auto ? 'fa-pause' : 'fa-play'}"></i>
                </button>
                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
            </div>
        </div>
        \${filterSummary ? `<div class="text-muted small mb-2">\${filterSummary}</div>` : ''}
        <style>\${table_badge_colors}</style>
        <div class="table-responsive summary-table" id="summary-table-\${reportKey}">
            <table class="table data-table mb-0 table-bordered">
                \${tableHead}
                \${tableBody}
            </table>
        </div>` : `<div class="alert alert-info py-2 mb-0">No summary data available</div>`;

    return `
        <div class="border-0 shadow-none card mb-3">
            <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                <h6 class="fw-bold"><i class="fas fa-table me-1"></i>\${configName}</h6>
                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
            </div>
            <div class="border-0 shadow-none card-body p-2">\${tableHTML}</div>
        </div>`;
}

// Convert start list API data to HTML
function convertStartListToHTML(apiData, heatNumber, reportKey) {
    const participants = apiData?.participants || [];
    
    let tableHTML = `
        <div class="table-responsive">
            <table class="table table-sm table-bordered mb-0">
                <thead class="table-primary">
                    <tr>
                        <th>Bib</th>
                        <th>Name</th>
                        <th>Category</th>
                        <th>Status</th>
                    </tr>
                </thead>
                <tbody>`;
    
    if (participants.length > 0) {
        participants.forEach(p => {
            tableHTML += `
                <tr>
                    <td><span class="badge bg-primary">\${p.bib_number || 'TBD'}</span></td>
                    <td>\${p.name || 'Unknown'}</td>
                    <td>\${p.category || 'N/A'}</td>
                    <td><span class="badge bg-\${p.status === 'confirmed' ? 'success' : 'warning'}">\${p.status || 'Pending'}</span></td>
                </tr>`;
        });
    } else {
        tableHTML += '<tr><td colspan="4" class="text-center text-muted">No participants assigned</td></tr>';
    }
    
    tableHTML += `
                </tbody>
            </table>
        </div>`;

    return `
        <div class="border-0 shadow-none card mb-3">
            <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                <h6 class="fw-bold"><i class="fas fa-users me-1"></i>Heat \${heatNumber} Start List</h6>
                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('\${reportKey}')"></button>
            </div>
            <div class="border-0 shadow-none card-body p-2">\${tableHTML}</div>
        </div>`;
}

// FLIP Animation Functions
function getRowRects(reportKey) {
    const container = document.getElementById(`summary-table-\${reportKey}`);
    const rows = container ? container.querySelectorAll('tbody tr') : null;
    const rects = {};
    if (!rows) return rects;
    rows.forEach(tr => {
        const key = getRowKeyBySecondColumn(tr);
        if (key) rects[key] = tr.getBoundingClientRect();
    });
    return rects;
}

function playFlipAnimation(reportKey, prevRects) {
    const container = document.getElementById(`summary-table-\${reportKey}`);
    if (!container) return;
    const rows = container.querySelectorAll('tbody tr');
    rows.forEach(tr => {
        const key = getRowKeyBySecondColumn(tr);
        if (!key) return;

        const oldRect = prevRects[key];
        const newRect = tr.getBoundingClientRect();

        if (oldRect) {
            const dy = oldRect.top - newRect.top;
            if (Math.abs(dy) > 0.5) {
                tr.style.transition = 'none';
                tr.style.transform = `translateY(\${dy}px)`;
                // force reflow
                void tr.offsetHeight;
                tr.style.transition = 'transform 1200ms ease, opacity 1200ms ease';
                tr.style.transform = '';
            }
        } else {
            // New row: fade+slide in
            tr.style.transition = 'none';
            tr.style.opacity = '0';
            tr.style.transform = 'translateY(8px)';
            void tr.offsetHeight;
            tr.style.transition = 'transform 1200ms ease, opacity 1200ms ease';
            tr.style.opacity = '1';
            tr.style.transform = '';
        }

        const cleanup = () => {
            tr.style.transition = '';
            tr.style.transform = '';
            tr.style.opacity = '';
            tr.removeEventListener('transitionend', cleanup);
        };
        tr.addEventListener('transitionend', cleanup);
    });
}

function getRowKeyBySecondColumn(tr) {
    const cells = tr.querySelectorAll('td');
    return cells.length >= 2 ? cells[1].textContent.trim() : null;
}

function buildSummaryRowMaps(reportKey) {
    const container = document.getElementById(`summary-table-\${reportKey}`);
    const rows = container ? container.querySelectorAll('tbody tr') : null;
    const indexMap = {};
    const contentMap = {};
    if (!rows) return { indexMap, contentMap };
    let idx = 0;
    rows.forEach(tr => {
        const key = getRowKeyBySecondColumn(tr);
        if (key) {
            indexMap[key] = idx++;
            contentMap[key] = tr.innerHTML;
        }
    });
    return { indexMap, contentMap };
}

function trackAndAnimateSummaryTable(reportKey, prev) {
    const maps = buildSummaryRowMaps(reportKey);
    // Add highlighting for changed rows
    const container = document.getElementById(`summary-table-\${reportKey}`);
    if (container && prev.rowContentMap) {
        const rows = container.querySelectorAll('tbody tr');
        rows.forEach(tr => {
            const key = getRowKeyBySecondColumn(tr);
            if (key && prev.rowContentMap[key] && prev.rowContentMap[key] !== tr.innerHTML) {
                tr.classList.add('table-warning');
                setTimeout(() => tr.classList.remove('table-warning'), 3000);
            }
        });
    }
    return maps;
}

// Report control functions
function closeReportCard(reportKey) {
    activeReports.delete(reportKey);
    const parts = reportKey.split('-');
    const eventId = parts[0];
    const heatKey = parts.slice(1, -1).join('-');
    
    let holder;
    if (heatKey === 'all') {
        holder = document.getElementById(`general-report-cards-\${eventId}`);
    } else {
        holder = document.getElementById(`report-cards-\${eventId}-\${heatKey}`);
    }
    
    if (holder) {
        morphHtml(holder, '');
    }
}

function refreshReport(reportKey) {
    const report = activeReports.get(reportKey);
    if (!report) return;
    
    const parts = reportKey.split('-');
    const eventId = parts[0];
    const heatNumber = parts[1] !== 'all' ? parts[1] : null;
    
    if (report.type === 'summary_table') {
        loadSummaryTableCard(eventId, heatNumber, report.configId, report.configName, { refresh: true });
    } else if (report.type === 'start_list') {
        loadStartListCard(eventId, heatNumber);
    }
}

function toggleReportAuto(reportKey) {
    const report = activeReports.get(reportKey);
    if (report) {
        report.auto = !report.auto;
        refreshReport(reportKey); // Refresh to update UI
    }
}

function showReportUpdating(reportKey) {
    const container = document.getElementById(`summary-table-\${reportKey}`);
    if (container) {
        const overlay = document.createElement('div');
        overlay.className = 'updating-overlay';
        overlay.innerHTML = '<div class="spinner-border spinner-border-sm"></div>';
        overlay.style.cssText = 'position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(255,255,255,0.8);display:flex;justify-content:center;align-items:center;z-index:10;';
        container.style.position = 'relative';
        container.appendChild(overlay);
        
        setTimeout(() => overlay.remove(), 2000);
    }
}

// Simple morphing function for DOM updates
function morphHtml(element, newHtml) {
    element.innerHTML = newHtml;
}

function getHumanIntervalLabel(ms) {
    if (ms < 60000) return `\${ms/1000}s`;
    return `\${ms/60000}min`;
}

// Auto-refresh for active reports
setInterval(() => {
    activeReports.forEach((report, reportKey) => {
        if (report.auto !== false && report.type === 'summary_table') {
            const jitter = Math.random() * 5000; // 0-5 second jitter
            setTimeout(() => refreshReport(reportKey), jitter);
        }
    });
}, refreshMs);

JS;
}

// Generate Participant Photos - Copy participant photos to publish directory
function generateParticipantPhotos($event_id, $temp_dir, $pdo) {
    error_log("Starting generateParticipantPhotos for event $event_id in $temp_dir");
    $files = [];
    
    try {
        // Get all participants for this event
        $participants_stmt = $pdo->prepare("
            SELECT p.id, p.name, p.photo_filename 
            FROM participants p 
            WHERE p.event_id = ? AND p.photo_filename IS NOT NULL AND p.photo_filename != ''
        ");
        $participants_stmt->execute([$event_id]);
        $participants = $participants_stmt->fetchAll(PDO::FETCH_ASSOC);
        
        if (empty($participants)) {
            error_log("No participants with photos found for event $event_id");
            return $files;
        }
        
        // Create photos directory in temp dir
        $photos_dir = $temp_dir . '/photos';
        if (!file_exists($photos_dir)) {
            mkdir($photos_dir, 0755, true);
        }
        
        $copied_count = 0;
        
        foreach ($participants as $participant) {
            $photo_filename = $participant['photo_filename'];
            
            // Try different possible locations for the photo
            $possible_sources = [
                __DIR__ . '/../uploads/' . $photo_filename,
                __DIR__ . '/../uploads/photos/' . $photo_filename,
                __DIR__ . '/../photos/' . $photo_filename,
                __DIR__ . '/../' . $photo_filename
            ];
            
            $source_found = false;
            foreach ($possible_sources as $source_path) {
                if (file_exists($source_path)) {
                    $destination_path = $photos_dir . '/' . $photo_filename;
                    
                    if (copy($source_path, $destination_path)) {
                        $files[] = [
                            'local' => $destination_path,
                            'remote' => 'photos/' . $photo_filename
                        ];
                        $copied_count++;
                        error_log("Copied photo: $photo_filename for participant: {$participant['name']}");
                    } else {
                        error_log("Failed to copy photo: $photo_filename");
                    }
                    $source_found = true;
                    break;
                }
            }
            
            if (!$source_found) {
                error_log("Photo not found: $photo_filename for participant: {$participant['name']}");
            }
        }
        
        error_log("Copied $copied_count out of " . count($participants) . " participant photos");
        
    } catch (Exception $e) {
        error_log("Error in generateParticipantPhotos: " . $e->getMessage());
        throw $e;
    }
    
    return $files;
}

// Generate JSON Data - Legacy support
function generateJSONData($event_id, $temp_dir, $pdo) {
    $files = [];
    
    try {
        // Generate the same API files as the static dashboard
        $api_files = generateStaticAPIFiles($event_id, $temp_dir, $pdo);
        $files = array_merge($files, $api_files);
        
        error_log("Generated " . count($files) . " JSON data files for event $event_id");
        
    } catch (Exception $e) {
        error_log("Error generating JSON data: " . $e->getMessage());
    }
    
    return $files;
}
