<?php
// a3_lib.php - orijinal a3.php mantığı burada
declare(strict_types=1);

$items = [
  ["b"=>"Bakırköy","g"=>265],
  ["b"=>"Avcılar","g"=>192],
  ["b"=>"Arnavutköy","g"=>145],
  ["b"=>"Bahçelievler","g"=>193],
  ["b"=>"Ataşehir","g"=>190],
  ["b"=>"Bağcılar","g"=>189],
  ["b"=>"Başakşehir","g"=>150],
  ["b"=>"Bayrampaşa","g"=>146],
  ["b"=>"Beykoz","g"=>186],
  ["b"=>"Beşiktaş","g"=>294],
  ["b"=>"Beylikdüzü","g"=>152],
  ["b"=>"Beyoğlu","g"=>463],
  ["b"=>"Büyükçekmece","g"=>291],
  ["b"=>"Çekmeköy","g"=>250],
  ["b"=>"Esenler","g"=>242],
  ["b"=>"Eminönü","g"=>298],
  ["b"=>"Esenyurt","g"=>243],
  ["b"=>"Fatih","g"=>304],
  ["b"=>"Eyüpsultan","g"=>144],
  ["b"=>"Gaziosmanpaşa","g"=>236],
  ["b"=>"Güngören","g"=>147],
  ["b"=>"İmes","g"=>289],
  ["b"=>"İstoç","g"=>238],
  ["b"=>"Kadıköy","g"=>309],
  ["b"=>"Kağıthane","g"=>191],
  ["b"=>"Kartal","g"=>198],
  ["b"=>"Pendik","g"=>151],
  ["b"=>"Küçükçekmece","g"=>178],
  ["b"=>"Maltepe","g"=>244],
  ["b"=>"Merter","g"=>305],
  ["b"=>"Sarıyer","g"=>185],
  ["b"=>"Sancaktepe","g"=>187],
  ["b"=>"Silivri","g"=>233],
  ["b"=>"Sultanbeyli","g"=>148],
  ["b"=>"Sultangazi","g"=>188],
  ["b"=>"Şişli","g"=>288],
  ["b"=>"Şile","g"=>251],
  ["b"=>"Tuzla","g"=>237],
  ["b"=>"Üsküdar","g"=>143],
  ["b"=>"Ümraniye","g"=>153],
  ["b"=>"Zeytinburnu","g"=>194],
];


function a3_items_hash(): string {
  global $items;
  return md5(json_encode($items, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES));
}






function ihh_base_url(): string {
  return "https://portal.ihh.org.tr/crea/donate/donate";
}

/**
 * $q: portal query parametleri (department HARİÇ). Örn:
 *  ["date_a"=>"01.01.2025","date"=>"between","date_b"=>"30.12.2025","type"=>"1","found"=>"10", ...]
 * $department: items listesindeki departman id
 */
function ihh_url_from_q(array $q, $department): string {
  $q2 = $q;
  $q2["department"] = (string)$department;

  // RFC3986 encode: rawurlencode benzeri
  $qs = http_build_query($q2, '', '&', PHP_QUERY_RFC3986);
  return ihh_base_url() . "?" . $qs;
}

// Geriye dönük uyum: eski imza
function ihh_url($e, $f, $g) {
  $q = [
    "date_a" => (string)$e,
    "date"   => "between",
    "date_b" => (string)$f,
  ];
  return ihh_url_from_q($q, $g);
}


function parse_ihh_verisi($html) {
    // 1) Span içindeki format
    if (preg_match('/<span>\s*([\d.]+)\s*Kayıt\s*\|\s*([\d.,]+)\s*TRY\s*<\/span>/iu', $html, $m)) {
        return trim($m[1])." Kayıt | ".trim($m[2])." TRY";
    }
    // 2) Genel format
    if (preg_match('/[\d.]+\s*Kayıt\s*\|\s*[\d.,]+\s*TRY/iu', $html, $m)) {
        return trim($m[0]);
    }
    return null; // bulunamadi/parse edilemedi
}

function make_ch(string $url, string $cookie, int $connectTimeout, int $totalTimeout) {
  $ch = curl_init();
  curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_CONNECTTIMEOUT => $connectTimeout,
    CURLOPT_TIMEOUT => $totalTimeout,
    CURLOPT_HTTPHEADER => [
      "Cookie: {$cookie}",
      "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    ],
  ]);
  return $ch;
}

function a3_calc_wait_seconds(int $attempt, array $cfg): int {
  // attempt: 1,2,3...
  $maxWait = (int)($cfg['max_wait'] ?? 300);

  // Eğer config'te açıkça wait dizisi verildiyse onu kullan (1-based)
  $waits = $cfg['retry_waits'] ?? null;
  if (is_array($waits) && count($waits) > 0) {
    $idx = $attempt - 1;
    if ($idx < 0) $idx = 0;
    if ($idx >= count($waits)) $idx = count($waits) - 1;
    $w = (int)$waits[$idx];
    if ($w < 0) $w = 0;
    $w = min($w, $maxWait);
  } else {
    // Exponential backoff: 1->0, 2->1, 3->2, 4->4, 5->8, ...
    if ($attempt <= 1) $w = 0;
    else {
      $exp = $attempt - 2;
      if ($exp > 10) $exp = 10;
      $w = (1 << $exp);
      $w = min($w, $maxWait);
    }
  }

  // Jitter (%0-20) - aynı anda patlamayı azaltır
  if ($w > 0) {
    $jitter = (int)floor($w * (mt_rand(0, 20) / 100));
    $w += $jitter;
  }
  return $w;
}

function run_a3_job(array $params, array $cfg, callable $progress_cb = null): array {
  $e = (string)($params['e'] ?? '');
  $f = (string)($params['f'] ?? '');
  $c = (string)($params['c'] ?? 'H6');

  // Yeni sistem: params['q'] altında portal query parametleri gelebilir (department HARİÇ)
  $q = $params['q'] ?? null;
  if (is_array($q) && count($q) > 0) {
    // e/f ayrıca verilmişse ve q içinde yoksa tamamla (opsiyonel)
    if ($e !== '' && !isset($q['date_a'])) $q['date_a'] = $e;
    if ($f !== '' && !isset($q['date_b'])) $q['date_b'] = $f;
    if (!isset($q['date'])) $q['date'] = 'between';
  } else {
    // Geriye dönük: e/f ile eski url
    if ($e === '' || $f === '') {
      throw new RuntimeException("Parametre hatası: (q) veya (e,f) zorunlu");
    }
    $q = [
      'date_a' => $e,
      'date'   => 'between',
      'date_b' => $f,
    ];
  }

  // Minimum kontrol: date_a/date_b olsun (portal için kritik)
  if (!isset($q['date_a']) || !isset($q['date_b'])) {
    throw new RuntimeException("Parametre hatası: date_a ve date_b zorunlu (q içinde)");
  }

  $cookie_file = (string)$cfg['cookie_file'];
  if (!file_exists($cookie_file)) {
    throw new RuntimeException("cookie dosyası bulunamadı: {$cookie_file}");
  }
  $my_cookie = trim((string)file_get_contents($cookie_file));

  // Orijinal items listesi
  global $items;
  if (!is_array($items) || count($items) === 0) {
    throw new RuntimeException("items listesi boş");
  }

  $concurrency    = (int)$cfg['concurrency'];
  $connectTimeout = (int)$cfg['connect_timeout'];
  $totalTimeout   = (int)$cfg['total_timeout'];

  // 0 = sonsuz dene (bulana kadar)
  $maxRetries = (int)($cfg['max_retries'] ?? 0);

  $total = count($items);
  $done  = 0;

  // İş listesi
  $jobs = [];
  foreach ($items as $idx => $it) {
    $b = (string)$it['b'];
    $g = (string)$it['g'];
    $url = ihh_url_from_q($q, $g);
    $jobs[$idx] = [
      'b'=>$b,'g'=>$g,'url'=>$url,
      'attempt'=>0,
      'next_time'=>0.0,
      'done'=>false,
      'running'=>false,
      'handle'=>null,
    ];
  }

  $results = [];

  $mh = curl_multi_init();
  $handles_meta = []; // handleId => idx

  $last_progress = -1;

  while ($done < $total) {
    $now = microtime(true);

    // Boş slotları doldur (concurrency kadar)
    $runningCount = 0;
    foreach ($jobs as $j) if ($j['running']) $runningCount++;

    if ($runningCount < $concurrency) {
      foreach ($jobs as $idx => $j) {
        if ($runningCount >= $concurrency) break;
        if ($j['done'] || $j['running']) continue;
        if ($j['next_time'] > $now) continue;

        $ch = make_ch($j['url'], $my_cookie, $connectTimeout, $totalTimeout);
        curl_multi_add_handle($mh, $ch);

        $jobs[$idx]['running'] = true;
        $jobs[$idx]['handle'] = $ch;

        $hid = (int)$ch;
        $handles_meta[$hid] = $idx;

        $runningCount++;
      }
    }

    // Eğer hiç running yoksa ama herkes bekliyorsa, en yakın next_time'a kadar uyu
    $runningCount = 0;
    $minNext = null;
    foreach ($jobs as $j) {
      if ($j['running']) $runningCount++;
      if (!$j['done'] && !$j['running']) {
        if ($minNext === null || $j['next_time'] < $minNext) $minNext = $j['next_time'];
      }
    }
    if ($runningCount === 0 && $minNext !== null) {
      $sleep = (int)max(0, ceil($minNext - microtime(true)));
      if ($sleep > 0) sleep($sleep);
      continue;
    }

    // Multi çalıştır
    do {
      $status = curl_multi_exec($mh, $active);
    } while ($status === CURLM_CALL_MULTI_PERFORM);

    // Bekle
    if ($active) {
      curl_multi_select($mh, 1.0);
    }

    // Tamamlananları topla
    while ($info = curl_multi_info_read($mh)) {
      $ch = $info['handle'];
      $hid = (int)$ch;
      $idx = $handles_meta[$hid] ?? null;

      $html = curl_multi_getcontent($ch);
      $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
      $errno = (int)curl_errno($ch);
      $err  = $errno ? curl_error($ch) : null;

      // cleanup
      curl_multi_remove_handle($mh, $ch);
      curl_close($ch);
      unset($handles_meta[$hid]);

      if ($idx === null) continue;

      $jobs[$idx]['running'] = false;
      $jobs[$idx]['handle'] = null;

      // Sonuç parse
      $val = null;
      if ($errno === 0 && $http > 0 && $http < 400) {
        $val = parse_ihh_verisi($html); // null dönebilir
      }

      $isZeroOk = ($val !== null && preg_match('/^0\s*Kayıt\s*\|\s*0[,\.]00\s*TRY$/iu', $val));
      $isOk = ($val !== null); // parse bulunduysa OK (0 kayıt dahil)

      if ($isOk) {
        // Başarı: yazılacak değer var
        $results[] = [
          "b" => $jobs[$idx]["b"],
          "g" => $jobs[$idx]["g"],
          "url" => $jobs[$idx]["url"],
          "ihh_verisi" => $val,
          "http" => $http,
          "curl_errno" => $errno,
          "curl_error" => $err,
          "attempt" => $jobs[$idx]["attempt"] + 1,
        ];
        $jobs[$idx]['done'] = true;
        $done++;

      } else {
        // Başarısız -> Bulunamadı yazma YOK. Aynı URL tekrar denenecek.
        $jobs[$idx]['attempt']++;

        // Eğer maxRetries > 0 ise ve aşıldıysa yine de bırakma istersen burada davranışı değiştirebilirsin.
        // Şu an default: maxRetries=0 (sonsuz) önerilir.
        if ($maxRetries > 0 && $jobs[$idx]['attempt'] >= $maxRetries) {
          // "Bulsun bulana kadar" isteğine sadık kalmak için burada da retry yerine "uzun bekle"ye düşürüyoruz.
          $jobs[$idx]['attempt'] = $maxRetries; // sabitle
        }

        $wait = a3_calc_wait_seconds($jobs[$idx]['attempt'], $cfg);
        $jobs[$idx]['next_time'] = microtime(true) + $wait;
      }

      // Progress callback
      if ($progress_cb) {
        $pct = (int)floor(($done / max(1, $total)) * 100);
        $meta = [
          "b" => $jobs[$idx]["b"],
          "g" => $jobs[$idx]["g"],
          "url" => $jobs[$idx]["url"],
          "attempt" => $jobs[$idx]["attempt"] + 1,
          "pct" => $pct,
          "http" => $http,
          "curl_errno" => $errno,
          "ok" => $isOk,
          "ihh_verisi" => $isOk ? $val : null
        ];
        $progress_cb($done, $total, $meta);
     }
    }
  }

  curl_multi_close($mh);

  return $results;
}


function a3_write_results_to_sheet(array $results, string $cell, string $spreadsheetId, string $googleAuthJson): array {
  require_once __DIR__ . '/vendor/autoload.php';

  $client = new \Google_Client();
  $client->setAuthConfig($googleAuthJson);
  $client->addScope(\Google_Service_Sheets::SPREADSHEETS);
  $service = new \Google_Service_Sheets($client);

  $data = [];
  foreach ($results as $r) {
    // results elemanlarında b ve ihh_verisi olmalı (senin mevcut yapın böyle)
    $range = $r["b"] . "!" . $cell;
    $data[] = new \Google_Service_Sheets_ValueRange([
      'range' => $range,
      'values' => [[ $r["ihh_verisi"] ]]
    ]);
  }

  $batchBody = new \Google_Service_Sheets_BatchUpdateValuesRequest([
    'valueInputOption' => 'RAW',
    'data' => $data
  ]);

  try {
    $service->spreadsheets_values->batchUpdate($spreadsheetId, $batchBody);
    return ["sheet_ok"=>true, "sheet_error"=>null];
  } catch (Throwable $ex) {
    return ["sheet_ok"=>false, "sheet_error"=>$ex->getMessage()];
  }
}
