<?php
declare(strict_types=1);

mb_internal_encoding('UTF-8');

const ZINRO_LOG_URL      = 'https://zinro.net/m/log.php?id=';
const ZINRO_ROOMLIST_URL = 'https://zinro.net/m/room_list.php?scene=%E7%B5%82%E4%BA%86';
const ZINRO_UA           = 'SaPuRi - Kaiseki-Senseki';

// 保存先ディレクトリ & 進捗ファイル
const ZHTML_DIR      = __DIR__ . '/zhtml';
const PROGRESS_FILE  = __DIR__ . '/zinro_log_progress.txt';
const DEFAULT_SLEEP_USEC = 2000000; // 2秒

// -------------------------------------------------
// CLIオプション
//   --from=123      : このIDから開始（省略可）
//   --sleep=2000000 : 1件ごとの sleep(マイクロ秒, 省略可)
// -------------------------------------------------
$options = getopt('', [
    'from::',
    'sleep::',
]);

$fromOpt   = isset($options['from'])  ? max(1, (int)$options['from'])  : null;
$sleepUsec = isset($options['sleep']) ? max(0, (int)$options['sleep']) : DEFAULT_SLEEP_USEC;

// -------------------------------------------------
// 開始ID決定（優先順位: --from > progressファイル > 1）
// -------------------------------------------------
if ($fromOpt !== null) {
    $fromId = $fromOpt;
} elseif (is_file(PROGRESS_FILE)) {
    $last = (int)trim((string)file_get_contents(PROGRESS_FILE));
    $fromId = max(1, $last + 1);
} else {
    $fromId = 1;
}

// -------------------------------------------------
// 終了ページから「今回試みる最大ID」を取得
// -------------------------------------------------
$toId = fetchLatestLogIdFromRoomList();
if ($toId === null || $toId < $fromId) {
    fwrite(STDERR, "終了ページから有効な最終IDが取得できません (from={$fromId}, to={$toId})" . PHP_EOL);
    exit(1);
}

echo "=== Zinro HTML Archiver (raw) ===" . PHP_EOL;
echo "範囲: {$fromId} ～ {$toId}" . PHP_EOL;
echo "Sleep per ID: {$sleepUsec} usec" . PHP_EOL;

// -------------------------------------------------
// 保存ディレクトリ作成
// -------------------------------------------------
if (!is_dir(ZHTML_DIR)) {
    if (!mkdir(ZHTML_DIR, 0775, true) && !is_dir(ZHTML_DIR)) {
        fwrite(STDERR, "ERROR: zhtmlディレクトリを作成できません: " . ZHTML_DIR . PHP_EOL);
        exit(1);
    }
}

// -------------------------------------------------
// メインループ
//   fromId ～ toId まで、ひたすらダウンロード
//   （cronで10分おき起動を想定）
// -------------------------------------------------
for ($id = $fromId; $id <= $toId; $id++) {

    $filePath = ZHTML_DIR . '/' . $id . '.html';

    // 既にファイルがあるならスキップ（再実行対策）
    if (is_file($filePath)) {
        echo "[SKIP] id={$id} (file exists)" . PHP_EOL;
        updateProgress($id);
        continue;
    }

    echo "[GET ] id={$id} ... ";

    $html = fetchLogHtml($id);
    if ($html === null) {
        echo "NG (fetch failed)" . PHP_EOL;
        // 取得失敗でも「このIDは終わった」とみなして次へ
        updateProgress($id);
        usleep($sleepUsec);
        continue;
    }

    // 生HTMLをそのまま保存（存在しないログのメッセージも含めて保存）
    $bytes = file_put_contents($filePath, $html);
    if ($bytes === false) {
        echo "NG (write failed)" . PHP_EOL;
    } else {
        echo "OK ({$bytes} bytes)" . PHP_EOL;
    }

    // 進捗更新
    updateProgress($id);

    // 負荷軽減
    usleep($sleepUsec);
}

// -------------------------------------------------
// 関数群
// -------------------------------------------------

/**
 * 進捗ファイルに「最後に処理したID」を書く
 */
function updateProgress(int $lastId): void
{
    file_put_contents(PROGRESS_FILE, (string)$lastId . PHP_EOL);
    @chmod(PROGRESS_FILE, 0644);
}

/**
 * 終了ページから最新ログID(shuryo) を取る。
 * 元バッチの実装をベースに、HTMLを1回だけ取りにいく。
 */
function fetchLatestLogIdFromRoomList(): ?int
{
    $header = [
        "User-Agent: " . ZINRO_UA,
        "Content-Type: application/x-www-form-urlencoded",
    ];

    $context = stream_context_create([
        'http' => [
            'method'  => 'GET',
            'header'  => implode("\r\n", $header),
            'timeout' => 10,
        ],
    ]);

    $contents = @file(ZINRO_ROOMLIST_URL, flags: 0, context: $context);

    if ($contents === false || !is_array($contents)) {
        fwrite(STDERR, "[ERR ] room_list.php 取得失敗" . PHP_EOL);
        return null;
    }

    $shuryo = null;
    foreach ($contents as $line) {
        if (strpos($line, 'confirmDialog') !== false && strpos($line, 'id=') !== false) {
            if (preg_match('/id=(\d+)/', $line, $m)) {
                $shuryo = (int)$m[1];
                break;
            }
        }
    }

    if (!$shuryo) {
        fwrite(STDERR, "[ERR ] confirmDialog 行から id= が見つからない" . PHP_EOL);
        return null;
    }

    echo "最新ログID(shuryo): {$shuryo}" . PHP_EOL;
    return $shuryo;
}

/**
 * ログHTML取得（curl）
 */
function fetchLogHtml(int $logId): ?string
{
    $url = ZINRO_LOG_URL . $logId;

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_TIMEOUT        => 15,
        CURLOPT_USERAGENT      => ZINRO_UA,
    ]);

    $body = curl_exec($ch);
    if ($body === false) {
        curl_close($ch);
        return null;
    }
    $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($code >= 400) {
        return null;
    }
    return $body;
}
