<?php
/**
 * تخصیص محله به بلوک‌ها — بهینه‌شده با bounding box + batch UPDATE
 * http://chat.local/wp-content/plugins/acc-engine/run-blocks-assign.php?key=run2024
 */
if (($_GET['key'] ?? '') !== 'run2024') die('کلید اشتباه');

require_once dirname(__FILE__) . '/../../../wp-load.php';
global $wpdb;
set_time_limit(600);
ini_set('memory_limit', '256M');

/* ── خروجی بلادرنگ ── */
if (ob_get_level()) ob_end_clean();
ob_implicit_flush(true);

echo '<!DOCTYPE html><html dir="rtl"><head><meta charset="utf-8">
<style>
  body{font-family:monospace;padding:20px;background:#0f172a;color:#e2e8f0;font-size:13px}
  h2{color:#7c3aed}
  .bar-wrap{background:#1e293b;border-radius:8px;height:22px;width:100%;margin:8px 0}
  .bar{background:linear-gradient(90deg,#7c3aed,#a78bfa);height:22px;border-radius:8px;transition:width .3s}
  .ok{color:#4ade80} .warn{color:#fb923c} .err{color:#f87171}
  table{border-collapse:collapse;width:100%} td,th{border:1px solid #334155;padding:5px 10px}
  th{background:#1e293b} tr:hover td{background:#1e293b}
  #counter{font-size:18px;font-weight:bold;color:#a78bfa}
</style></head><body>';
echo '<h2>🗺️ تخصیص محله به بلوک‌ها</h2>';
flush();

/* ════════════════════════════════════════
   توابع هندسی
════════════════════════════════════════ */
function pip(float $lat, float $lng, array $ring): bool {
    $n = count($ring); $j = $n - 1; $in = false;
    for ($i = 0; $i < $n; $i++) {
        $xi = (float)$ring[$i][0]; $yi = (float)$ring[$i][1];
        $xj = (float)$ring[$j][0]; $yj = (float)$ring[$j][1];
        if ((($yi > $lat) !== ($yj > $lat)) &&
            $lng < ($xj-$xi)*($lat-$yi)/($yj-$yi)+$xi) $in = !$in;
        $j = $i;
    }
    return $in;
}

function in_geojson(float $lat, float $lng, array $geo): bool {
    if ($geo['type'] === 'Polygon')
        return pip($lat, $lng, $geo['coordinates'][0]);
    if ($geo['type'] === 'MultiPolygon')
        foreach ($geo['coordinates'] as $p)
            if (pip($lat, $lng, $p[0])) return true;
    return false;
}

/* ════════════════════════════════════════
   مرحله ۱: ستون‌ها
════════════════════════════════════════ */
$cols = array_column($wpdb->get_results("SHOW COLUMNS FROM {$wpdb->prefix}gis_blocks"), 'Field');
if (!in_array('neighborhood_id', $cols)) {
    $wpdb->query("ALTER TABLE {$wpdb->prefix}gis_blocks
        ADD COLUMN neighborhood_id   int(11)      NULL,
        ADD COLUMN neighborhood_name varchar(200) NULL,
        ADD COLUMN district_id       int(11)      NULL,
        ADD INDEX  idx_neighborhood  (neighborhood_id)");
    echo '<p class="ok">✅ ستون‌ها اضافه شدند</p>';
} else {
    echo '<p>ℹ️ ستون‌ها موجود هستند</p>';
}
flush();

/* ════════════════════════════════════════
   مرحله ۲: بارگذاری محله‌ها + bounding box
════════════════════════════════════════ */
$raw_hoods = $wpdb->get_results(
    "SELECT id, name, district_id, geometry FROM {$wpdb->prefix}gis_neighborhoods WHERE geometry IS NOT NULL",
    ARRAY_A
);

$hoods = [];
foreach ($raw_hoods as $n) {
    $geo = json_decode($n['geometry'], true);
    if (!$geo) continue;

    /* bounding box از کل مختصات */
    $all_pts = [];
    if ($geo['type'] === 'Polygon') $all_pts = $geo['coordinates'][0];
    elseif ($geo['type'] === 'MultiPolygon')
        foreach ($geo['coordinates'] as $p) $all_pts = array_merge($all_pts, $p[0]);

    $lngs = array_column($all_pts, 0);
    $lats = array_column($all_pts, 1);

    $hoods[] = [
        'id'   => (int)$n['id'],
        'name' => $n['name'],
        'dist' => (int)$n['district_id'],
        'geo'  => $geo,
        'minlat' => min($lats), 'maxlat' => max($lats),
        'minlng' => min($lngs), 'maxlng' => max($lngs),
    ];
}
echo '<p class="ok">✅ محله‌ها بارگذاری شدند: <strong>' . count($hoods) . '</strong></p>';
flush();

/* ════════════════════════════════════════
   مرحله ۳: پردازش بلوک‌ها
════════════════════════════════════════ */
$total   = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}gis_blocks");
$matched = 0; $unmatched = 0; $processed = 0;
$batch_size  = 500;   // خواندن از DB
$update_size = 200;   // batch UPDATE
$offset      = 0;
$update_buf  = [];    // [block_code => {id,name,dist}]

$t_start = microtime(true);

echo "<p>📦 کل بلوک‌ها: <strong>{$total}</strong></p>";
echo '<div class="bar-wrap"><div class="bar" id="bar" style="width:0%"></div></div>';
echo '<p id="counter">۰ / ' . $total . '</p>';
echo '<p id="status">⏳ شروع پردازش...</p>';
flush();

function flush_updates(array &$buf): void {
    global $wpdb, $matched;
    if (empty($buf)) return;

    $codes   = array_keys($buf);
    $in      = implode(',', array_map(fn($c) => "'" . esc_sql($c) . "'", $codes));
    $case_id = $case_nm = $case_dt = '';

    foreach ($buf as $code => $v) {
        $c = esc_sql($code);
        $case_id .= " WHEN '{$c}' THEN {$v['id']}";
        $case_nm .= " WHEN '{$c}' THEN '" . esc_sql($v['name']) . "'";
        $case_dt .= " WHEN '{$c}' THEN {$v['dist']}";
    }

    $wpdb->query("UPDATE {$wpdb->prefix}gis_blocks
        SET neighborhood_id   = CASE block_code {$case_id} END,
            neighborhood_name = CASE block_code {$case_nm} END,
            district_id       = CASE block_code {$case_dt} END
        WHERE block_code IN ({$in})");

    $matched += count($buf);
    $buf = [];
}

while ($offset < $total) {
    $blocks = $wpdb->get_results($wpdb->prepare(
        "SELECT block_code, center_lat, center_lng FROM {$wpdb->prefix}gis_blocks LIMIT %d OFFSET %d",
        $batch_size, $offset
    ), ARRAY_A);
    if (empty($blocks)) break;

    foreach ($blocks as $b) {
        $lat = (float)$b['center_lat'];
        $lng = (float)$b['center_lng'];
        $found = null;

        /* فیلتر سریع با bounding box، سپس PIP دقیق */
        foreach ($hoods as $h) {
            if ($lat < $h['minlat'] || $lat > $h['maxlat'] ||
                $lng < $h['minlng'] || $lng > $h['maxlng']) continue;
            if (in_geojson($lat, $lng, $h['geo'])) { $found = $h; break; }
        }

        if ($found) {
            $update_buf[$b['block_code']] = ['id'=>$found['id'],'name'=>$found['name'],'dist'=>$found['dist']];
            if (count($update_buf) >= $update_size) flush_updates($update_buf);
        } else {
            $unmatched++;
        }
        $processed++;
    }

    /* flush باقیمانده */
    flush_updates($update_buf);

    $offset += $batch_size;
    $pct     = round($processed / $total * 100);
    $elapsed = round(microtime(true) - $t_start, 1);
    $rate    = $elapsed > 0 ? round($processed / $elapsed) : 0;
    $remain  = $rate > 0 ? round(($total - $processed) / $rate) : '?';

    echo "<script>
        document.getElementById('bar').style.width='{$pct}%';
        document.getElementById('counter').textContent='{$processed} / {$total} ({$pct}%)';
        document.getElementById('status').textContent='⚡ سرعت: {$rate} بلوک/ثانیه  |  ✅ تطبیق: {$matched}  |  ⏱ باقیمانده: ~{$remain} ثانیه';
    </script>";
    flush();
}

/* ════════════════════════════════════════
   گزارش نهایی
════════════════════════════════════════ */
$elapsed_total = round(microtime(true) - $t_start, 1);
echo "<script>
    document.getElementById('bar').style.width='100%';
    document.getElementById('status').textContent='✅ پردازش کامل شد';
</script>";

echo "<h3 class='ok'>✅ پردازش کامل — زمان: {$elapsed_total} ثانیه</h3>";
echo "<p class='ok'>🟢 تطبیق یافته: <strong>{$matched}</strong></p>";
echo "<p class='warn'>🟡 بدون محله: <strong>{$unmatched}</strong></p>";
flush();

$summary = $wpdb->get_results("
    SELECT district_id, neighborhood_name,
           COUNT(*) AS blocks, SUM(parcel_count) AS parcels
    FROM {$wpdb->prefix}gis_blocks
    WHERE neighborhood_id IS NOT NULL
    GROUP BY district_id, neighborhood_id, neighborhood_name
    ORDER BY district_id, blocks DESC");

echo '<h3>📊 توزیع بلوک‌ها در محله‌ها</h3>';
echo '<table><tr><th>ناحیه</th><th>محله</th><th>بلوک</th><th>پارسل</th></tr>';
foreach ($summary as $r)
    echo "<tr><td>{$r->district_id}</td><td>{$r->neighborhood_name}</td>
          <td>{$r->blocks}</td><td>" . number_format($r->parcels) . "</td></tr>";
echo '</table>';

echo '<p class="err" style="margin-top:30px">⚠️ این فایل را پاک کنید!</p>';
echo '</body></html>';
