<?php
/**
 * Script: updateBonstMedia3.php Gemini
 * Version: 5.0 (Targeted Replacement Character Stripping)
 */

$start_time = microtime(true);
$run_date = date('Y-m-d H:i:s');

chdir(__DIR__);
require_once("LoggerLib.php"); //

// --- Log Rotation & Maintenance ---
$log_dir = '/home/bonstdata3/public_html/logs/'; //
$max_log_age = 30 * 86400; //
$max_log_size = 10 * 1024 * 1024; //

if (is_dir($log_dir)) {
    foreach (glob($log_dir . "*.log*") as $file) {
        if (filemtime($file) < (time() - $max_log_age)) {
            unlink($file); //
        }
    }
    $current_log = $log_dir . 'bonstmedia.log'; //
    if (file_exists($current_log) && filesize($current_log) > $max_log_size) {
        rename($current_log, $current_log . '.' . date('Y-m-d') . '.bak'); //
    }
}

// --- Configuration ---
$api_token = '3a55451872ef2a363ab5de3a5525b23466479eca'; //
$db_host   = 'localhost'; //
$db_name   = 'bonstdata3db'; //
$db_user   = 'bonstdata3user'; //
$db_pass   = 'Lb3@e9#23rhCVnYQ'; //

$download_limit = 5000; //
$ts_file        = './timestamps/updateBonstMedia3.ts'; //
$backup_log     = $log_dir . 'bonstmedia.log'; //
$log = new Logger("MLSMediaSync", $backup_log, "Replication"); //
$log->info("Starting Media Sync..."); //

try {
    $pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass); //
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //
} catch (Exception $e) {
    die("DB FAIL: " . $e->getMessage()); //
}

if (!file_exists($ts_file)) {
    file_put_contents($ts_file, date('Y-m-d\TH:i:s.v\Z', strtotime('-1 hour'))); //
}
$last_mts = trim(file_get_contents($ts_file)); //

$mapping = [
    'Order' => 'Order', 'MediaObjectID' => 'MediaObjectID', 'LongDescription' => 'LongDescription',
    'ImageHeight' => 'ImageHeight', 'ImageWidth' => 'ImageWidth', 'ImageSizeDescription' => 'ImageSizeDescription',
    'MediaURL' => 'MediaURL', 'MediaModificationTimestamp' => 'MediaModificationTimestamp',
    'ModificationTimestamp' => 'ModificationTimestamp', 'ResourceRecordKey' => 'ResourceRecordKey',
    'ResourceRecordID' => 'ResourceRecordID', 'ResourceName' => 'ResourceName',
    'OriginatingSystemName' => 'OriginatingSystemName', 'MlgCanView' => 'MlgCanView', 'MediaKey' => 'MediaKey'
]; //

$total_props_checked = 0; //
$total_media_processed = 0; //
$max_mts = $last_mts; //
$keep_running = true; //

while ($keep_running) { //
    if ($download_limit > 0 && $total_props_checked >= $download_limit) break; //

    $request_top = 1000; //
    if ($download_limit > 0) { //
        $remaining = $download_limit - $total_props_checked; //
        $request_top = ($remaining < 1000) ? $remaining : 1000; //
    }

    $url = 'https://api.mlsgrid.com/v2/Property?' . 
           rawurlencode('$filter') . "=OriginatingSystemName%20eq%20%27northstar%27%20and%20ModificationTimestamp%20gt%20$last_mts" . 
           "&" . rawurlencode('$expand') . "=Media&" . rawurlencode('$top') . "=$request_top"; //
           
    $ch = curl_init($url); //
    curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $api_token", "Accept-Encoding: gzip"]); //
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //
    $res = curl_exec($ch); //
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); //
    curl_close($ch); //

    if ($http_code === 429) { sleep(5); continue; } //
    if ($http_code !== 200) break; //
    if (substr($res, 0, 2) == "\x1f\x8b") { $res = gzdecode($res); } //
    $data = json_decode($res, true); //
    $batch_count = isset($data['value']) ? count($data['value']) : 0; //

    if ($batch_count > 0) { //
        $fields = array_keys($mapping); //
        $quoted_cols = "`" . implode("`,`", $fields) . "`, `Added_DateTime`, `Source`, `InsertedDate`, `UpdatedDate`"; //
        $placeholders = ":" . implode(",:", $fields) . ", NOW(), 'Northstar', NOW(), NOW()"; //
        $updates = []; //
        foreach ($fields as $field) { if ($field !== 'MediaKey') $updates[] = "`$field`=VALUES(`$field`)"; } //
        $updates[] = "`UpdatedDate`=NOW()"; //
        
        $stmt = $pdo->prepare("INSERT INTO bonstmedia3 ($quoted_cols) VALUES ($placeholders) ON DUPLICATE KEY UPDATE " . implode(",", $updates)); //

        foreach ($data['value'] as $row) { //
            $parent_ts = $row['ModificationTimestamp'] ?? null; //
            if (!empty($row['Media']) && is_array($row['Media'])) { //
                foreach ($row['Media'] as $mediaItem) { //
                    $params = []; //
                    foreach ($mapping as $db_col => $api_key) { //
                        $val = $mediaItem[$api_key] ?? null; //

                        // Ensure ResourceRecordID is populated from Parent if missing
                        if ($api_key === 'ResourceRecordID' && empty($val)) $val = $row['ListingId']; //
                        if ($api_key === 'ResourceRecordKey' && empty($val)) $val = $row['ListingKey']; //

                        // Inherit Parent TS if Child TS is missing
                        if (($api_key === 'ModificationTimestamp' || $api_key === 'MediaModificationTimestamp') && empty($val)) $val = $parent_ts; //

                        // Improved Boolean Check with explicit parent fallback inheritance
                        if ($api_key === 'MlgCanView') { //
                            if (is_null($val) && isset($row['MlgCanView'])) { //
                                $val = $row['MlgCanView']; //
                            }
                            if (is_null($val)) { //
                                $val = 'true'; //
                            } else {
                                $val = filter_var($val, FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false'; //
                            }
                        }

                        if (is_array($val)) $val = implode(", ", $val); //
                        $params[":$db_col"] = $val; //
                    }

                    // TARGETED FIX: Explicitly strip the hex representations of the invalid replacement characters
                    $clean_params = [];
                    foreach ($params as $key => $param_val) {
                        if (is_string($param_val)) {
                            // 1. Strip the literal multi-byte byte sequence \xEF\xBF\xBD
                            $cleaned = str_replace("\xEF\xBF\xBD", " ", $param_val);
                            // 2. Strip Unicode replacement variants if interpreted differently by PHP
                            $cleaned = preg_replace('/\x{FFFD}/u', ' ', $cleaned);
                            // 3. Fallback mb filter to clear remaining broken sequences
                            $cleaned = mb_convert_encoding($cleaned, 'UTF-8', 'UTF-8');
                            
                            $clean_params[$key] = trim($cleaned);
                        } else {
                            $clean_params[$key] = $param_val;
                        }
                    }

                    $stmt->execute($clean_params);
                    $total_media_processed++; //
                }
            }
            if ($row['ModificationTimestamp'] > $max_mts) $max_mts = $row['ModificationTimestamp']; //
        } 
        $total_props_checked += $batch_count; //
        $last_mts = $max_mts; //
        file_put_contents($ts_file, $max_mts); //
        if ($batch_count < $request_top) $keep_running = false; //
    } else {
        $keep_running = false; //
    }
    usleep(600000); //
}

if ($total_props_checked > 0) {
    $current_mts = trim(file_get_contents($ts_file)); //
    $buffer_ts = date('Y-m-d\TH:i:s.v\Z', strtotime($current_mts) - 9); //
    file_put_contents($ts_file, $buffer_ts); //
}

$elapsed = round(microtime(true) - $start_time, 4); //
$summary = "[$run_date] Media Sync: Props: $total_props_checked | Media: $total_media_processed | Time: {$elapsed}s"; //
$log->info($summary); //
echo $summary . "\n"; //
?>